Serializable
なクラスにおいて、そのオブジェクトがデシリアライズされるときに呼び出される Serializable.readObject()
メソッドはオーバーロード可能である。readObject()
メソッドと readResolve()
メソッドでは、受け取ったシリアライズデータは細工されているものと想定して処理を行うべきである。デシリアライズされるオブジェクトが信頼できるクラスのもののみであることを、あらかじめ用意したホワイトリストで保証していないかぎり、潜在的に危険な操作を行うことは避けるべきである。ホワイトリストによるチェックなしにデシリアライズが行われている場合、潜在的に危険な操作を行うことはこのルールに違反する。
これは「SER12-J. 信頼できないクラスの復元はしない」と対をなすルールである。SER12-J では、危険な操作を行うクラスがないことを、デシリアライズを行う前に確認することを要請している。SER13-J では、デシリアライズされるすべてのクラスに対して、デシリアライズされる際に危険な操作を行わないことを要請している。SER12-J と SER13-J どちらも、デシリアライズに関する脆弱性を防ぐための対策である。これらの脆弱性を作りこんでいないことを保証するには、
- (1) システム中のすべてのコードが SER12-J に適合している、あるいは
- (2) システム中のすべてのコードが SER13-J に適合している
SER13-J への適合性という観点では、ObjectInputStream
がホワイトリストを持っている場合、クラス C の readObject
メソッドや readResolve
メソッドが呼び出されるのはクラス C がホワイトリストに含まれている場合のみ、と仮定してよいものとする。言い換えると、クラス C はホワイトリストに自分が含まれているかどうかをチェックしなくてもよい。ホワイトリストがあることだけをチェックすればよいということだ。この仮定をおくことにより、ホワイトリストの冗長なチェックを省くことができ、また、ホワイトリストの様々な実装方法を許容することができる。
違反コード
次に示す違反コード例では、クラス OpenedFile
がデシリアライズされる際ファイルをオープンしている。多くの OS ではひとつのプロセスがオープンできるファイルの数には上限があり、しかも、この上限はそれほど大きな数字ではない(例えば 1024 まで、など)。したがって、OpenedFile
オブジェクトのリストをデシリアライズしていく状況では、デシリアライズされた OpenedFile
オブジェクトが適切にガベージコレクションされない限り、オープンできるファイル数の上限に達して問題が発生する可能性がある。
import java.io.*;
class OpenedFile implements Serializable {
String filename;
BufferedReader reader;
public OpenedFile(String _filename)
throws FileNotFoundException {
filename = _filename;
init();
}
private void init() throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
}
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeUTF(filename);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
filename = in.readUTF();
init();
}
}
適合コード
次に示す適合コードでは、デシリアライズ処理がホワイトリストで保護されていない場合、readObject()
メソッドが例外をスローする。これは、SER12-J の適合コードの手法と対になるものである。SER12-J の適合コードでは、ObjectInputStream
のサブクラスを作成し、デシリアライズ処理を行うコードをカスタマイズした。このサブクラスでは、resolveClass()
メソッドをオーバーライドし、デシリアライズされるオブジェクトたちの属するクラスがホワイトリストに指定されていることを、readObject()
メソッドが呼ばれる前にチェックするのであった。これに対して以下の適合コードでは、readObject()
メソッドの中で、ホワイトリストの存在をチェックしている。
import java.io.*;
import java.lang.reflect.*;
class OpenedFile implements Serializable {
String filename;
BufferedReader reader;
public OpenedFile(String _filename)
throws FileNotFoundException {
filename = _filename;
init();
}
private void init() throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
}
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeUTF(filename);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
boolean hasWhitelist = false;
try {
in.getClass().getDeclaredField("whitelist");
hasWhitelist = true;
} catch (ReflectiveOperationException e) {}
if (!hasWhitelist) {
throw new SecurityException("Deserialization without a whitelist is disallowed for class " +
this.getClass().getName() + ".");
}
filename = in.readUTF();
init();
}
}
適合コード
次に示す適合コードでは、潜在的に危険な操作をデシリアライズ処理の外に移動している。このクラスを使う場合には、デシリアライズ処理の後に明示的に init()
を呼び出す必要がある。
import java.io.*;
class OpenedFile implements Serializable {
String filename;
BufferedReader reader;
boolean isInitialized;
public OpenedFile(String _filename) {
filename = _filename;
isInitialized = false;
}
public void init() throws FileNotFoundException {
reader = new BufferedReader(new FileReader(filename));
isInitialized = true;
}
private void writeObject(ObjectOutputStream out)
throws IOException {
out.writeUTF(filename);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
filename = in.readUTF();
isInitialized = false;
}
}
リスク評価
このルールに違反したときの深刻度は「潜在的に危険な操作」がどのようなものかに依存する。危険性の低い操作しか行っていない場合、運用妨害(DoS)攻撃が可能になる程度で済むかもしれない。一方、もっと危険な状況として、例えば外部から受け取った入力を(直接あるいはリフレクションを通じて) Runtime.exec
の引数として使っている場合などは、遠隔からのコード実行が行われる可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
SER13-J |
高 |
高 | 高 | P9 | L2 |
自動検出
ツール
|
バージョン
|
チェッカー
|
説明
|
---|---|---|---|
このルールに違反しているかどうかを確認する攻撃コードを作成する際の参考になる |
関連ガイドライン
CWE-502, Deserialization of Untrusted Data |
参考文献
[API 2014] |
|
|
|
[CVE 2011] |
翻訳元
これは以下のページを翻訳したものです。
SER13-J. Treat data to be deserialized as potentially malicious by default (revision 30)