(本記事は理解促進のためのフィクションであり、実在のシステム・ログ・顧客環境とは一切関係ありません。)
ある日、会社のSlackでこんな会話を目にしました。
投稿したのは、機械学習エンジニア出身で、最近アプリ側も触るようになった後輩くん。
普段は、「モデルの精度が〜」とか「学習曲線が〜」と言ってるタイプの彼が、珍しくログを貼ってきた。
最初は「ふざけて変な文字列を入れたユーザーでもいたのかな?」
くらいに思っていたのですが----
次の瞬間、背筋がすっと冷えました。
これは、SQLインジェクション攻撃の典型的なパターンだ。
「え、マジですか?」
一旦状況を整理するために、このログが示すものを説明しました。
「これね、全然笑えないよ。
ログに出てる ' or '1'='1 って、"条件を常に真にする"ための魔法の呪文みたいなものなんだよ」
後輩くんも状況がわかったようで、「これ、結構やばいですかね?」と焦り気味。
もちろんやばい。
でも本当に怖いのは、アプリの実装がこの攻撃を想定していないかもしれないことだ。
Slackのチャンネルが一気にざわつく
別のメンバーも反応し始めます。
「これ、検証ログだよね...?」
「対象APIってどのエンドポイント?」
「そもそもPrepared Statement使ってるっけ?」
普段は静かなチャンネルが、急に"何か起きてる感"で満ちていく。
後輩くんはさらに混乱して、
「え、SQLインジェクションってそんなに危険なんですか?」
と聞いてきました。
SQLインジェクションとは?
まずは定義から。
ユーザーの入力欄に、データベース操作の"命令"を紛れ込ませて、アプリに誤って実行させる攻撃のことです。
Webアプリはどこかで必ずSQLを使ってデータベースに問い合わせますよね。
そのSQL文を文字列連結して作っていると、
攻撃者にとっては"書き換え放題の裏口"になります。
' or '1'='1 がその代表例です。
▼何が起きるの?
ログイン画面で例えると、
SELECT * FROM users WHERE name = '$name';
こういう実装をしている場合、
攻撃者は $name に次のように入力します。
' OR '1'='1
すると、SQL文はこうなります:
SELECT * FROM users WHERE name = '' OR '1'='1';
'1'='1'は常に真。
つまり、
文字列連結でSQLを組み立てていると、' OR '1'='1 のような入力によりWHERE句が常に真となり、パスワード検証や返却レコードの扱いに不備がある場合は認証を突破される恐れがあります。
これがSQLインジェクションの最小構成です。
"小学生でもできる攻撃"と呼ばれることすらあります。
じゃあ、実行されるとどうなる?
1. 個人情報の漏えい
SQLインジェクションが成立すると、攻撃者は次のようなSQLも実行できます。
SELECT * FROM users;
つまり、
- 氏名
- 住所
- メールアドレス
- パスワード(ひどいと平文)
- クレジットカード情報
など、全部盗める状況になります。
本当にあった過去の個人情報漏えい事件😨
「SQLインジェクション 個人情報漏洩」といったキーワードで検索すると、本当にあった過去の事件を多数確認することができます。
2. データ改ざん・削除(地獄です)
攻撃者の目的が破壊なら、もっとひどいです。
DELETE FROM users;
DROP TABLE orders;
UPDATE accounts SET balance=0;
これすら実行できます。
3. 管理者権限奪取 → サイト乗っ取り
もし管理者アカウント情報まで奪われると、
プラットフォームごと支配されます。
- サイトにマルウェアを仕込む
- 偽のページに書き換える
- 全ユーザーをフィッシングに誘導する
ここまで来ると、一般企業ではほぼ対応不能です。
じゃあどうすれば防げるの?
ここが重要なポイント💡
実はSQLインジェクションは、
基本さえ守ればほぼ防げる攻撃でもあります。
1. プリペアドステートメント(Prepared Statement)を使う【最重要】
すべての対策の中で最強で必須。
プリペアドステートメントを使うと、
SQL文とデータ(ユーザー入力)が完全に分離されるので、
「命令として実行されない」
という状態が作れます。
❌️ 悪い例(文字列連結)
cur.execute(f"SELECT * FROM users WHERE name = '{name}'")
✅️ 良い例(パラメータ化クエリ、プリペアドステートメント)
cur.execute("SELECT * FROM users WHERE name = ?", (name,))
これだけで ' or '1'='1 の攻撃は防げます!
2. 入力のバリデーション・サニタイズ
- 数字なら数字だけ
- 文字列の長さ制限
- 危険な文字をエスケープ
これは補助的な対策ですが、確実に効果があります。
3. ORMやフレームワークを使う
ORMは内部で自動的にプリペアドステートメントを使うため、
生SQLを書かない限り安全になりやすい。
4. エラーメッセージに内部情報を出さない
攻撃者にヒントを与えるだけです。
本番では一般的なメッセージだけ返しましょう。
5. DB権限の最小化
- 読み込みだけなら「読み込み専用ユーザー」
- 管理者権限は極力使わない
まとめ:SQLインジェクションは「今すぐ知るべき基本」
Slackの"変なログ"から始まった今回の話。
後輩くんと同じように、
「知らない=危険に気付けない」 という状況は多くの現場で起こり得ます。
SQLインジェクションは怖い攻撃ですが、
逆に言えば、プリペアドステートメントを使うだけでも大部分を防げます。
今日のポイントはこれ。
SQLインジェクション対策の最重要ポイント
「入力されたものは、常に攻撃を意識して扱う」
「SQLは絶対に文字列連結しない」
「プリペアドステートメントを使う」
補足(免責)
本記事のエピソードは、理解を深めるために構成したフィクションです。
登場する人物・会話・ログなどはすべて筆者による創作であり、実在の社員・組織・システムを示すものではありません。
