---
title: Tránh SQL Injection khi dùng SQLite, FMDB hoặc CoreData NSPredicate
impact: CRITICAL
impactDescription: Ghép chuỗi trực tiếp vào câu lệnh SQL hoặc NSPredicate format string cho phép attacker đọc, xóa hoặc sửa toàn bộ database của app.
tags: swift, ios, sql-injection, sqlite, fmdb, coredata, nspredicate, security
---

## Tránh SQL Injection khi dùng SQLite, FMDB hoặc CoreData NSPredicate

Trong iOS, SQL injection xảy ra khi ghép chuỗi trực tiếp vào câu lệnh SQLite/FMDB, hoặc dùng `NSPredicate(format:)` với giá trị người dùng chưa được escape. Phải dùng parameterized query hoặc `NSPredicate(format:argumentArray:)`.

**Incorrect (ghép chuỗi trực tiếp):**

```swift
import FMDB

// !! Ghép chuỗi tên đăng nhập trực tiếp vào SQL
func findUser(username: String, db: FMDatabase) -> [String: Any]? {
    let query = "SELECT * FROM users WHERE username = '\(username)'"  // SQL Injection!
    let result = db.executeQuery(query, withArgumentsIn: [])
    return nil
}

// !! NSPredicate với format string không an toàn
func fetchMessages(senderName: String, context: NSManagedObjectContext) -> [Message] {
    let predicate = NSPredicate(format: "sender == '\(senderName)'")  // Injection!
    let request = Message.fetchRequest()
    request.predicate = predicate
    return (try? context.fetch(request)) ?? []
}
```

**Correct (parameterized query):**

```swift
import FMDB

// SAFE: Dùng parameterized query với ?
func findUser(username: String, db: FMDatabase) -> FMResultSet? {
    let query = "SELECT * FROM users WHERE username = ?"
    return db.executeQuery(query, withArgumentsIn: [username])
}

// SAFE: NSPredicate với argumentArray
func fetchMessages(senderName: String, context: NSManagedObjectContext) -> [Message] {
    let predicate = NSPredicate(format: "sender == %@", argumentArray: [senderName])
    let request = Message.fetchRequest()
    request.predicate = predicate
    return (try? context.fetch(request)) ?? []
}

// SAFE: SQLite3 với bound parameters
func insertNote(title: String, body: String, db: OpaquePointer) {
    var stmt: OpaquePointer?
    let sql = "INSERT INTO notes (title, body) VALUES (?, ?)"
    if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK {
        sqlite3_bind_text(stmt, 1, (title as NSString).utf8String, -1, nil)
        sqlite3_bind_text(stmt, 2, (body as NSString).utf8String, -1, nil)
        sqlite3_step(stmt)
    }
    sqlite3_finalize(stmt)
}
```

**Tools:** SwiftLint custom rule, OWASP MASVS-CODE-4, Instruments (SQLite profiler)
