たった1行でシステムを乗っ取るSQLインジェクション
13分07秒 | DBSQLFE
基本情報技術者試験の頻出テーマを解説した音声コンテンツです。
トランスクリプト(字幕テキスト)
今回のテーマは、セキュリティ脆弱性の「王様」とも呼ばれるSQLインジェクションです。手元にある資料がですね、新人エンジニアのアキラと、指導役のシニアエンジニア・ヨミの対話を通じて、この脆弱性の本質に迫るという、そういうシナリオです。 へぇ。これ、あの、単なる技術者だけの話ではないんですよね。なぜ、たった1行のコードの油断が、時にビジネスそのものを破壊するほどの力を持ってしまうのか、そのメカニズムをこのシナリオに沿って解き明かしていくのが、今回の目的です。 はい。では、早速このシナリオを紐解いていきましょう。ミッションは明確ですね。「OR 1=1」。この、一見すると意味不明な文字列が、なぜシステムを乗っ取る「魔法の呪文」になり得るのか、その核心に迫ります。 物語の冒頭なんですけど、新人エンジニアのアキラはすごく自信満々で。ええ。「入力されたIDとパスワードをそのままSQLの文字列にガッチャンコするだけ。シンプル・イズ・ベストです」と。 この「文字列をくっつける」っていう単純な発想、なんか僕もプログラム学び始めた頃はついやってしまいそうだな、って感じました。ああ、分かります。多くの人が通る道かもしれませんね。 はい。でも、ここでシニアエンジニアのヨミが使う比喩が、この行為の危険性をすごく的確に表現してるんですよ。と言いますと。「これは城の門番に『合言葉を知っている人を通せ』と命じるのとは全く違う」と。 ええ。「門番に直接命令を上書きできるメガホンを渡す行為なんだ」って言うんですね。「メガホンを渡す」。つまり、本来は合言葉をチェックするだけの門番に、「お前はもうチェックしなくていい、全員通せ」みたいな、そういう直接命令ができてしまうということですか? まさにその通りです。ユーザーからの入力、例えばパスワードっていうのは、本来は「検証されるべきデータ」じゃないですか。はい、データですね。 ところが、文字列をそのまま連結することで、それがSQL文という「データベースへの命令」の一部に変わってしまう。この「データと命令の境界線」が曖昧になるっていうのが、全ての元凶なんです。 なるほど。でも、ここで素朴な疑問なんですけど、最近のプログラミング言語とか、ま、よく使われるフレームワークを使っていれば、こんな初歩的なミスって自動的に防いでくれる仕組みになってるんじゃないんですか? なぜ、今でもこの問題がなくならないんでしょう。あぁ、非常に良い質問ですね。確かに、現代の主要なフレームワークには、この脆弱性を防ぐ仕組みが標準で備わっています。 ですよね。ただ、問題が根絶されない理由がいくつかありまして。一つは、納期とか予算のプレッシャーから、つい安易な実装に走ってしまうケース。うーん、ありがちですね。 もう一つは、古いシステムの改修案件なんかで、過去の危険なコードがそのまま残ってしまっている。あぁー。あと、意外に多いのが、ネット上の古いブログ記事とかチュートリアルを参考にして、脆弱なコードをそのままコピー&ペーストしちゃう学習者とか、若手のエンジニアがいることなんです。 うわぁ、それはありそうですね。良かれと思って学んだ知識が、実は時限爆弾の作り方だった、なんてこともあり得ると。ええ。アキラもそんな意図しない罠にハマってしまったわけですね。 そうなんです。そしてヨミは、その危険性をアキラに体感させるために、具体的な「呪文」を教えるわけです。ここからが、このお話の本当に恐ろしいところですよね。ヨミはアキラに、パスワード欄に「OR 1=1」と入力させると。 ええ。すると、正しいパスワードを全く知らないのに、なぜかシステムにログインできてしまう。データベースの中では、えっと、一体何が起きていたんですか? あの、アキラが書いたコードのせいで、データベースはSQL文をですね、このように解釈します。WHERE ID = 'user' AND PASS = '' OR 1=1。 はい。元々アキラが意図していたのは、ANDの前までだったんですよ。PASS = '(く)'のこのシングルクォーテーションで終わるはずだった。なるほど。 でも、入力された文字列のせいで、その後に「OR 1=1」がくっついちゃったわけです。「OR 1=1」。これって「または 1は1と等しい」っていう、1が1と等しいのって、ま、当たり前というか、絶対的な真実ですよね。 その通り。そこがポイントなんです。SQLの条件式って、「真(しん)」つまり「True」か「False」かで判断されますよね。ええ。 ANDで結ばれた前半部分、「IDとパスワードが一致するか」っていう条件が、たとえ偽(ぎ)だったとしても、ORで繋がれた後半の「1=1」が常に真(しん)なので、この条件式全体が「真(しん)」と評価されてしまうんです。 なるほど。つまり認証を「突破」するというか、ええ。認証の仕組み自体を、もう乗っ取っている感覚に近いですね。「パスワードは合っていますか?」っていう問いに対して、無理やり「はい、合っています」と答えさせているような。 まさに。データベースからすれば、「条件に一致しましたよ」と正直に結果を返すしかない。結果、認証は完全に無力化されると。 資料の中で、アキラが「僕が書いた1行のせいで、城の裏門が全部開いちゃったってことですか?」って愕然としてますけど、まさにその通りなんです。 しかしヨミは、それだけじゃないと、さらに深刻な警告をしますよね。ええ。もし入力された文字列が「; DROP TABLE users --」だったら、と。 この一言で、アキラの顔が青ざめるわけですが、えっ、ちょっと待ってください。パスワード欄に入力した文字列で、データベースのテーブルを消せるんですか?そうなんです。ログイン画面からそんな操作ができてしまうなんて、想像もつきませんでした。 これがSQLインジェクションの、ま、最も恐ろしい側面なんです。不正ログインっていうのは、いわば序章にすぎません。この脆弱性があるということは、データベースに対して、もうありとあらゆる命令を実行できる状態だということです。 「DROP TABLE users」。「usersテーブルを削除しろ」っていう、すごく直接的で暴力的な命令ですね。はい。ここでのポイントは、文字列の最初にある「セミコロン」。 セミコロン。これで、元々あった認証用のSQL文を強制的に終わらせて、全く新しい第2の命令として「DROP TABLE users」を注入しているんです。なるほど。 そして、最後の「ハイフン2つ」は、それ以降の文字列をコメント、つまり無効化するための記述です。あ、じゃあそこで後ろの余計な部分を全部無視させてるんですね。まさにその通りです。 これによって、元のSQL文の残骸が悪影響を及ぼさないようにしている。非常に巧妙な手口ですよね。うーん。ということは、ユーザーの氏名、住所、購入履歴、クレジットカード情報、そういった企業の生命線とも言えるデータが全て詰まったテーブルを、一瞬で跡形もなく消し去ることが可能になると。 そういうことです。実際にこういう攻撃で、サービスが数日間にわたって停止して、信用の失墜と顧客離れで、数億円規模の損失が出たっていう事例も過去にはあります。うわぁ。 データの流出や改ざんだけでも致命的ですけど、データが完全に消滅してしまえば、事業の継続自体が不可能になる。まさに一撃で会社が傾く事態に直結します。 しかも、攻撃ってデータを消すだけじゃないんですよね。なんか、もっと陰湿な手口もあるって聞きますけど。ええ。例えば、「ブラインドSQLインジェクション」っていう、より巧妙な攻撃があります。「ブラインド」? これは、画面にエラーとかを直接表示させずに、データベースの反応の「ある・なし」だけで情報を抜き取る手法なんです。え、どういうことですか? 例えば、「ユーザーIDの1文字目は『A』ですか?」っていうSQL文を投げて、システムが正常に応答すれば「Yes」、エラーになれば「No」と判断する。これを延々と繰り返して、時間をかけて少しずつ、1文字ずつデータを盗み出すんです。 まるでデータベースと20の質問をしてるみたいですね。派手な破壊行為じゃない分、検知が難しく、気づいた頃には全ての情報が盗まれていた、なんてことになりかねない。おっしゃる通りです。だからこそ、入り口の対策が何よりも重要になるわけです。 では、この最悪の事態を防ぐために、具体的にどうすればいいのか。ヨミは解決策として、「プレースホルダ」とか「バインド変数」の重要性を説きますね。はい。これは一体どういう仕組みなんでしょうか。 ここでもヨミの比喩が秀逸なんです。「入力された文字を命令としてではなく、ただの『無害な値』として扱うための消毒液だ」と。「消毒液」。さっきの「メガホンを渡す」とは全く逆の発想ですよね。 なるほど。でも、結局はユーザーが入力した値を使ってデータベースに問い合わせるわけじゃないですか。文字列をくっつけるのと、具体的に何が違うんでしょう。あぁ、これは決定的かつシンプルな違いがあります。それは「手順」です。「手順」ですか。 プレースホルダを使う場合、まずSQL文の「骨格」だけを先にデータベースに送って、「これからこういう形の命令文を実行しますよ」って宣言するんです。はいはい。 例えば、WHERE ID = ? AND PASS = ?っていうように、値が入る部分を「?」みたいな記号で仮置きしておく。まず命令のテンプレートを確定させてしまうんですね。そうです。 データベース側はそのテンプレートを受け取って、「OK、これはIDとパスワードを検証する命令だね」って先に解釈して準備します。ふむふむ。そして、その後に、ユーザーが入力した文字列を「この『?』に入れるデータはこれです」と別便で送るんです。 あぁ、なるほど。「命令文」と「データ」を完全に分離して渡す、ということですか。その通りです。こうすることで、たとえ後から送られてきたデータが「OR 1=1」みたいな悪意のある文字列だったとしても。 データベースは、「あぁ、そういう名前のパスワードなんだな」って判断するだけ。命令の一部として解釈することは決してありません。骨格はもう確定してますからね。 まさに、資料の中でアキラが気づいたように、ウイルスを素手で触るんじゃなくて、きちんと消毒してから扱うイメージですね。入力値っていう何が入ってるか分からないものを、まず無害化してからデータベースに渡すと。 ええ。これが「Never Trust User Input」、つまり「ユーザーの入力は決して信用するな」というセキュリティの基本原則です。なるほど。WAF、Webアプリケーションファイアウォールみたいな、外部からの攻撃を検知してブロックする仕組みも重要ですけど。 それはあくまで追加の防御層。アプリケーション自体に脆弱性がないこと、つまりプレースホルダを正しく使うことが、最も根本的で重要な対策と言えます。では、今回の話をまとめていきましょう。現場で守るべき鉄則は本当にシンプル。 まず一つ。「SQLは文字列結合で絶対に作るな」。はい。そしてもう一つ。「必ずプレースホルダを使って、ユーザーからの入力を無害化せよ」。この二つに尽きますね。その通りです。 「OR 1=1」というのは、情報処理の試験でよく見る記号かもしれません。ええ、見ますね。しかし、その記号の裏には、情報が流出し、データが改ざんされ、最悪の場合にはビジネスの全てが消し飛ぶという現場の本当の恐怖が隠れていることを忘れてはなりません。 アキラが信じた「シンプル・イズ・ベスト」という言葉は魅力的ですけど、セキュリティの世界においては、そのシンプルさが最大の脆弱性を生むこともあると、非常に教訓的なシナリオでした。
このコンテンツは Web society で視聴・学習できます。