SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Javaのデシリアライズに潜むセキュリティ問題

Javaのデシリアライズに関する問題

Javaのデシリアライズに潜むセキュリティ問題(中編)

  • X ポスト
  • このエントリーをはてなブックマークに追加

デシリアライズにおける問題

 外部から受け取ったシリアライズデータを検証なしにデシリアライズすることは、以下のようなリスクを伴う。

1. 意図しないクラスのオブジェクトが生成される可能性がある

 クラスパス上に存在する任意のクラスのオブジェクトを、攻撃者が指定するままに生成してしまう可能性がある。クラスパス上には、アプリケーション独自のクラスやJava標準ライブラリの他、ミドルウエアやフレームワークなど多数のライブラリが存在することが多い。それらのクラスはシリアライズ可能なケースが多く、デシリアライズ処理を悪用した攻撃が行われる可能性も考えられる。前回解説したInvokerTransformerやLazyMap、AnnotationInvocationHandlerクラスの悪用などがその例だ。

 この問題を説明するための下記シンプルなサンプルコードを見てほしい。Class2はアプリケーションでデシリアライズすることを想定しているクラス、Class1はクラスパス上には存在するが、デシリアライズすることを想定していないクラスと仮定する。攻撃者がシリアライズデータとしてClass1をアプリケーションに渡した場合、デシリアライズ後のキャスト処理でエラーが発生するが、エラーになる前にClass1のreadObject()が実行され、OSコマンドが実行される。

サンプルコード2:意図しないクラスのオブジェクト生成
class Class1 implements Serializable {
  private String command = null;

  Class1() {
    command = "cmd /c mkdir class1";
  }

  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    try {
      // 外部コマンド実行
      Runtime.getRuntime().exec(command);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

class Class2 implements Serializable {
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
  }
}

public class Sample2 {
  public static void main(String[] args) throws ClassNotFoundException, IOException {
    ObjectInputStream ois = deserialize();
    // 想定するクラスは Class2
    Class2 class2 = (Class2) ois.readObject();
  }
}

2. デシリアライズ処理の意図しない呼び出しやシリアライズデータの細工が行われる可能性がある

 アプリケーションでデシリアライズすることを想定しているクラスであったとしても、検証なしにデシリアライズを行うことはリスクを伴う。何らかの方法(通信の盗聴や逆コンパイルなど)で攻撃者がシリアライズ対象クラスの情報を入手した場合、readObjectメソッド内などで攻撃に使えそうな処理(Dos攻撃や任意のコマンド実行につながるような処理)が実装されていれば、処理を悪用する方法を考えるかもしれない。例えば、readObject()で外部コマンドを起動するような実装になっている場合はデシリアライズ処理の意図しない呼び出しを受けた際に危険なことがお分かりいただけるだろう。

 また、攻撃者によりシリアライズデータを細工された場合、デシリアライズしたオブジェクトのフィールドに意図しないデータを設定されるなどの危険性がある。意図しないデータが設定された場合、アプリケーションの挙動に影響を与えたり、クラッシュしたりするリスクも考えられる。

 下記サンプルコード3は、サンプルコード2と異なり、Class1はアプリケーションでデシリアライズを想定しているクラスと仮定する。

サンプルコード3:シリアライズデータの細工
public class Sample2 {
  public static void main(String[] args) throws ClassNotFoundException, IOException {
    ObjectInputStream ois = deserialize();
    // 想定するクラスは Class1
    Class1 class1= (Class1) ois.readObject();
  }
}

 Class1は実行するコマンドをフィールドに保持しているが、攻撃者がシリアライズデータに保存されているフィールド値を細工した場合、攻撃者が指定したコマンドが実行されてしまう危険性がある。

図4 シリアライズデータの細工
図4 シリアライズデータの細工

 前述したデシリアライズの問題を悪用し、攻撃を行うための主な条件は以下だ。

  • A)シリアライズデータを送受信しているサービスにアクセスできること。
  • B)シリアライズデータを送受信している通信パケットのペイロードが特定できること。
  • C)シリアライズデータのクラス情報を入手できること。
表1 デシリアライズの問題を悪用した攻撃を行うための条件とその対策
  攻撃を行うための条件 攻撃への対策
A)サービスへのアクセス B)パケットのペイロード特定 C)クラス情報入手
1. 悪意あるクラスのオブジェクト生成 不要 デシリアライズ前の検証
2. 悪意あるデシリアライズ処理呼び出し、シリアライズデータの細工 デシリアライズ時の検証

 外部から受け取ったシリアライズデータをデシリアライズしている場合は、A)、B)、C)の条件に該当し、攻撃が可能な状態になっていないか確認することをお勧めする。

 なお、デシリアライズ処理を行っているかは、以下のデシリアライズに関連するAPIを使っているかを検索することで確認できる。

  • readObject():Serializableを実装するクラスで独自に定義でき、デシリアライズ時に実行される。
  • readResolve():Serializableを実装するクラスで独自に定義でき、readObject()の実行後に呼び出され、デシリアライズするオブジェクトを置換できる。
  • readExternal():Externalizableを実装するクラスで定義され、デシリアライズ時に実行される。

 本記事で解説したデシリアライズに関する問題を踏まえて、次回はその対策について解説する。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Javaのデシリアライズに潜むセキュリティ問題連載記事一覧

もっと読む

この記事の著者

藤本 万里子(JPCERT コーディネーションセンター)(フジモト マリコ)

一般社団法人JPCERTコーディネーションセンター 早期警戒グループ 情報セキュリティアナリスト。前職では、国内企業においてソフトウエアの開発や内部統制のためのシステム開発等を担当。 2015年4月、JPCERTコーディネーションセンター早期警戒グループに情報セキュリティアナリストとして着任。主に国...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/9175 2016/01/18 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング