IDS11-J. 文字列の変更はすべて検査の前に行う
攻撃者に検査を回避されないよう、文字列は検査後に変更しないことが重要である。例として、クロスサイトスクリプティング (XSS) やその他の脆弱性を避けるため、HTML 入力から <script>
タグを削除するプログラムを考える。感嘆符 (!) が検査後に入力値から削除される場合、攻撃者が「<scr!ipt>
」のような文字列を渡すと、入力値検査で <script>
タグは検出されないが、続く感嘆符の削除により入力値に <script>
タグが作られてしまう。
多くの異なる文字カテゴリを除外したい場合がある。たとえば、「Unicode 標準」 [Unicode 2012] では以下のような文字カテゴリが定義されており、それぞれ対応する正規表現で検出できる。
略称 | 正式名称 | 説明 |
---|---|---|
Cc | Control | C0 または C1 制御コード |
Cf | Format | 書式制御文字 |
Cs | Surrogate | サロゲートコードポイント |
Co | Private_Use | 私用文字 |
Cn | Unassigned | 予約済みの未割り当てコードポイントと非文字 |
独自に定義した文字集合を削除または置換する場合もある。いずれにしても、あらゆる文字列操作は検査前に行わなければならない。
違反コード (非文字コードポイント)
Unicode 5.2 より前のいくつかのバージョンでは、適合性条件 C7 において、非文字コードポイント (noncharacter code points) を削除することを認めている。たとえば、Unicode 5.1 の適合性条件 C7 には以下のように記述されている [Unicode 2007]。
C7. 有効 (valid) な文字列の解釈を変えないようにするためには、プロセスは正準等価な文字列への変換および非文字コードポイントの削除以外の変換を行ってはならない。
Unicode Technical Report #36「Unicode Security Considerations」のセクション3.5「コードポイントの削除」には以下のように記述されている [Davis 2008b]。
C7 の古いバージョンで認められていたような、暗黙のうちに文字を削除する動作 (置き換えでなく) はセキュリティ上の問題となりうる。たとえば、次のような場合が考えられる。ゲートウェイが「delete」という文字列が含まれているデータを拒否するように設定されているとする。データのなかに「deXlete」 (ここで「X」は非文字を表しているとする) という文字列が含まれていても、ゲートウェイのチェックにはかからずに通過する。しかし、この後の処理の中で「X」が暗黙のうちに削除されてしまうと、その結果は「delete」という文字列になり、ゲートウェイによるチェックを回避されてしまったことになる。
以下の違反コードの filterString()
メソッドは、入力文字列を正規化し、入力値に <script>
タグが含まれていないことを確認し、その後に入力文字列から非文字コードポイントを削除している。入力値検査が非文字コードポイントの削除より前に行われているため、攻撃者は <script>
タグに非文字コードポイントを含めることで入力値検査を回避できる。
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TagFilter {
public static String filterString(String str) {
String s = Normalizer.normalize(str, Form.NFKC);
// 入力値検査
Pattern pattern = Pattern.compile("<script>");
Matcher matcher = pattern.matcher(s);
if (matcher.find()) {
throw new IllegalArgumentException("Invalid input");
}
// 非文字コードポイントの削除
s = s.replaceAll("[\\p{Cn}]", "");
return s;
}
public static void main(String[] args) {
// 「\uFDEF」は非文字コードポイント
String maliciousInput = "<scr" + "\uFDEF" + "ipt>";
String sb = filterString(maliciousInput);
// sb = "<script>"
}
}
適合コード (非文字コードポイント)
以下の適合コードでは、認識できない文字や表示できない文字を Unicode 文字 \uFFFD
に置き換えている。\uFFFD
は、アプリケーションが認識できない文字と入れ換えるために予約された文字である。また、この入れ換えは <script>
の存在チェック等あらゆる無害化処理の前に行われる。こうすることで、偽装した入力による検査回避を防ぐことができる。
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TagFilter {
public static String filterString(String str) {
String s = Normalizer.normalize(str, Form.NFKC);
// 非文字コードポイントをすべて Unicode の U+FFFD に置き換える
s = s.replaceAll("[\\p{Cn}]", "\uFFFD");
// 入力値検査
Pattern pattern = Pattern.compile("<script>");
Matcher matcher = pattern.matcher(s);
if (matcher.find()) {
throw new IllegalArgumentException("Invalid input");
}
return s;
}
public static void main(String[] args) {
// 「\uFDEF」は非文字コードポイント
String maliciousInput = "<scr" + "\uFDEF" + "ipt>";
String s = filterString(maliciousInput);
// s = <scr?ipt>
}
Unicode Technical Report #36「Unicode Security Considerations」[Davis 2008b] によれば、「U+FFFD
はまさにこのような用途のために予約された文字であり、通常はなんの問題も起こさない。プログラミング言語の文法上なんらかの意味を持つ文字であるとか、構造化データにおいて特殊な意味を持つ文字であるといったことがなく、単純に文字列のパーズ処理を失敗させるだけだからである。ただし、出力先の文字セットが Unicode でない場合、これに相当する文字は存在しないかもしれない」
リスク評価
入力文字列から文字を削除したり変更したりする前に文字列の検証を行っている場合、細工された文字列によって検証を回避される可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
IDS11-J |
高 |
中 |
中 |
P12 |
L1 |
自動検出
ツール | バージョン | チェッカー | 説明 |
---|---|---|---|
The Checker Framework |
2.1.3 |
Tainting Checker | Trust and security errors (see Chapter 8) |
Parasoft Jtest |
2024.1 |
CERT.IDS11.VPPD | Validate all dangerous data |
関連ガイドライン
CWE-182, Collapse of Data into Unsafe Value |
参考文献
[API 2006] |
|
[Davis 2008b] |
Section 3.5, "Deletion of Noncharacters" |
[Seacord 2015] | |
[Unicode 2007] |
|
[Unicode 2011] |
|
[Weber 2009] |
"Handling the Unexpected: Character-deletion" (slides 72–74) |
翻訳元
これは以下のページを翻訳したものです。
IDS11-J. Perform any string modifications before validation (revision 128)