AntiSamy による HTML/CSS のサニタイズ

ユーザーからの入力として HTML を受け取る場合には、通常その HTML に意図せぬ JavaScript などが埋め込まれないようにデータをチェックします。 これをデータのサニタイズ (sanitize) といいます。

OWASP AntiSamy を利用すると HTML/CSS をサニタイズできます。

ポリシーファイル

OWASP AntiSamy ではポリシーファイルを変更することによって、サニタイズをどのように実施するか定義できます。

事前に定義されたポリシーファイルは次の通りです。

ポリシーファイル名概要
antisamy-slashdot.xml非常に制限の厳しいポリシー。CSS は許可せず、 <b>, <u>, <i>, <a>, <blockquote> のみが許可される。
antisamy-ebay.xmlantisamy-slashdot.xml よりは制限の緩いポリシー。eBay 的な動作。
antisamy-myspace.xmlJavaScript の含まれていない HTML を許可する。
antisamy-anythinggoes.xml何でも OK

上記ポリシーファイルをテンプレートとして、値を設定します。
利用できるオプションは次の通りです。

ディレクティブ 既定値 意味
useXHTML boolean false XHTML フォーマットでサニタイズされたデータを出力する
omitXMLDeclaration boolean true useXHTML が true のとき AntiSamy は XML ヘッダーを付けます。 この機能を有効にすると、この動作を行いません。(つまりヘッダーを付けません)
formatOutput boolean true これを有効にすると、単純なルールによってインデントなどのフォーマットを行います。
maxInputSize int 100K 検証前のサイズでの入力値の最大値を指定できます。
embedStyleSheets boolean false CSS の埋め込みを許可するかどうか
maxStyleSheetImports int 1 1 つの入力データから最大いくつの外部 CSS の取り込みを行うか指定する
connectionTimeout int 1K embedStyleSheets が有効なときの、外部 CSS リソース取り込みのタイムアウト値
preserveComments boolean false HTML コメントを残すかどうか
nofollowAnchors boolean false これを有効にすると、全てのアンカータグに rel="nofollow" 属性が付きます。
validateParamAsEmbed boolean false これを有効にすると、AntiSamy は embed タグの属性と、embed タグ内の param タグと同じように扱います。 ビデオなどを提供する人には必要です。
preserveSpace boolean false これを有効化すると、空白文字をそのまま保持します

使用方法

  1. *.jar ファイルとポリシーファイルダウンロードする。

    AntiSamy を利用するだけなら、*.jar ファイルとポリシーファイル antisamy-*-バージョン.xml をダウンロードすれば OK です。
    » AntiSamy のダウンロード

  2. ポリシーファイルを必要に応じて変更します。
  3. コード例は以下を参照してください。

次の例では、テキストファイルから HTML データを読み込んで、それをサニタイズして出力しています。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

import org.owasp.validator.html.AntiSamy;
import org.owasp.validator.html.CleanResults;
import org.owasp.validator.html.Policy;
import org.owasp.validator.html.PolicyException;
import org.owasp.validator.html.ScanException;

public class TestApp1 {

  public static void main(String[] args
    throws PolicyException, ScanException, IOException {

    String htmlText = readFileAsString(
        "C:/owasp/test/test1.html");
    Policy policy = Policy.getInstance(
        "C:/owasp/antisamy/antisamy-ebay-1.4.1.xml");
    AntiSamy as = new AntiSamy();
    CleanResults cr = as.scan(htmlText, policy);
    System.out.println(cr.getCleanHTML());
  }
  
  private static String readFileAsString(String filePath)
    throws java.io.IOException{
    StringBuffer fileData = new StringBuffer(1000);
    BufferedReader reader = new BufferedReader(
        new FileReader(filePath));
    char[] buf = new char[1024];
    int numRead=0;
    while((numRead=reader.read(buf)) != -1){
      String readData = String.valueOf(buf, 0, numRead);
      fileData.append(readData);
      buf = new char[1024];
    }
    reader.close();
    return fileData.toString();
  }

}

ディレクティブの設定とサニタイズ後の値

antisamy-ebay-1.4.1.xml ポリシーファイルをベースとしてディレクティブを変更して、 どのように出力が変わるかみてみましょう。

変換前の HTML スニペットは次の通りです。

<script type="text/javascript">
alert('Hello, JavaScript');
</script>
<style type="text/css">
h1 { color: blue; }
</style>

<h1>Hello, AntiSamy!</h1>

<!-- Comment -->
<p style="color:red;">これはテストです。</p>

<p>
<a href="http://www.google.com">Google</a>
</p>

また、antisamy-ebay-1.4.1.xml の元のディレクティブは次のように設定されています。

<directives>
	<directive name="omitXmlDeclaration" value="true"/>
	<directive name="omitDoctypeDeclaration" value="true"/>
	<directive name="maxInputSize" value="20000"/>
	<directive name="useXHTML" value="true"/>
	<directive name="formatOutput" value="true"/>
	<directive name="embedStyleSheets" value="false"/>
</directives>

既定の設定による変換

antisamy-ebay-1.4.1.xml ポリシーファイルそのままで上記 HTML を変換すると、 次のようになります。


<style type="text/css"><![CDATA[h1 {
	color: blue;
}
]]></style>


<h1>Hello, AntiSamy!</h1>



<p style="color: red;">これはテストです。</p>


<p>
<a href="http://www.google.com">Google</a>
</p>

確かに JavaScript コード、コメントが削除されていることがわかります。

ディレクティブの変更

useXHTML を false にして、さらに nofollowAnchors を追加し、値を true にしてみましょう。

<directives>
	<directive name="omitXmlDeclaration" value="true"/>
	<directive name="omitDoctypeDeclaration" value="true"/>
	<directive name="maxInputSize" value="20000"/>
	<directive name="useXHTML" value="false"/>
	<directive name="formatOutput" value="true"/>
	<directive name="embedStyleSheets" value="false"/>
	<directive name="nofollowAnchors" value="true"/>
</directives>

変換後の結果は次の通りです。



<style type="text/css">h1 {
	color: blue;
}
</style>


<h1>Hello, AntiSamy!</h1>



<p style="color: red;">これはテストです。</p>


<p>
<a href="http://www.google.com" rel="nofollow">Google</a>
</p>

デフォルトの変換結果と比べて、赤くマークした部分が正しく変わっていることがわかります。

尚、pre タグなどを許可して改行文字を認める場合以外は、次のようにして余計な改行も削除すると良いです。

String cleanHTML = cr.getCleanHTML();
cleanHTML = cleanHTML.replaceAll("(\\r|\\n)""");

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Java 入門