インジェクション攻撃には様々な手法があります。メモリ管理をプログラマが行うC言語などではメモリにインジェクションするバッファローオーバーフロー/アンダーフロー、テキストベースのインターフェースではテキストインジェクション(JavaScriptインジェクション、SQLインジェクションなど)があります。
これらのインジェクション脆弱性はなぜ発生するのでしょうか?
今回は「インジェクション対策、基礎の基礎」の話です。
インジェクション脆弱性の原因
インジェクション脆弱性が発生する原理は非常に簡単です。インジェクション脆弱性は
- 命令とデータの分離
に失敗した場合に発生します。命令とデータとは何でしょうか?
脆弱性が発生する仕組みと対策が単純なSQLを例に解説しますが、この考え方はプラットフォームや出力先に関係なく全ての出力先に利用できます。
命令とデータ
SQLの命令とはSQL文に「意味」を持たせる文字列です。
- SELECT
- INSERT
- UPDATE
- DELETE
- UNION
- WHERE
これらは一部でSQL文として意味を持った文字列全てが「命令」です。
SQL文におけるデータとはSQL文として意味を持たず、SQL文を実行する為に必要となるパラメーターがデータです。データには
- テーブル名、フィールド名などの識別子
- クエリ条件などのリテラル
があります。
インジェクション攻撃
SQLインジェクションは「本来データである部分」に「SQL命令」を混入させることで攻撃します。よくあるSQLインジェクションの例(PHP+PostgreSQLの場合)は以下のようなコードです。
pg_query("SELECT * FROM $table WHERE category = $category ORDER BY date $order" );
このコードは$table、$category、$order変数にそれぞれテーブル名、カテゴリ名、ソート順序(DESCまたはASC)が保存されている事を期待しています。プログラマの意図としては$table、$category、$order変数がデータです。
$table、$category、$order変数に「データ」ではなく「命令」が混入すると、SQLインジェクションが可能になります。
例えば、$tableに’buyers; DROP TABLE product; —‘が設定されている場合、実行されるSQL文は
SELECT * FROM buyers; DROP TABLE product; -- WHERE category (以下省略)
になります。このSQL文は文法的に正しいのでデータベースサーバーはSQL文を実行し、productテーブルの削除権限があれば削除します。
この例では、本来は「データ」であるべき$tableに
- SQL文を区切る命令である「;」が挿入され
- 次にテーブルを削除する「DROP TABLE」命令が挿入され
- 文字列をコメント化する「–」命令が挿入
されています。
$tableで指定されるテーブル名(識別子)のみでなく、$categoryで指定されるパラメーター(リテラル)でも$orderで指定されるSQL語句(SQL命令)に「命令」が混入していてもインジェクションが可能になります。
インジェクション対策の基本は命令とデータの分離
インジェクション攻撃が可能になる原因は「命令とデータの分離」に失敗していることにあります。
pg_query("SELECT * FROM $table WHERE category = $category ORDER BY date $order" );
このコードでは「データとして渡した」変数すべて、「命令」と分離ができていない状態で渡しています。
インジェクション対策はどのような方法でも「命令」と「データ」を確実に分離できれば構いません。
SQLインジェクション対策の場合、
- エスケープを利用(識別子・リテラルの分離)
- プリペアードクエリを利用(リテラルの分離)
- バリデーション(SQL語句の保護)
これらが確実な「命令」と「データ」の分離に必要です。
命令をデータを分離するAPIを利用しても、データが誤って命令と解釈されないエスケープを使っても同じ効果を得られます。
ただし、命令とデータを分離するAPIは全てのテキストインターフェースで用意されているとは限りません。分離はできても「命令」の部分に「データ」を埋め込む事を禁止できないAPIもあります。このため、セキュリティの基礎教育では不完全なAPIより確実なエスケープ・バリデーションを合わせて教える事が不可欠です。
入力データバリデーション
インジェクション対策として忘れがち、というより忘れられている/無視されている対策に入力データバリデーションがあります。出力対策の3原則+1原則の”+1原則”が入力データバリデーションです。入力データバリデーション無しのインジェクション対策はあり得ません。
まとめ
SQLインジェクションに限らず、全てのインジェクション対策は「命令」と「データ」を確実に分離することで対策可能です。KVS(Key Value Store)がSQLデータベースより安全であるのは、インターフェースレベルで命令とデータを完全に分離しているからです。KVSはテキストインターフェースの命令が存在しません。データを全てAPIで渡すので命令をインジェクションされる余地がありません。
(注:MongoDBインジェクションの様にデータ形式でのインジェクションが可能となる場合がある。KVSでもテキストによる命令がある場合、インジェクションの可能性があることに注意)
「命令」と「データ」の分離はどのようなプラットフォームを利用している場合でも変わらない、共通のセキュリティ対策です。どんな出力先でも
- 命令とデータが確実に分離できているか?
を意識し、保証すれば安全に出力できます。
基本はとても簡単ですよね!楽しく、セキュアにコーディングしましょう!