例 2: ソースからシンクへの変更

例 2 は、例 1 のコードを変更したものです。検証ルーチンの getVulnerableSource と、writeToVulnerableSink で呼び出されるエンコード・ルーチンを追加することで、例 1 を改善しています。

import java.io.*;

public class TestCase_IOT_Instance_Val_Encode {
  public static void main(String[] args) {
    try {
      TestCase_IOT_Instance_Val_Encode testCase = new 
        TestCase_IOT_Instance_Val_Encode();
      String file = args[0];
      String source = testCase.getVulnerableSource(file);
      source = testCase.validate(source);
      String encodedStr = testCase.encode(source);
      testCase.writeToVulnerableSink(file, encodedStr);
    } catch (Exception e) {
    }
  }

  public String getVulnerableSource(String file) throws Exception {
    FileInputStream fis = new FileInputStream(file);
    byte[] buf = new byte[100];
    fis.read(buf);
    fis.close();

    String ret = new String(buf);
    return ret;
  }

  public void writeToVulnerableSink(String file, String str) 
      throws FileNotFoundException {
    FileOutputStream fos = new FileOutputStream(file);
    PrintWriter writer = new PrintWriter(fos);
    writer.write(str);
  }

  private String validate(String source) throws Exception {
    if (source.length() > 100) {
      throw new Exception("Length too long: " + source.length());
    }
		return source;
  }

  private String encode(String source) {
    return source.trim();
  }
}

最初のスキャンで、例 1 と同様のスタック・トレースが生成されます。

ナレッジベース・データベース を拡張して検証ルーチンとエンコード・ルーチンを組み込むことにより、検出結果内の不要な情報を削除し、すべての呼び出しグラフについて検証ルーチンとエンコード・ルーチンが呼び出されているかどうかを確認することができます。例えば、例 1 で java.io.FileInputStream.read(byte[]):int に対するすべての呼び出しからのデータを指定した場合、スキャンは read からのすべての呼び出しを除去します。これは、read もこの検証ルーチンを呼び出すためです。また、カスタム検証メソッドを呼び出さなかった read からの呼び出しの状況は、「確定」セキュリティー検出結果に引き上げられます。これは、コード内の既知の検証メソッドを呼び出さなかった場合、悪質な攻撃を受ける可能性があるためです。

検証ルーチンは、FileInputStreamread メソッドの他のバリエーションを検証することもできます。これらは追加のソースとして指定することもできます。加えて、特定のシンク (つまり、特定のプロパティーを持つシンク) のみをこのメソッドで検証されるように指定することもできます。例えば、このルーチンを、Technology.IO プロパティーを持つシンク (このサンプル・データを取り込むのに使用される PrintWriter.write シンクなど) に制限できます。