目次 | 前の項目 | 次の項目 Java オブジェクト直列化仕様
Version 6.0

オブジェクト直列化におけるセキュリティー





付録 A


トピック:


A.1 概要

オブジェクト直列化システムを使えば、オブジェクトグラフからバイトストリームを作成し、(ディスクに保存するか、ネットワークを介して) JavaTM 環境の外へ送り、それを使って、同じ状態を持つ同等な新しいオブジェクトセットを再作成することができます。

オブジェクトの状態がこの環境の外でどうなるかは、(定義により) JavaTM システムの制御外のことであり、したがって、システムで提供されるセキュリティーの制御外のことです。ここで 1 つの疑問が生じます。いったんオブジェクトが直列化されると、結果のバイト配列が、ある意味その直列化復元を行う Java プログラムのセキュリティーに悪影響を及ぼして実行および変更される可能性はないのでしょうか。ここでは、このようなセキュリティーの問題に焦点を当てて説明します。


A.2 設計目標

オブジェクト直列化の目標は、できる限りシンプルでありながら、既知のセキュリティーの制約と一貫性があることです。 システムがシンプルであるほど、安全である可能性はより高くなります。以下は、オブジェクト直列化で使用されているセキュリティー方式の概要です。


A.3 セキュリティーの問題

セキュリティーを十分に確保しないでオブジェクトを直列化すると、悪意のあるユーザーが、直列化バイトストリームにアクセスして機密データを読み込んだり、不正なまたは危険な状態のオブジェクトを作成したり、直列化復元されているオブジェクトの private フィールドへの参照を取得する可能性があります。セキュリティーに関係する実装を行うときは、直列化するときに次の点を考慮する必要があります。


A.4 機密データの直列化の防止

機密性の高いデータを含むフィールドは、直列化すべきではありません。直列化すると、直列化ストリームにアクセスすることでそれらの値を相手方に公開することになります。フィールドの直列化を防ぐ方法をいくつか次に示します。


A.5 クラス特有の直列化メソッドの作成

直列化復元されたオブジェクトが、保証されるべきである不変式のセットを壊すような状態にならないように、クラスが独自の直列化と直列化復元のメソッドを定義することができます。あるクラスのデータメンバー間で維持する必要がある不変式のセットがある場合、これらの不変式について知ることができるのはそのクラスだけなので、これらの不変式を検査する直列化復元メソッドを提供するかどうかは、そのクラスの作成者次第です。

セキュリティーを重視した実装を行う際には、直列化可能クラスの readObject メソッドが実際に public コンストラクタでなければならず、そのように扱う必要があります。これは、readObject メソッドが暗黙的である場合と明示的である場合のどちらにもあてはまります。readObject メソッドに提供されたバイトストリームが、適切に構築された正しいタイプのオブジェクトを直列化することによって生成されると想定するのは危険です。より防御性に優れたプログラミングを行うには、構築中のオブジェクトに悪影響を及ぼそうとする悪意あるユーザーによってバイトストリームが提供されることを想定する必要があります。

このことは、セキュリティーを関知しない場合でも重要です。ディスクファイルが壊れ、直列化データが無効になることも起こり得ます。したがって、そのような不変式を検査することは、単にセキュリティーのためだけではなく、有効性を確保する手段でもあります。しかし、これを行うことができるのは、特定クラスのコードの中だけです。これは、どの不変式を維持し、検査するかを直列化パッケージで判断する方法がないからです。

JavaTM 2 SDK, Standard Edition Version 1.4 では、クラス定義メソッド readObjectNoData のサポートが追加されました (「3.5 readObjectNoData メソッド」を参照)。フィールドをデフォルト以外の値に初期化する final 以外の直列化可能クラスでは、サブクラスインスタンスが直列化復元されるときに直列化ストリームがそのクラスを直列化復元されたオブジェクトのスーパークラスとしてリストしない場合に、一貫した状態を保証するために readObjectNoData メソッドを定義する必要があります。これは、受け取り側が、送り側とは異なるバージョンの直列化復元されたインスタンスのクラスを使用し、受け取り側のバージョンが、送り側のバージョンによって継承されないクラスを継承する場合に発生する可能性があります。また、直列化ストリームが改変された場合にも発生することがあります。したがって、readObjectNoData は、「悪意のある」または不正なソースストリームであっても、直列化復元されたオブジェクトを正しく初期化するのに役立ちます。


A.6 非共有の直列化復元されたオブジェクトの保護

