Engineering Note

プログラミングなどの技術的なメモ

入力値の検証(Java セキュアコーディング 入門)

coding-icon

本記事は、Javaのセキュアコーディングについてのメモとして書かれています。

主にアスキー・メディアワークスから出版されている「Javaセキュアコーディングスタンダード CERT/ Oracle版」を中心に学習をしていきます。

今回は、入力値の検証について学んでいきます。

  

 

正規化(標準化)とは

正規化(normalization)とは、データなどを統一的なルールに基づいて扱えるように変形させることを言います。

Webの世界では、ユーザから入力されたデータなどを扱うことが多く、これらの中にはそのまま扱うと危険なデータが混入している可能性があります。

そのため、それらのデータを統一的に扱える形式へと変形させ、その上でデータを無害化しなければなりません。

一般的に、テキスト正規化処理にはUnicode正規化(Unicode Normalization)を行います。

Unicode正規化には以下の4種類の形式があります。

 

  • NFD(Normalization Form Canonical Decomposition):正規化形式D
  • NFC(Normalization Form Canonical Composition):正規化形式C
  • NFKD(Normalization Form Compatibility(K) Decomposition):正規化形式KD
  • NFKC(Normalization Form Compatibility(K) Composition):正規化形式KC

 

上記の中でKCが最も適した正規化の形式とされているため、本記事ではこちらを使用します。

 

入力値の検証方法

正規化には、java.textパッケージのNormalizerクラスを利用します。

しかし、入力値の検証と正規化の手順を間違えてしまうと意味が無くなってしまいます。

以下は、正規化を入力値の検証後に行ってしまった例になります。

 

 //CheckBeforeNorm.java 
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CheckBeforeNorm {
    public static void checkString(String str) throws IllegalStateException {
        Pattern pat = Pattern.compile("[<>]");
        Matcher mat = pat.matcher(str);
        if(mat.find()){
            throw new IllegalStateException();
        }
    }
    public static void main(String[] args) {
        String str = "\uFE64" + "script" + "\uFE65";
        checkString(str);
        str = Normalizer.normalize(str, Form.NFKC);
    }
}

 

上記の\uFE64及び\uFE65は、Unicode表現でそれぞれを表しており、以下のコードで自環境での符号化方式を確認することができます。

 

 System.out.println(System.getProperty("file.encoding"));

 

自環境ではutf-8となっており、正規表現上での<及び>はそれぞれ、0X3C及び0X3Eで符号化されます。

しかし、上記の環境依存(機種依存)による小なり(less than)及び大なり(greater than)はそれぞれ0XEFB9A4及び0XEFB9A5エンコードされるため、正規表現のパターンマッチをすり抜け、IllegalStateExceptionの例外がスローされることなく実行されてしまいます。

そのため、以下のようにチェックをする前に正規化により、統一的な形式に整える必要があります。

 

 // CheckAfterNorm.java
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CheckAfterNorm {
    public static void checkString(String str) throws IllegalStateException {
        Pattern pat = Pattern.compile("[<>]");
        Matcher mat = pat.matcher(str);
        if(mat.find()){
            throw new IllegalStateException();
        }
    }
    public static void main(String[] args) {
        String str = "\uFE64" + "script" + "\uFE65";
        str = Normalizer.normalize(str, Form.NFKC);
        checkString(str);
    }
}

 

上記の手順で正規化及びパターンマッチを行うことで、データの検証(validation)を行い、必要に応じてデータの無害化(sanitization)を行う必要があります。

 

最後に

今回は入力値の検証について学びました。

基本的に入力値の検証や無害化の箇所に問題があると、クロスサイトスクリプティングXSS)攻撃の脆弱性が生まれてしまうため、正しい手順でデータを検証し、必要に応じて無害化をしなければいけません。

 

参考書籍

Javaセキュアコーディングスタンダード CERT/ Oracle版