Bỏ qua menu, vào nội dung chính

SQL injection: cách phòng tránh thật sự

SQL injection cách tránh thật sự: parameterized query, ORM, escape không đủ, second-order injection. Code Node.js + Postgres minh hoạ rõ ràng.

3 phút đọc

SQL injection là vulnerability cổ điển nhưng vẫn xuất hiện trong audit production. Bài này code Node.js + PostgreSQL, đi qua các pattern attack thực tế và defense duy nhất đáng tin.

OWASP SQL Injection Prevention Cheat Sheet
OWASP SQL Injection Prevention Cheat Sheet (cheatsheetseries.owasp.org), ảnh chụp ngày 27/09/2026.

Vấn đề kinh điển

javascript// ❌ String concat - SQL injection cổ điển
const email = req.body.email
db.query(`SELECT * FROM users WHERE email = '${email}'`)

// User nhập: ' OR '1'='1
// Query thực thi: SELECT * FROM users WHERE email = '' OR '1'='1' - trả mọi user

// User nhập: '; DROP TABLE users; --
// Query: SELECT * FROM users WHERE email = ''; DROP TABLE users; --'

Parameterized query - defense duy nhất đáng tin

javascript// ✓ pg lib - $1 placeholder
const { rows } = await pool.query(
  'SELECT * FROM users WHERE email = $1',
  [email]
)

// ✓ MySQL - ? placeholder
const [rows] = await mysql.query(
  'SELECT * FROM users WHERE email = ?',
  [email]
)

// ✓ Prisma - ORM
const user = await prisma.user.findFirst({ where: { email } })

// ✓ Drizzle
const user = await db.select().from(users).where(eq(users.email, email))

Database engine biết phần nào là query, phần nào là data - không bao giờ execute data. Escape charset không thể vượt qua được.

Raw query trong ORM vẫn nguy hiểm

javascript// ❌ Sequelize raw - SQL injection
sequelize.query(`SELECT * FROM users WHERE name = '${name}'`)

// ✓ Sequelize raw có replacements
sequelize.query('SELECT * FROM users WHERE name = :name',
  { replacements: { name }, type: QueryTypes.SELECT })

// ❌ Knex.raw concat
knex.raw(`UPDATE users SET name = '${name}' WHERE id = ${id}`)

// ✓ Knex.raw bind
knex.raw('UPDATE users SET name = ? WHERE id = ?', [name, id])

Dynamic WHERE clause

Khi cần build dynamic filter, đừng concat. Dùng query builder:

javascript// ❌ Sai
let where = '1=1'
if (req.query.status) where += ` AND status = '${req.query.status}'`
db.query(`SELECT * FROM orders WHERE ${where}`)

// ✓ Đúng - array params
const conds = []
const params = []
if (req.query.status) {
  params.push(req.query.status)
  conds.push(`status = $${params.length}`)
}
const where = conds.length ? 'WHERE ' + conds.join(' AND ') : ''
db.query(`SELECT * FROM orders ${where}`, params)

Second-order injection

Inject payload lưu DB -> sau đó dùng dynamic SQL đọc ra:

javascript// User đăng ký với name = "admin' --"
await db.query('INSERT INTO users (name) VALUES ($1)', [name])  // OK, parameterized

// Sau đó admin generate report:
const userName = (await db.query('SELECT name FROM users WHERE id = $1', [id])).rows[0].name
// userName = "admin' --"
await db.query(`SELECT * FROM logs WHERE user = '${userName}'`)
// → SQL injection ở đây!

Quy tắc: parameterize ở MỌI query, kể cả data đến từ DB.

Least privilege DB user

sql-- Tạo user app với quyền minimum
CREATE USER alodev_app WITH PASSWORD '...';
GRANT SELECT, INSERT, UPDATE, DELETE ON users, orders, products TO alodev_app;
-- KHÔNG GRANT DROP, ALTER, CREATE
-- KHÔNG GRANT trên bảng admin/audit nếu app không cần

Dù SQL injection xảy ra, attacker không drop table được.

Test bằng sqlmap

bashsqlmap -u "https://alodev.vn/api/users?id=1" \
  --headers="Authorization: Bearer $TOKEN" \
  --batch --level=3 --risk=2

# Test POST
sqlmap -u "https://alodev.vn/api/login" \
  --data='{"email":"a@b.c","password":"x"}' \
  --header='Content-Type: application/json'

Kết luận

SQL injection phòng được hoàn toàn nếu giữ kỷ luật: tham số hoá MỌI query. Escape không đủ. ORM cơ bản an toàn nhưng raw query phải cẩn thận. Combine với least privilege DB user + sqlmap test trong CI. Đọc OWASP Top 10 để cover các vulnerability khác.

Nhận bài mới

Để lại email để nhận bài mới của series Kiến thức lập trình, hoặc theo dõi bằng RSS.

RSS Kiến thức lập trình

Email chỉ dùng để gửi bài mới, xem Chính sách bảo mật.

Trao đổi về dự án

Cần tư vấn?