flawopen.com/SQL Injection di Python/Django ORM
Apakah Django ORM aman dari SQL Injection secara default?
API QuerySet Django memisahkan SQL dari nilai dan mengirim nilai sebagai parameter melalui driver database untuk kueri umum seperti .filter(), .get(), dan .exclude(). Perlindungan ini tidak mencakup semua cara menyusun kueri: raw SQL, ekspresi khusus, serta struktur kueri pilihan pengguna memerlukan pemeriksaan tersendiri.
Menggunakan nilai dengan aman di Django QuerySet
username = request.GET.get("u", "")
User.objects.raw(
f"SELECT * FROM myapp_user WHERE username = '{username}'"
)
username = request.GET.get("u", "")
User.objects.raw(
"SELECT * FROM myapp_user WHERE username = %s",
[username],
)
Batas yang perlu diperhatikan
Manager.raw() menjalankan SQL yang diberikan. Kirim nilai yang dikendalikan pengguna melalui params dan biarkan placeholder tanpa tanda kutip, misalnya User.objects.raw("SELECT * FROM users WHERE username = %s", [username]). Jangan memasukkan nilai ke SQL dengan f-string, format %, atau konkatenasi. Parameter mewakili nilai, bukan identifier SQL seperti nama tabel atau kolom.
Untuk akses database langsung, gunakan cursor.execute(sql, params) dan kirim nilai secara terpisah sesuai placeholder Django. Jangan menginterpolasi nilai ke sql. Tinjau juga ekspresi RawSQL dan Func: konteks template dan argumen bernama dapat diinterpolasi ke SQL, bukan diikat sebagai parameter.
Jalur field dari pengguna pada .filter(**{field: value}) tidak otomatis menjadi SQL Injection: Django memproses nama lookup sebagai struktur kueri. Namun, hal ini dapat membuka field, relasi, atau lookup yang seharusnya tidak tersedia di endpoint, serta menimbulkan error. Petakan opsi urutan dan filter eksternal ke allow-list kecil berisi field model dan lookup yang diizinkan.
Cara mengaudit kueri Django
rg -n '(\.raw\(|RawSQL\(|\.extra\(|connection\.cursor\()' --glob '*.py' ..raw(), RawSQL, .extra(), dan connection.cursor(). Tinjau tiap temuan dan telusuri aliran nilainya: parameter terikat, fragmen SQL tak tepercaya, identifier atau lookup dinamis, serta template Func khusus.Pertanyaan umum
QuerySet.extra() adalah API lama untuk menambahkan fragmen SQL. Hindari di kode baru dan tinjau argumen where serta select yang sudah ada. Utamakan ekspresi QuerySet; jika raw SQL diperlukan, ikat nilai melalui argumen params yang terdokumentasi.
Django REST Framework tidak mengubah parameterisasi SQL ORM. Aturan yang sama berlaku untuk kueri dari view, serializer, dan viewset; batasi juga field pengurutan atau filter yang dikendalikan klien dengan allow-list.
Referensi
- Django docs — SQL injection protection
- flawopen.com — SQL Injection in Python
- Django docs — Performing raw SQL queries
- Django docs — Query expressions and RawSQL