Description
Executing SQL constructed from user input can let an attacker change the query structure, bypass authentication, or read, modify, and delete data.
Potential impact
- Authentication or authorization bypass
- Sensitive data access and modification
- Data deletion or service disruption
Remediation
- Do not concatenate or interpolate user input into SQL strings.
- Use APIs that separate query structure from values, such as SQLite bindings, prepared statements, or ORM query builders.
- Select identifiers that cannot be bound, such as table and column names, only from an allow-list.
Examples
Before
swift
let query = "SELECT * FROM users WHERE name = '\(username)'"
try db.execute(query)
After
swift
import GRDB
func findUser(db: Database, username: String) throws -> Row? {
return try Row.fetchOne(
db,
sql: "SELECT * FROM users WHERE name = ?",
arguments: [username])
}
Explanation:
- Before: User input becomes part of the SQL structure.
- After: GRDB's
Row.fetchOnereads the result using bound arguments. The query structure and values remain separate, so input is treated as data.