クラスが private または package private のオブジェクト参照フィールドを保持し、かつそのクラスが、それらのオブジェクト参照はクラス (またはパッケージ) の外部では利用できないという事実に依存する場合は、防衛のために、直列化復元プロセスの一環として参照オブジェクトをコピーするか、または ObjectOutputStream.writeUnshared メソッドと ObjectInputStream.readUnshared メソッド (JavaTM 2 SDK, Standard Edition Version 1.4 で導入) を使って内部オブジェクトへの一意参照を保証する必要があります。

コピーする方法では、ストリームから直列化復元されるサブオブジェクトを、「信頼されない入力」として扱う必要があります。新たに作成したオブジェクトを初期化して、直列化復元されたサブオブジェクトと同じ値を保持させ、readObject メソッドを使ってサブオブジェクトの代替とする必要があります。たとえば、オブジェクトが private バイト配列フィールド b を保持する場合、次に示すように、このフィールドを private のままにしなければなりません。

    private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException
    {
        s.defaultReadObject();

        b = (byte[])b.clone();

        if (<invariants are not satisfied>)
            throw new java.io.StreamCorruptedException();
    }
この問題は、可変サブオブジェクトへの内部参照 (private) を含む不変オブジェクトの直列化を考慮する際に特に重要です。コンテナオブジェクトの直列化復元時に、サブオブジェクトをコピーするための特別な措置が何もとられない場合、直列化ストリームへの書き込み権限を持つ悪意のある第三者が、可変サブオブジェクトへの参照を偽造し、これらの参照を使ってコンテナオブジェクトの内部状態を変更することで、コンテナオブジェクトの不変性を侵害する場合があります。このような場合に備えて、不変のコンテナクラスがクラス固有の直列化復元メソッドを提供することがポイントです。直列化復元される各可変コンポーネントオブジェクトのプライベートなコピーの作成は、このメソッドを使って行います。不変性を維持するために、不変コンポーネントオブジェクトのコピーを作成する必要はないことに留意してください。

また、clone の呼び出しが、常にサブオブジェクトを自己防衛的にコピーする正しい方法とは限らないことに留意することも重要です。独立したコピーを作成するため (およびコピーへの参照を「横取り (from stegword)」しないため) に clone メソッドをあてにできない場合、別の方法でコピーを作成する必要があります。サブオブジェクトのクラスが final ではない場合、呼び出される clone メソッドやヘルパーメソッドがサブクラスによってオーバーライドされる可能性があるため、常に別の方法でコピーを作成する必要があります。

JavaTM 2 SDK, Standard Edition Version 1.4 以降では、ObjectOutputStream.writeUnshared メソッドと ObjectInputStream.readUnshared メソッドを使って、直列化復元された配列オブジェクトへの一意参照を保証することもできます。この方法では、防衛的にコピーする方法の複雑さ、パフォーマンスコスト、およびメモリオーバーヘッドの問題を回避できます。readUnshared メソッドと writeUnshared メソッドの詳細は、「3.1 ObjectInputStream クラス」および「2.1 ObjectOutputStream クラス」を参照してください。


A.7 外部化可能オブジェクトの上書きの防止

Externalizable インタフェースを実装するオブジェクトは、public の readExternal メソッドを提供しなければなりません。このメソッドは public であるため、オブジェクトにアクセス権のあるユーザーによって任意で呼び出されます。オブジェクトの内部状態を複数の (不正な) readExternal の呼び出しによって上書きされないようにするには、実装の際にチェックを追加して、適切な場合にだけ内部の値が設定されるようにします。

    public synchronized void readExternal(ObjectInput in)
        throws IOException, ClassNotFoundException
    {
        if (! initialized) {
            initialized = true;

            // read in and set field values ...
        } else {
            throw new IllegalStateException();
        }
    }



A.8 バイトストリームの暗号化

仮想マシンの外でバイトストリームを保護する別の方法として、直列化パッケージで作成したストリームの暗号化があります。バイトストリームを暗号化することにより、直列化オブジェクトの private 状態の復号化と読み込みができなくなり、ストリームの内容の不正な変更に対する保護となります。

暗号の使用可能なクラスが独自に直列化および直列化復元のメソッドを定義できるようにすることにより、または合成可能なストリームの抽象化に従うことにより、オブジェクトの直列化で暗号を使用できます。 この合成可能なストリームの抽象化により、データを暗号化する別のフィルタストリームに送られる直列化ストリームの出力を許可します。



目次 | 前の項目 | 次の項目
Copyright © 2005 Sun Microsystems, Inc. All Rights Reserved.