SQLインジェクションの概要
SQL文を組み立てるWebアプリケーションの脆弱性を利用してデータベースを不正操作する攻撃をいいます。
情報処理安全確保支援士では、SQL構文が登場したらSQLインジェクションの問題です。SQLの構文のほか、SQLインジェクションの脆弱性とその対策が問われます。
「インジェクション」とは、注入を意味しており、不正なコードをプログラムに注入することで、不正な命令を実行させます。
SQLインジェクションのほか、OCコマンドインジェクション、HTTPヘッダインジェクション、メールヘッダインジェクションがあります。
SQLの構文
SQLの知識は応用情報処理技術者試験(午前)レベルで問題ありません。
例えば、下のようなデータベースがあるとします。
id | name | date | age | user | password |
---|---|---|---|---|---|
001 | Taro | 2025/1/1 | 24 | aaa | pass |
002 | Jiro | 2025/2/1 | 32 | bbb | pass2 |
userが「aaa」でpasswordが「pass」の場合のidとnameを取得するSQLは以下のとおりです。
SELECT id, name FROM LIST WHERE user = ‘aaa’ and password = 'pass'
リテラル
リテラルはSQL文中で使用される定数をいいます。文字列型、数値型、日付型などがあります。上記の’aaa’や’pass’は文字列型リテラルです。
型 | 例 | 解説 |
---|---|---|
文字列型 | name = ‘Taro’ | 文字列をシングルクォートで囲みます。 |
数値型 | age = 24 | 数値 |
日付型 | date = ‘2025/01/01’ | yyyy/mm/ddをシングルクォートで囲みます。 |
対象となる文字列中にシングルクォートが含まれてる場合、シングルクォートを重ねます。
(例)「McDonal’s」→「’McDonal”s’」
ここで、シングルクォートを重ねなかった場合、それ以降はリテラルとみなされません。リテラルとならない部分がSQL文の構文に反するため、構文エラーとなります。
(例)「’McDonal’s’」→リテラルは「’McDonal’」→「s’」が余り構文エラー
コメント
コメント「--」はコメントと呼び、これ以降はSQL文に含まれません。「--」を追加することで、SQL文の一部を無効化することができます。
SQLインジェクションの脆弱性
IPA「安全なウェブサイトの作り方」で指摘される脆弱性を紹介します。
情報漏えい
id | name | date | age | user | password |
---|---|---|---|---|---|
001 | Taro | 2025/1/1 | 24 | aaa | pass |
002 | Jiro | 2025/2/1 | 32 | bbb | pass2 |
ログイン画面でuserに「' or 1=1 --」、passwordに「a」を入力します。
SELECT name FROM LIST WHERE user = ' ' or 1 = 1 -- ' and password = 'a'
この場合、userは空白又は「1=1」(これ以降はコメント扱いのため無視)となります。
「1=1」は全ての行について真(Ture)となり、全ての行のidとnameが選択され、情報漏えいにつながります。
このほか、エラーメッセージによる情報漏えいもあります。
指定したuserに該当がない場合に「該当するユーザーがありません。」などのエラーが表示されると、指定したユーザーがデータベースがないことが判明してしまいます。ユーザー名とパスワードの一方のみに誤りがあった場合、「ユーザー名またはパスワードが正しくありません。」と表示し、どちらに誤りがあるか判明しないようにします。
また、英語のエラーメッセージなどが表示される場合、使用しているSQLの種類など、攻撃のヒントが含まれている可能性があります。
情報の改ざんや消去
UPDATE文を組むことでデータを更新することができます。
UPDATE LIST SET name = 'Hanako'
この場合、LIST表中のnameが全て「Hanako」に書き換えられてしまいます。
認証回避による不正ログイン
パスワードにSQLを組み込むことにより、パスワードによる認証を回避することができます。
SELECT * FROM LIST WHERE id = 'Taro' and password = ' ' or 'a' = 'a'
この場合、パスワードは空白又は「’a’ = ‘a’」となります。「’a’ = ‘a’」によりpasswordの条件は無視され、「id = ‘Taro’」のみがWHERE句の条件となることから、パスワードを知らなくてもログインできてしまいます。
OS コマンドの実行
データベース内で複数の連続したSQL命令を一連の処理としてプログラムとして保存及び実行できる機能をストアプロシージャといいます。
ストアプロシージャの中にはOSのコマンドシェルを起動するものもあります。
SQLインジェクションの対策
IPA「安全なウェブサイトの作り方」で指摘される対策を紹介します。
プレースホルダを実装する(バインド機構)
SQLインジェクションの最も有効な対策は、プレースホルダによりSQL文を組み立てることです。
プレースホルダとは、後から値や文字を入れるまで一時的に入れておく値や文字をいい、実際に値や文字を割り当てることをバインド機構といいます。プレースホルダはバインド変数と呼ばれることもあります。
SELECT id, name FROM LIST WHERE user = ‘aaa’ and password = ?
この例では、「?」がプレースホルダです。
プレースホルダにパラメタを入力することによりSQL文が実行されますが、パラメタ(バインド値)を文字定数として取り扱うため、正しいリテラルが組み立てられます。
なお、プレースホルダが含まれたSQL文(Prepared Statement)をデータベースエンジンにあらかじめ送信した上で、パラメタをデータベースエンジンに送信することで、データベースエンジン側でバインドする方法を静的プレースホルダといいます。一方で、アプリケーション側のライブラリ内でバインドする方法を動的プレースホルダといいます。セキュリティの観点からは、静的プレースホルダの方が安全です。
エスケープ処理等を行うAPIを利用する
プレースホルダを導入することが困難であるときは、エスケープ処理が有効です。
上記で解説したように、SQLインジェクションは文字列を連結するよりSQL文を組み立てることにより行われます。この際に、特別な意味を持つ記号文字(「’」や「\」など)を特別な意味を持たない記号文字(「”」や「\\」など)に変換するエスケープ処理を実施することで、不正なSQL文が成立しないようにします。
ただし、その文字をどのようにエスケープ処理するかはデータベースエンジンの種類により異なるため、プレースホルダによる方法の方が安全です。
確実にエスケープ処理するため、リテラルを文字列として生成する専用のAPIを利用することが推奨されています。
Webアプリケーションに渡されるパラメータに SQL 文を直接指定しない
HTTPリクエストのhiddenパラメータ等にSQL文をそのまま指定する実装は、パラメタが改変されるリスクがあるため、避けるべき実装とされています。
エラーメッセージをそのままブラウザに表示しない(保険的対策)
上記「SQLインジェクションの脆弱性」の「情報漏えい」で解説したとおり、エラーメッセージは攻撃者にとってヒントを与える可能性があることから、ブラウザに表示しないことが望まれます。
データベースアカウントに適切な権限を与える(保険的対策)
万が一不正なSQL文が実行された場合であっても、データベースが改ざんされないよう、最小権限の原則により、データベースアカウントに適切な権限を付与することが大切です。例えば読み出し権限のみ付与することなどが考えられます。