flawopen.com/PythonのSQL Injection/Django ORM
Django ORMはデフォルトでSQL Injectionを防げる?
.filter()、.get()、.exclude()など一般的なQuerySet APIは、SQLと値を分離し、値をデータベースドライバーへパラメーターとして渡します。ただし、あらゆるクエリー構築方法が保護されるわけではありません。raw SQL、カスタム式、ユーザーが選ぶクエリー構造は個別に確認します。
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],
)
保護の境界を確認する
Manager.raw()は指定されたSQLを実行します。ユーザー制御値はparamsで渡し、プレースホルダーを引用符で囲まないでください。例:User.objects.raw("SELECT * FROM users WHERE username = %s", [username])。f-string、%書式、文字列連結で値をSQLに埋め込まないでください。パラメーターは値用であり、テーブル名や列名などSQL識別子には使えません。
DBへ直接アクセスする場合はcursor.execute(sql, params)を使い、Djangoの仕様に沿って値を分けて渡します。sqlに値を補間してはいけません。RawSQLやFunc式も確認してください。テンプレートコンテキストやキーワード引数はパラメーターとして束縛されず、SQLに補間されることがあります。
.filter(**{field: value})のフィールドパスがユーザー制御でも、自動的にSQL Injectionになるわけではありません。Djangoはlookup名をクエリー構造として解決します。ただし、endpointが公開すべきでないフィールド、関連、lookupを露出したり、エラーを起こしたりする可能性があります。外部の並べ替え・絞り込み項目を許可リストのフィールドとlookupに変換してください。
Djangoのクエリーを監査する
rg -n '(\.raw\(|RawSQL\(|\.extra\(|connection\.cursor\()' --glob '*.py' ..raw()、RawSQL、.extra()、connection.cursor()の使用箇所を検索します。各箇所で値の流れを確認し、パラメーター束縛、未信頼SQL断片、動的識別子やlookup、カスタムFuncテンプレートを調べます。よくある質問
QuerySet.extra()はraw SQL断片を追加する旧APIです。新規コードでは避け、既存のwhereとselect引数を慎重に確認してください。QuerySet式を優先し、raw SQLが必要な場合は文書化されたparams引数で値を束縛します。
Django REST FrameworkもORMのSQLパラメーター化を変えません。view、serializer、viewsetからのクエリーにも同じルールが適用されます。クライアントが指定する並べ替えやフィルター項目も許可リストで制限してください。