Table of Contents
エスケープの必要がないAPIの制限を熟知する
出力を無害化する方法には
- エスケープする
- エスケープする必要がないAPIを利用する
- バリデーションする
の3つの方法がある。「エスケープする必要がないAPI」は盲目的に利用して安全とは言えない。
非適合コード例(Prepared query)
プリペアードクエリ/プレイスホルダーはSQLパラメータとSQL文を分離し、SQLインジェクションを防止できる。しかし、SQL識別子、SQL語句の分離は行えない。またLIKEクエリや正規表現によるクエリを利用する場合、それぞれLIKE構文、正規表現構文に合ったエスケープを行わないと安全性を確保できない場合がある。
<?php $db = pg_connect('host=localhost'); $result = pg_query_params($db, 'SELECT * FROM user_table_prefix_'.$_GET['tblname'].' WHERE id = $1', [$_GET['id']]);
適合コード例(Escape)
プリペアードクエリ/プレイスホルダはSQLパラメーターしか分離しない。他のパラメーターやSQLパラメーターが他のコンテクストで解釈される場合は別の対策が必要となる。
<?php $db = pg_connect('host=localhost'); // SQL識別子(テーブル名、フィールド名など)は”(ダブルクオート)で括ってエスケープが必要 $result = pg_query_params($db, 'SELECT * FROM '. pg_escape_literal('user_table_prefix_'.$_GET['tblname']).' WHERE id = $1', [$_GET['id']]);
pg_escape_literal()関数は引数として渡された文字列をSQL識別子としてエスケープ(”は”でエスケープ)し、”で括った文字列を返します。
識別子のエスケープ方法はSQLデータベースによって異なる場合があります。例えば、MySQLの場合は動作モードによってエスケープ方法が異なったりします。正しいエスケープ方法でエスケープします。
識別子を安全に処理するにはエスケープする方法以外に識別子として利用可能な文字でのみ構成されているかバリデーションする方法も利用できます。
SQL識別子のエスケープ/バリデーションで注意しなけれならないのは多くのデータベースで” ”(半角スペース)も識別子に利用可能な文字であることです。この為、文字列がSQL識別子として利用可能な文字だけで構成されているのみでなく識別子の文字列を区切る”(ダブルクオート、SQL標準の場合)で括られていなければなりません。
非適合コード例(execv, execve)
OSのシステムコールとしてexecv/execveなどを利用するAPIを用いれば、OSコマンドの引数をエスケープせずに安全に利用できる。execv/execveはコマンドと引数を分離するが、コマンド自体が変数の場合は任意コマンドを実行できる。
PHPの場合、pcntl_exec関数はexecv/execveを内部的に利用している。
<?php pcntl_exec('/path/to/user/cmd/'.$_GET['cmd'], $_GET['params']);
上記のコードには2つの問題がある。
- execv, execveシステムコールはコマンドとその引数を分離するが、コマンド部分がインジェクションに脆弱である場合、安全性を維持できない。(SQLのプリペアードクエリにおけるプリペア文とパラメーターの関係と同じ)
- execv, execveシステムコールはコマンドとその引数を分離するが、コマンドに脆弱性がある場合、安全性を維持できない。(コマンドの引数を更に別のコマンドの引数に利用する場合などにこの問題が発生しうる。シェルスクリプトなどではこのような状況は容易に起こり得る)
適合コード例(Validation)
通常のホワイトリストによるバリデーションを行う。
<?php if (!isset($_GET['cmd']) || strspn($_GET['cmd'], 'abcdefghijklmnopqrstuvwxyz') !== strlen($_GET['cmd'])) { throw Exception('cmd validation error'); } foreach($_GET['params'] as $param) { if (strspn($param, 'abcdefghijklmnopqrstuvwxyz1234567890') !== strlen($param)) { throw Exception('param validation error'); } } pcntl_exec('/path/to/user/cmd/'.$_GET['cmd'], $_GET['params']);
バリデーションの注意点
- コマンド引数でパストラバーサルが可能な場合、任意コマンドに実行許してしまう。コマンドとして“.”(ピリオド)、“/”,“\”(スラッシュ、バックスラッシュ)の許可には十分な注意を払う。
- パラメーター引数は指定したコマンドにより、更に別のコマンド実行に利用される可能性もある。この為、シェルの特殊文字を一文字でも許可するとリスクが発生する。文字種によるバリデーションより、特定の文字列のみをホワイトリスト形式でバリデーションする方がより安全性が高い。
例外
例外はない。
リスク評価
[ルール/推奨事項のリスク評価]
[評価例 - 英語表記に統一]
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| IDS05-J | medium | unlikely | medium | P4 | L3 |
