第20回北海道情報セキュリティ勉強会レポート – 文字コードの脆弱性はこの4年間でどの程度対策されたか?〜前半【徳丸先生】#secpolo

2014.12.01

こんにちは。夜のSQLインジェクション、せーのです。今回は少し趣向を変えてセキュリティ関連の記事を書きたいと思います。 先日の11/29(土)、北海道大学にて行われた「第20回北海道情報セキュリティ勉強会」に参加してきましたのでレポート致します。

secpolo

今回のゲストはセキュリティ関連では「徳丸先生」の呼び名でおなじみ、HASHコンサルティング株式会社の徳丸浩さん。セキュリティにまつわる所謂「徳丸本」の著者でもあります。 なかなか徳丸先生の講義が札幌で聞ける事はないので、とても楽しみにしておりました!

前提

「文字コードによる脆弱性」とは

今回扱う「文字コードによる脆弱性」とはあくまで正しいセキュリティ対策を施しているのにも関わらず文字コードの違いにより脆弱性が出てしまうケースについて扱います。 前提として必要な脆弱性についてさらっとおさらいします。

CSRF

クロスサイト・リクエスト・フォージェリ、と言います。通称「シーサーフ」。FormタグのAction属性はドメインなど関係なくどのURLでも指定することが出来ます。そこを利用して関係ないサイトに見せかけといて裏にJavascript等でターゲットサイトに対してForm投稿をする、みたいな感じのものです。ゆうちゃんの最初の犯行がこれに当たります。

secpolo_yu

対策はトークン一択、となります。別にワンタイムトークンである必要はありません。セッション毎のトークンで充分です。

$s = file_get_contents('/dev/urandom', false, NULL, 0, 24);

みたいな感じでトークンをつくってhiddenタグに入れておきましょう。

XSS

クロスサイトスクリプティング、と言います。定義としては

  • 利用者(被害者)のブラウザ上で
  • 攻撃対象のドメイン(オリジン)で
  • 攻撃者が自由にJavaScriptを実行できる

という3つが揃うと脆弱性となります。2つまではよくあったりしますが3つ揃わないように気をつけましょう。 対策としては「htmlspecialchars()を使う」「JavaScriptの動的生成を避ける」などがあります。

SQLインジェクション

攻撃対象のサーバーのDBにつないで攻撃者が自由にSQL呼び出しができる、という脆弱性です。有名ですね。最近では「ブラインドSQLインジェクション」なんて攻撃方法もあります。 対策としては「basename(),sprintf()等適切な関数を使うこと」「新しいPHPを使う」等があります。

「文字集合」と「文字エンコーディング」

さて、本題に入る前にそもそも文字コードとはなにか、用語として覚えておきましょう。 文字集合 - 文字を集めたもので集めた文字に符号(番号)をつけたものを「符号化文字集合(文字集合)」といいます。代表的なものはASCII, Unicode等があります。 文字エンコーディング - 文字集合を使ってコンピュータ処理するために、文字をバイト列として表現する方式を言います。代表的なものはShift_JIS,EUC-JP,UTF-8等があります。 文字コードの脆弱性はこのうち「文字エンコーディング」の変換によって「文字集合」が変更され、バックスラッシュと円記号が同じ円記号になるなど多対1の変換になったり対応するものが無かったりする時に主におきます。それぞれの文字集合の相関関係はこのようになっています。

secpolo20-1

secpolo20-2

特に5CとA5は文字集合によって円記号の位置が違うため、変換時の事故が多発するポイントです。 secpolo20-3

文字エンコーディング毎の代表的な問題

Shift_JIS - 5C問題

Shift_JISは1バイトor2バイトで1文字を表現しますが、1バイト文字と2バイト文字の後続バイト(後ろのほうのバイト)の領域が重なっているためこのような問題がおきます。例えば「」という文字は[95][5C]の2バイトで表されますが2バイト目が[¥]にマッチします。

EUC-JP - 螢問題

EUC-JPの場合は1バイト文字と2バイト文字が重なることはないのですが、文字境界(ある文字の2バイト目と次の文字の1バイト目)をまたがってマッチする場合があります。例えばリは[A5][EA]でルは[A5][EB]なのですが、[EA][A5]で「」という文字とマッチします。 ※これを螢問題と名づけたのは徳丸先生です。一般的な用語ではないそうです。

UTF-8 - 非最短形式問題

UTF-8は1バイトー4バイトで1文字を表現しますが、1バイト文字、マルチバイトの先行バイト、後続バイトは全く重なりません。なので上のような問題は起こらないのですが、Nバイトで表現できる文字はN+1バイト以上の形式でも計算上表現できてしまいます。これを「非最短形式」と言います。例えばスラッシュは1バイトで[0x2F]と表しますが[0xC0][0xAF],[0xE0][0x80][0xAF],[0xF0][0x80][0x80][0xAF]もスラッシュとして表現出来ます。現在は非最短形式は禁止されています。PHP5.3.1以前のhtmlspecialchars()関数で、JREはJava SE6 Update10までUTF-8の非最短形式を許容していました。

UTF-7について

最後に余り聞きませんが「UTF-7」という方式についておさらいしておきます。これは7ビットでUnicodeを符号化する方式です。元々電子メールでの利用を想定していましたが現在規格上は廃止されているものになります。英数字と一部の記号はASCIIそのまま、それ以外の文字はUTF-16で符号化→Base64エンコーディング→先頭に+, 末尾に-を追加します。 UTF-7で

<script>alert(1)</script>
+ADw-script+AD4-alert(1)+ADw-/script+AD4-

となります。

Q&A

Q:UTF-8のチェックについて。昔は6バイトだったが今は4バイトまでなので「4バイト以内」「コードポイントの範囲に収まっているか」をチェックする、という対策で良いか?

A:mb_check_encoding()関数等なるべく「ありもの」を使ってチェックした方が良い。

前半まとめ

以上、まずは前半の様子をレポートいたしました。実はまだ本題である文字コードの脆弱性の話題に入っていないにも関わらずこの内容の濃さ!ここで抑えておかなくてはいけないポイントは「文字集合ごとに包含関係があるため変換時に上手く1対1で変換できない場合がある」「主要な文字エンコーディングにはそれぞれ意図しないマッチングをしてしまう問題がある」という2点です。ここにPHPやJavaのエスケープ処理やDB独自の変換方式等が絡んで、全く意図していなかった脆弱性を生み出すことになるわけです。後半はこの絡み方について実例を上げつつポイントを整理、対策を考えていきます。後半のレポートもお楽しみに!