コンテクストを知る、はデータを安全に扱う為に必須の基礎知識です。
よくある致命的な脆弱性を作る考え方は
- コンテクストなんてどうでもよい。このAPIを使えば良い。
です。セキュリティ対策ではコンテクストが何であるのか?を正しく理解することが重要です。ここではコンテクストについて紹介します。
コンテクストが大切な理由
コンテクストが大切な理由は
- コンテクストによってデータの意味/形式が変わる
ことにあります。
攻撃者は「コンテクストによってデータの意味/形式が変わる」ことを利用し「データを命令に変えて」インジェクション攻撃をしてきます。
攻撃方法を知れば対策が理解る、と言われます。個別の攻撃方法を知るのも必要なことですが、その前に「何故インジェクション攻撃が可能になるのか?」を正しく理解すると全ての命令実行型のインジェクション攻撃に対し、開発者自身で対策できるようになります。
つまり原理と基本を知れば、どのようなインジェクション攻撃にも対策できるようになります。
コンテクストの意味はコンテクストスイッチのコンテクストではなく、
1(文章の)前後関係,文脈,脈絡,コンテキスト.
in a different context 異なった文脈の[で(は)].
2〔ある事柄の〕状況,環境 〔of〕.
in the context of politics 政治という面において(は).
の意味で使います。コンテキストと書く事が一般的なようですが、普通に英語発音を聞くと「コンテクスト」の方が近く、私は「コンテクスト」と言っているのでこちらを使います。(トラヒックとトラフィックみたいなモノ?)
単純な固定長と可変長データ
まずは単純な固定長と可変長データ構造について整理します。
固定長データ構造のコンテクスト
場所の違いでコンテクストが変る、のが固定長データ構造の特徴です。C言語のstructなどです。
以下はPHP変数の内部データ構造(C言語)です。
struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; };
_zval_struct内のデータの意味は「メモリの中の位置(場所)」によって決まります。
可変長データ構造のコンテクスト
特殊文字でコンテクストが変わる、のが可変長データ構造の特徴です。CSVファイルなどです。
以下はCSVデータのフォーマット例です。
# シンプルな形式 aaa,bbb,ccc CRLF zzz,yyy,xxx CRLF # ダブルクオートを使う形式(とシンプルな形式) "aaa","b""bb","ccc" "aaa","b CRLF bb","ccc" CRLF zzz,yyy,xxx
CSVデータの中では意味を持つ , (カンマ)と “(ダブルクオート)によりデータの意味が変わります。
固定長/可変長データの守り方
固定長データ構造の場合、”場所/位置”に意味が変わります。従って、”場所/位置”が正しいことを保証すればデータが不正に解釈されません。以下の3つのチェック項目でデータをバリデーションすればデータが無害であることを保証できます。
- オフセット(場所/位置)が正しい
- データの長さが正しい
- データの値/形式が妥当
可変長データデータ構造の場合、特別な意味を持つ”特殊文字/データ”で意味が変わります。従って、以下の3つの方法でデータを無害します。
- 特殊な意味を持つ文字/データをエスケープし、意味を持たなくさせる
- 特殊な意味を持つ文字/データが意味を持たなくなるAPIを利用する
- 特殊な意味を持つ文字/データを持たないことをバリデーションする
固定長/可変長ともに3つの方法で完全に無害なデータであること保証できます。無害であってもデータの妥当性保証はない点に注意します。データの妥当性はバリデーションで保証することに留意してください。
Webアプリはテキストインターフェースで出来ている
WebアプリはHTTP、HTML、CSS、JavaScript、JSON、SQL、LDAP、SMTP、XML、XPath、正規表現、OSコマンドなどのテキストインターフェースで出来ています。これら全てが可変長のデータ構造を持っています。
Webアプリでも固定長のデータ構造を扱うこともありますが、ほとんどが可変長データ構造のデータです。可変長データを間違いなく扱うことが重要になります。
可変長データ構造のデータには必ず意味を持つ特殊文字が定義されています。
UTF-8も可変長データ構造のデータ
防御しなければならない可変長データの中で最も多く防御が忘れられているのが文字エンコーディングです。UTF-8も可変長データ構造を持つので、HTMLやSQLなどと同様に攻撃によって意味が変わらないよう防御する必要があります。UTF-8以外のほとんどの文字エンコーディングも可変長データ構造を持つデータです。
マルチバイト文字もHTMLやSQLなどと同様に意味を持つ「特殊文字」や「特殊データ」で後に続くデータの意味が変わります。
今時のWebアプリはUTF-8文字エンコーディングのみを使って構築されます。UTF-8文字エンコーディングの構造だけ簡単に紹介します。
UTF-8の場合、オクテット(バイト)のMSBが1の場合で連続する1の数と同じオクテット(バイト)分がUTF-8文字を構成する一文字、という構造を持っています。構造的には固定長データ構造と可変長データ構造を組み合わせたような構造になっています。
UTF-8文字の最初のオクテットと文字数の関係(UTF-8文字の構造)
- 0xxxxxxx – 1バイトで表せるASCIIコードの文字
- 10xxxxxx – 2バイトで表せるUnicode文字
- 110xxxxx – 3バイトで表せるUnicode文字(日本語文字のほとんど)
- 1110xxxx – 4バイトで表せるUnicode文字(一部の日本語文字や絵文字)
HTMLやSQLとは少しだけ仕組みが異なりますが、基本構造は同じです。特別な意味を持つ文字(データ)により後続のデータの意味が変わります。
文字エンコーディングを使ったインジェクション攻撃を知らない方にはピンとこないかも知れません。このUTF-8文字の構造を使い特定のコンテクストに対して「意味のある文字を消す」「意味のある文字を残す」といった無害化(サニタイズ)を利用して、HTMLやSQLといった別のコンテクストに対してインジェクション攻撃が可能になります。(今のブラウザ、DBは不正な文字データを致命的なエラーとします。Webアプリから見るとこれらの防御機能はフェイルセーフ機能なので頼ってはなりません。)
壊れた文字エンコーディングを無害化(サニタイズ)するのは、基本的にはアンチプラクティスです。壊れた文字エンコーディングデータがそもそもアプリケーション内に入らないようバリデーションしなければなりません。「臭い物には元から蓋」でないと問題はなくなりません。
文字エンコーディングが壊れていると困る理由は以下のブログで詳しく説明しています。
攻撃者は”意味を持つ文字”を使って攻撃する
Webアプリで大きな脅威はインジェクション攻撃です。HTTP、HTML、CSS、JavaScript、JSON、SQL、LDAP、SMTP、XML、XPath、正規表現、OSコマンド、ここに挙げていないテキストインターフェース含め、これら全てにインジェクション攻撃が可能です。
攻撃者はそれぞれのコンテクストで”意味を持つ文字”を使って攻撃してきます。その大きな目的は”本来はデータである文字列”を別の意味に変えることにあります。データであったはずの文字列を命令に変えて攻撃者の望む不正な命令を実行させる、これがWebアプリケーションにおけるインジェクション攻撃の主な目的です。(他の目的を持つ攻撃、例えばDoSや強制ブラウズなど、もありますが、話を簡単にするためここでは省略)
防御する側の開発者は”いかにしてデータがデータのままで意味が変わらない事を保証するか?”を考えれば、攻撃を防ぐことができます。
コンテクストは1つではない!場合がある
セキュリティ専門家でもよく見落しているのが
- コンテクストが複数あるケース
です。この見落しは致命的なインジェクション脆弱性を生むことがあり非常に危険です。
例えば、SQL文のパラメーターはXML/XPathクエリとして解釈されたり、JSONデータ、配列、正規表現、コマンド、ストアードプロシージャー引数、関数引数などとして解釈される場合があります。この場合、SQL文のパラメーターとしてだけ意味を持たないよう対策しても、その先のコンテクストでも意味を持たないことを保証しないと安全な出力になりません。
複数のコンテクストがある物には、HTML(JavaScript、CSS、URIなど)、SQL(識別子、XML、XPath、JSONなど)、コマンド(実行するコマンドのコンテクスト)などがありますが、これらに限りません。
SQLとコマンドについては、次のブログで説明しています。コンテクスト無視した「このAPIさえ使えば良い」はダメなアンチプラクティスです。
まとめ
データはコンテクスト(文脈)を正しく理解した上で出力しないと安全(無害)な出力になりません。
特に複数のコンテクストを考慮しないと無害にならないケースは要注意です。コード検査でよく見つかる間違いは複数コンテクストがあるケースです。
最も多いSQLインジェクション脆弱性は
- プリペアードクエリ/プレイスホルダだけ使っていれば大丈夫!
- ORMだけ使っていれば大丈夫!
と勘違いしているケースです。これらのAPIには制限ある場合が多く、何も考えないで使ってしまうと脆弱性を作ってしまいます。
最後に
- どんなデータであってもAPIに任せればOK、脆弱性があったならそれはAPIの責任!
と考えるのは危険な考え方で、アンチプラクティスです。出鱈目なデータをAPIに送ってエラーになるのも、そのまま出力してしまうのも、サニタイズしてしまうのもセキュリティ問題の原因になるからです。
セキュアコーディングの基本(全ての入力をバリデーション&全ての出力を無害化)に忠実に次のSAML脆弱性問題を理解するとアンチプラクティスである理由が理解ると思います。
出鱈目なデータを出力してしまう、それ自体が”アプリの脆弱性”です。