flawopen.com/GoのSQLインジェクション/GORM Raw SQLとSQLインジェクション
GORM Raw()はSQLインジェクションを防げますか?Goのバインド引数
CWE-89: SQL Injectionリファレンスページ
要点
placeholderは値を入れる封印済みの封筒だと考えてください。データベースドライバーはSQL命令と値を別々に送信します。クエリ内にplaceholderを残し、値を引数として渡せば、GORM Raw()は安全にバインドできます。ユーザー入力をSQL文字列にformatすると、封筒と命令が混ざり、クエリが変化する可能性があります。
危険な例
// ユーザー制御の文字列がSQL構文になる db.Raw(fmt.Sprintf( "SELECT * FROM users WHERE id = %s", userID, )).Scan(&user)
安全な例
// ドライバーがuserIDを値としてバインド db.Raw( "SELECT * FROM users WHERE id = ?", userID, ).Scan(&user)
安全なGORM Raw SQLとパラメーターのバインド
placeholderをSQLに残し、引数を別に渡すと、GORMとGoのdatabase/sqlは値をバインドします。例:db.Where("name = ?", userInput)、db.Raw("SELECT ... WHERE id = ?", userID)。placeholderは値を表し、SQL構文ではありません。テーブル名、列名、並び順、演算子は値のバインドでは保護できません。動的識別子は固定の許可リストから選びます。Order、Select、Table、Rawなどはユーザー入力をSQL断片に連結すると注入される可能性があります。
- ✓SQL構造を固定し、ユーザー制御の値は個別のplaceholder引数として渡します。
- ✓動的なテーブル名、列名、並び順はアプリが定義した固定許可リストから選びます。
- ✓
Raw、Exec、Order、Select、TableなどのSQL断片メソッドで連結やformatを確認します。 - ✓数値IDは期待するGo型に変換し、クエリ前に変換エラーを処理します。
コードベースの確認方法
rg -n --glob '*.go' '\.(Raw|Exec|Order|Select|Table)\s*\(' .
rg -n --glob '*.go' 'fmt\.(Sprintf|Sprint)' .GORM Raw SQLとバインド引数のFAQ
GORMのチェーンメソッドは常に安全ですか?
いいえ。Where("name = ?", input)のように値を別引数で渡す条件はバインドを使います。入力を条件文字列に連結したり、生のSQL構文として渡したりすると注入される可能性があります。一部のメソッドはエスケープされない識別子やSQL断片を受け取るため、引数を確認し、動的な構造には許可リストを使います。
placeholderでテーブル名や列名をバインドできますか?
できません。placeholderはデータ値を表します。動的識別子や並び順はアプリ定義の固定選択肢から選び、検証済みの定数だけをSQL構造に挿入してください。