ORM mặc định dùng parameterized query — giá trị truyền vào không bao giờ string-format vào SQL, mà gửi riêng qua driver:
python
# An toàn — Django sinh: WHERE email = %s và pass email làm param
User.objects.filter(email=user_input)Lỗ hổng SQL injection vẫn lọt qua 3 đường:
python
# 1. .extra() với raw SQL string-format — KHÔNG dùng
User.objects.extra(where=[f"email = '{user_input}'"]) # SQL injection!
# 2. .raw() / cursor.execute string-format
User.objects.raw(f"SELECT * FROM users WHERE email = '{user_input}'") # SQL injection!
# 3. Truyền cột/bảng động qua format (params chỉ bind giá trị, không bind identifier)
MyModel.objects.order_by(user_input) # nguy hiểm nếu user_input không whitelistCách đúng:
python
# raw an toàn nếu dùng params
User.objects.raw('SELECT * FROM users WHERE email = %s', [user_input])
# Cột động: whitelist trước khi dùng
ALLOWED_SORT = {'created_at', 'title', 'views'}
sort = user_input if user_input in ALLOWED_SORT else 'created_at'
Post.objects.order_by(sort)extra() đã deprecated, tránh hoàn toàn. Func(), Expression, Subquery mới là cách Django chuẩn để build dynamic SQL an toàn.
Quy ước review code nên cấm hẳn pattern f"... {var} ..." xuất hiện trong context SQL.