SQL Injection là attack inject SQL code qua user input để manipulate queries, bypass authentication, extract/delete data, hoặc execute OS commands (xp_cmdshell trong MSSQL).
- Classic example: username input là ' OR '1'='1' -- → query trở thành SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '...' — luôn true, bypass login.
- Second-order injection: input được store vào DB, sau đó dùng trong query khác mà không sanitize — khó phát hiện.
- Phòng chống chính: parameterized queries/prepared statements — placeholder thay vì string concatenation: db.query('SELECT * FROM users WHERE id = $1', [userId]) — user input không bao giờ được interpret là SQL code.
- ORM như Prisma, Sequelize, TypeORM tự động parameterize — nhưng raw queries vẫn nguy hiểm nếu dùng template literals: prisma.$queryRaw
SELECT FROM users WHERE id = ${userId}(safe với tagged template) vs prisma.$queryRawUnsafe('SELECT FROM users WHERE id = ' + userId) (unsafe). - Stored procedures có thể vẫn bị nếu dùng dynamic SQL bên trong.
- Input validation là defense in depth nhưng không thể thay thế parameterized queries — validation có thể bị bypass.
- Principle of least privilege: DB user chỉ có quyền cần thiết — SELECT/INSERT/UPDATE nhưng không DROP TABLE, không xp_cmdshell.
- WAF (Web Application Firewall) thêm layer bảo vệ nhưng không đủ một mình.
SQL injection is an attack that injects SQL code via user input to manipulate queries, bypass authentication, extract or delete data, or even execute OS commands (xp_cmdshell in MSSQL).
- Classic example: a username input of ' OR '1'='1' -- turns the query into SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = '...' — always true, bypassing login.
- Second-order injection: input is stored in the DB and later used in another query without sanitization — harder to detect.
- Primary prevention: parameterized queries / prepared statements — use placeholders instead of string concatenation: db.query('SELECT * FROM users WHERE id = $1', [userId]) — user input is never interpreted as SQL code.
- ORMs such as Prisma, Sequelize, and TypeORM parameterize automatically — but raw queries are still dangerous if built with template literals: prisma.$queryRaw
SELECT FROM users WHERE id = ${userId}(safe, tagged template) vs prisma.$queryRawUnsafe('SELECT FROM users WHERE id = ' + userId) (unsafe). - Stored procedures can still be vulnerable if they use dynamic SQL internally.
- Input validation is defense in depth but cannot replace parameterized queries — validation can be bypassed.
- Principle of least privilege: the DB user should have only the permissions it needs — SELECT/INSERT/UPDATE but not DROP TABLE or xp_cmdshell.
- A WAF (Web Application Firewall) adds an extra layer of protection but is not sufficient on its own.