正規表現インジェクション

(Last Updated On: 2018/08/13)

一文字でも意味がある文字があるとインジェクション攻撃が可能な場合が多いです。正規表現も例外ではありません。

正規表現インジェクションとは

正規表現に対する攻撃にはReDoS攻撃(検索対象の文字列を使って攻撃)がありますが、正規表現自体を使ってインジェクション攻撃が可能です。ヌル文字インジェクションと組み合わせて攻撃も可能です。

正規表現インジェクションで攻撃可能なパターンは正規表現の一部が変更可能で、検索可能な文字列を制限している場合です。

 

正規表現インジェクションのパターン

以下のようなログファイルがあるとします。

14:14:15 bob[14] Login from IP 10.10.10.123
14:14:30 tom[14] Login from IP 10.10.20.234
14:15:27 bob[34] Update password
14:15:27 tom[38] Update email from tom@example.com to tom@example.org
14:18:12 bob[15] Logout
14:19:43 bob[15] Logout

システムにbobがログインしている場合に、自身のログのみを検索する正規表現は以下のような物となる。

(.*? +bob\[\d+\] +.*<SEARCHTEXT>.*)

<SEARCHTEXT>はユーザーが入力する検索条件となる。この場合、攻撃者が

.*)|(.*

を入力した場合、検索用の正規表現は次のようになり

(.*? +bob\[\d+\] +.*.*)|(.*.*)

bob以外のログエントリ全てを取得可能となる。

正規表現インジェクションに脆弱なコード例

脆弱なコードは信頼できないユーザーに対して正規表現となる文字列を許可し正規表現検索を実行します。

function search_log($user, $pattern) {
  if (!strlen($pattern)) {
    return FALSE;
  }
  $regex = '(.*? +'. $user .'\[\d+\] +.*'. $pattern .'.*';
  $log =file_get_contents('/path/to/logfile');
  preg_match_all($regex, $log, $matches);
  return $matches;
}

$log = search_log($_SESSION['username'], $_POST['pattern']);
var_dump($log);

このコードは攻撃者に正規表現インジェクションを許します。

正規表現インジェクションに脆弱でないコード例(Whitelisting)

検索パターンにアルファベットのみを許可する。

function search_log($user, $pattern) {
  if (!strlen($pattern)) {
    return FALSE;
  }
  $regex = '#(.*? +'. $user .'\[\d+\] +.*'. $pattern .'.*#i';
  $log = file_get_contents('/path/to/logfile');
  preg_match_all($regex, $log, $matches);
  return $matches;
}
 
// $_POST['pattern'] is validated by JavaScript at client side and
// must not contain other than alphabet chars.
if (strlen($_POST['pattern']) !== strspn($_POST['pattern'], 'abcdefghijklmnopqrstuvwxyz')) {
  throw new Exception('Validation error: pattern may contain alphabet chars only.');
}
$log = search_log($_SESSION['username'], $_POST['pattern']);
var_dump($log);

 

正規表現インジェクションに脆弱でないコード例(Detect Meta Character)

preg_quote()はPCRE正規表現のメタ文字をエスケープする。エスケープされた場合、元の文字列より長くなることを利用してバリデーションを行う。

function search_log($user, $pattern) {
  if (!strlen($pattern)) {
    return FALSE;
  }
  $regex = '#(.*? +'. $user .'\[\d+\] +.*'. $pattern .'.*#i';
  $log = file_get_contents('/path/to/logfile');
  preg_match_all($regex, $log, $matches);
  return $matches;
}
 
// $_POST['pattern'] is validated by JavaScript at client side and
// must not contain other than alphabet chars.
if (strlen($_POST['pattern']) !== strlen(preg_quote($_POST['pattern'], '#')) {
  throw new Exception('Validation error: pattern may contain regex meta chars.');
}
$log = search_log($_SESSION['username'], $_POST['pattern']);
var_dump($log);

メタ文字も検索文字として許可したい場合はpreg_quote関数でエスケープした文字列を利用しても構いません。mbstringの正規表現関数を利用している場合、pregとメタ文字に差異があるのでpreg_quote関数だけでは不完全である事に注意してください。(DBMSの識別子もですが、エスケープ関数がないのは本当に不便です)

 

ヌル文字インジェクションとの関連

正規表現関数がバイナリセーフでない場合、ヌル文字をインジェクションする事により、ヌル文字以降の文字列を無視させることができます。ヌル文字インジェクションが可能な場合は攻撃者より大きな自由を与えることになります。

PHPのpreg関数やmbstringの正規表現関数はバイナリセーフなのでヌル文字インジェクションを利用した攻撃は行なえません。ヌル文字も通常の文字として扱われます。ereg関数は非バイナリセーフです。

 

類似の脆弱性 – LIKEクエリインジェクション

SQLのLIKEクエリは正規表現のような複雑なパターンマッチをサポートしていませんが、同類のインジェクション攻撃に脆弱に成り得ます。

プリペアードクエリ/プレイスホルダはLIKEクエリインジェクションには役立ちません。正規表現インジェクションと同じ対策が利用可能です。

 

まとめ

インジェクション対策、基礎の基礎を理解していれば、正規表現インジェクションを知らなくても脆弱なコードに自分で気づくと思います。これは正規表現インジェクションに限らず、全てのインジェクション脆弱性についても同じです。

正規表現のメタ文字を利用した検索を一般に公開することはリスクが高いです。比較的簡単にDoS攻撃が行えますし、正規表現ライブラリの脆弱性(バッファーオーバーフローなど)を攻撃されるリスクが発生します。

 

投稿者: yohgaki