最終更新
リンドくん
たなべ先生、「SQLインジェクション」って何ですか?
たなべ
SQLインジェクションは、データベースを操作するSQL文を不正に改ざんする攻撃なんだ。
実は、Webアプリケーションの脆弱性の中でも特に危険度が高くて、OWASP Top 10にも常にランクインしているんだよ。
Webアプリケーション開発において、データベースとのやり取りは避けて通れません。
しかし、そこには「SQLインジェクション」という深刻なセキュリティリスクが潜んでいます。
SQLインジェクション攻撃が成功すると、以下のような被害が発生する可能性があります。
この記事では、セキュリティ学習を始めたばかりの方でも理解できるよう、SQLインジェクションの仕組みから具体的な防御策まで、段階的にわかりやすく解説していきます。
HackATAは、エンジニアを目指す方のためのプログラミング学習コーチングサービスです。 経験豊富な現役エンジニアがあなたの学習をサポートします。
✓ 質問し放題
✓ β版公開中(2025年内の特別割引)
リンドくん
「インジェクション」って注入って意味ですよね?何を注入するんですか?
たなべ
その通り!攻撃者が悪意のあるSQL文を注入することで、本来の処理を改ざんするんだ。
まずは具体的な例を見てみよう。
SQLインジェクションは、Webアプリケーションのデータベース操作において、ユーザー入力を適切に処理しないことで発生する脆弱性です。
通常、Webアプリケーションはユーザーからの入力を受け取り、それをもとにデータベースへ問い合わせを行います。この際、入力値をそのままSQL文に組み込んでしまうと、攻撃者が不正なSQL文を実行できてしまうのです。
以下は、SQLインジェクションの脆弱性を持つ典型的なログイン処理のコード例です。
脆弱なPHPコード
このコードの何が問題なのでしょうか?
ユーザーが普通にusernameに「tanabe」、passwordに「mypassword」と入力すれば、以下のようなSQL文が生成されます。
これは正常な動作です。しかし、攻撃者が以下のような入力をした場合はどうなるでしょうか?
admin' --すると、生成されるSQL文は以下のようになります。
SQL文における--はコメントを意味します。つまり、--以降の部分は無視されるため、実際には以下のSQL文が実行されます。
パスワードのチェックが完全に無視され、攻撃者はパスワードなしでadminとしてログインできてしまうのです。
SQLインジェクションは、単なる認証回避だけでなく、さらに深刻な被害をもたらす可能性があります。
データベースの内容を全て取得する攻撃
攻撃者が以下のような入力をした場合を考えてみましょう。
' UNION SELECT username, password FROM users --生成されるSQL文は以下です。
この攻撃により、データベース内のすべてのユーザー名とパスワードが取得されてしまいます。
データを削除する攻撃
さらに悪質な例として、以下のような入力も可能です。
'; DROP TABLE users; --生成されるSQL文:
この攻撃が成功すると、usersテーブルが完全に削除されてしまいます。
このように、SQLインジェクションは単なるログイン回避にとどまらず、データベース全体を破壊する可能性を秘めた非常に危険な脆弱性なのです。
リンドくん
こんな攻撃、どうやって防げばいいんですか!?怖すぎます...
たなべ
安心して!プリペアドステートメントという強力な防御方法があるんだ。
これを使えば、ユーザー入力が絶対にSQL文として解釈されなくなるんだよ。
プリペアドステートメント(準備された文)は、SQL文の構造とデータを完全に分離する仕組みです。
この方式では、以下の2段階でSQL文を実行します。
?や:nameなど)で表す重要なのは、バインドされたデータは常に「データ」として扱われ、決してSQL文の一部として解釈されないということです。
PDOを使用した安全なコード
このコードでは、攻撃者がusernameにadmin' --と入力しても、それは単なる文字列として扱われます。
つまり、「admin' --」という名前のユーザーを検索するだけで、SQL文の構造を変えることはできません。
MySQLiを使用した実装
どの言語でも基本的な考え方は同じです。SQL文のテンプレートとデータを分離し、データは常にデータとしてのみ扱うことがポイントです。
リンドくん
プリペアドステートメントが使えない場合はどうすればいいんですか?
たなべ
古いシステムやレガシーコードでは、エスケープ処理を使う方法もあるよ。
ただし、プリペアドステートメントほど安全ではないから、できれば避けたい方法なんだけどね。
エスケープ処理は、特殊文字を無害化することで、SQL文の構造が変わらないようにする方法です。
具体的には、SQL文で特別な意味を持つ文字(シングルクォート'やダブルクォート"など)の前にバックスラッシュ\を付けて、ただの文字として扱うようにします。
この方法では、攻撃者がadmin' --と入力しても、エスケープ処理によりadmin\' --となり、シングルクォートがただの文字として扱われます。
エスケープ処理には、以下のような問題点があります。
すべての入力値に対して確実にエスケープ処理を適用する必要があり、一箇所でも漏れがあると脆弱性が残ります。
特定の文字エンコーディング(特にマルチバイト文字)では、エスケープ処理が正しく機能しない場合があります。
数値型のパラメータでは、クォートで囲まないため、エスケープだけでは不十分です。
脆弱な例
攻撃者がid=1 OR 1=1と入力すると、すべてのユーザー情報が取得されてしまいます。
安全な対策
このように、エスケープ処理は補助的な手段と考えるべきで、可能な限りプリペアドステートメントを使用することを強く推奨します。
リンドくん
プリペアドステートメント以外にも、やっておくべきことはありますか?
たなべ
多層防御の考え方が重要なんだ。
プリペアドステートメントだけでなく、いくつかの対策を組み合わせることで、より堅牢なシステムになるよ。
ユーザー入力が期待される形式に合っているか、事前にチェックすることも重要です。
バリデーションの例
ただし、バリデーションだけではSQLインジェクションを完全には防げません。あくまでプリペアドステートメントと組み合わせて使用する補助的な手段です。
データベースユーザーには、必要最小限の権限のみを付与することが重要です。
権限設定の例(MySQL)この設定により、たとえSQLインジェクション攻撃を受けても、以下のような被害を軽減できます。
詳細なエラーメッセージは、攻撃者にとって貴重な情報源になります。
脆弱な例
安全な例
ORM(Object-Relational Mapping)ツールを使用すると、SQLインジェクションのリスクを大幅に軽減できます。
Laravelの例(PHP)ORMを使用すると、直接SQL文を書く機会が減るため、SQLインジェクションのリスクが大幅に低下します。ただし、生のSQLを書く機能もあるため、その際は注意が必要です。
WAFは、Webアプリケーションへの攻撃を検知・ブロックするセキュリティツールです。
WAFができることは以下です。
ただし、WAFは万能ではありません。アプリケーション側での適切な対策(プリペアドステートメントなど)が最も重要で、WAFはあくまで追加の防御層として考えるべきです。
リンドくん
SQLインジェクション、怖いけどちゃんと対策すれば防げるんですね!
たなべ
その通り!プリペアドステートメントを正しく使うことが何より大切なんだ。
それに加えて、入力値のバリデーションや最小権限の原則など、複数の対策を組み合わせることで、より安全なアプリケーションを作れるよ。
この記事では、SQLインジェクションの仕組みから具体的な防御策まで解説してきました。
重要なポイントをおさらいしましょう。
SQLインジェクション対策は、Webアプリケーション開発における最も基本的かつ重要なセキュリティ対策の一つです。
開発の初期段階から、これらの対策を意識してコーディングする習慣を身につけることが大切です。
「自分のコードは大丈夫だろうか?」と少しでも不安に感じたら、それは非常に良い兆候です。
セキュリティ意識の高い開発者は、常に自分のコードを疑い、改善し続けます。
安全なデータベース操作の第一歩として、今日からプリペアドステートメントを徹底してみましょう!