Closed7

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

tamaco489tamaco489

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

tamaco489tamaco489

極端な例(pwをDBで管理ほぼないはず)

SELECT * FROM users WHERE username = '入力されたユーザー名' AND password = '入力されたパスワード';

これに、攻撃者が以下のような値を入力:

  • ユーザー名: OR '1'='1
  • パスワード:任意の文字列
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '任意の文字列';

このSQL文は常に真(true)になるため、本来認証が必要な画面に不正にログインできてしまう。

tamaco489tamaco489

Go言語で書くとこんな感じになる。

安全じゃない書き方の例

username := "user1"
password := "pass123"

// ⚠️ 危険な書き方
query := "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"
rows, err := db.Query(query)

→ ユーザーの入力がそのままSQL文に入ってしまうため、SQLインジェクションが可能。

tamaco489tamaco489

✅ 安全?というか推奨されてる書き方

? を使ったプレースホルダ を使用する

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インジェクションを抑止できる。

tamaco489tamaco489

アプリケーションがプレースホルダをどう処理するか

  1. SQL文を準備する(パース・準備)
  • アプリケーションは、SQL文(例:SELECT * FROM users WHERE username = ?)を文字列としてDBに送信。
  • プリコンパイルされたクエリ(準備済みステートメント)として扱われる。
  • プレースホルダ(?)の位置だけを記憶し、値はまだ埋まっていない。
  • 🔒 この時点で、SQLインジェクションの可能性は完全に排除されている(なぜなら構造が確定しているため)。


  1. プレースホルダに値をバインド(代入)
  • アプリケーションが、プレースホルダに対応するユーザー入力などの値を渡す。
  • 値は文字列、数値、日付などの型として明確に分離され、SQL構文とは別扱いされる。
  • DBドライバがこの値をエスケープ・検証し、SQL文の中で安全に処理される。
db.QueryRow("SELECT * FROM users WHERE username = ?", "admin")


  1. SQL文実行(DBが処理)
  • 値がバインドされた状態で、クエリがDBに送信され、実行される。
  • DBは、構文を改ざんされる心配がないまま、クエリを高速かつ安全に処理。
tamaco489tamaco489

対策

  • 入力値の検証(バリデーション)
    • 数値は数値、メールはメール形式などをきちんと検証する。※サニタイズは基本ではあるが、それだけでは不十分。


  • ORM(Object Relational Mapper)の活用
    • ORMを使えば、SQL文を直接書かずに済むため、インジェクションのリスクを根本から低減できます。


  • データベース権限の最小化
    • Webアプリ用DBユーザーには、不要なDROP, DELETE, ALTERなどの権限を与えないようにする。
このスクラップは4ヶ月前にクローズされました