FlawOpen

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断片に連結すると注入される可能性があります。

コードベースの確認方法

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構造に挿入してください。

公式資料と参考情報

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com 引用符のエスケープだけでSQL Injectionを防げる? ORM を使えば SQL インジェクションを防げるか Knex.jsはSQL Injectionを防げますか? MyBatisの${}と#{}:SQL Injectionを防ぐ MySQLiとPDO:安全なPrepared Statement NHibernate は SQL Injection を防げますか?