Closed7
SQLインジェクションについてさっくり説明する用

端的に言うと、Webアプリケーションのセキュリティ脆弱性の一つで、外部からの入力を通じてSQL文を意図的に改竄し、不正なDB操作を行わせる攻撃。

極端な例(pwをDBで管理ほぼないはず)
SELECT * FROM users WHERE username = '入力されたユーザー名' AND password = '入力されたパスワード';
これに、攻撃者が以下のような値を入力:
- ユーザー名:
OR '1'='1
- パスワード:任意の文字列
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '任意の文字列';
このSQL文は常に真(true)になるため、本来認証が必要な画面に不正にログインできてしまう。

Go言語で書くとこんな感じになる。
安全じゃない書き方の例
username := "user1"
password := "pass123"
// ⚠️ 危険な書き方
query := "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"
rows, err := db.Query(query)
→ ユーザーの入力がそのままSQL文に入ってしまうため、SQLインジェクションが可能。

✅ 安全?というか推奨されてる書き方
?
を使ったプレースホルダ を使用する
username := "user1"
password := "pass123"
// プレースホルダを使って安全に値を埋め込む
query := "SELECT * FROM users WHERE username = ? AND password = ?"
row := db.QueryRow(query, username, password)
// 結果の取得例
var id int
err := row.Scan(&id)
if err != nil {
// ユーザーが存在しない、またはエラー処理
}
QueryRow や Query に引数で値を渡すことで、Goのドライバが自動的にエスケープ・バインドしてくれるのでSQLインジェクションを抑止できる。

アプリケーションがプレースホルダをどう処理するか
- SQL文を準備する(パース・準備)
- アプリケーションは、SQL文(例:SELECT * FROM users WHERE username = ?)を文字列としてDBに送信。
- プリコンパイルされたクエリ(準備済みステートメント)として扱われる。
- プレースホルダ(?)の位置だけを記憶し、値はまだ埋まっていない。
- 🔒 この時点で、SQLインジェクションの可能性は完全に排除されている(なぜなら構造が確定しているため)。
- プレースホルダに値をバインド(代入)
- アプリケーションが、プレースホルダに対応するユーザー入力などの値を渡す。
- 値は文字列、数値、日付などの型として明確に分離され、SQL構文とは別扱いされる。
- DBドライバがこの値をエスケープ・検証し、SQL文の中で安全に処理される。
db.QueryRow("SELECT * FROM users WHERE username = ?", "admin")
- SQL文実行(DBが処理)
- 値がバインドされた状態で、クエリがDBに送信され、実行される。
- DBは、構文を改ざんされる心配がないまま、クエリを高速かつ安全に処理。

対策
- 入力値の検証(バリデーション)
- 数値は数値、メールはメール形式などをきちんと検証する。※サニタイズは基本ではあるが、それだけでは不十分。
- ORM(Object Relational Mapper)の活用
- ORMを使えば、SQL文を直接書かずに済むため、インジェクションのリスクを根本から低減できます。
- データベース権限の最小化
- Webアプリ用DBユーザーには、不要なDROP, DELETE, ALTERなどの権限を与えないようにする。
このスクラップは4ヶ月前にクローズされました