web-dev-qa-db-ja.com

逆シリアル化する前にオブジェクトを検証する方法

私のコードでは、JMSキューから受け取ったObjectMessageオブジェクトからgetObject()メソッドを呼び出しています。 Fortifyレポートは、このDynamic Code Evaluation: Unsafe Deserializationのようなエラー名でこのgetObject()メソッドについて不平を言っています。基本的には、オブジェクトストリームの内容を検証せずに、信頼できないデータを逆シリアル化しないでください。以下はコードです。このFortifyレポートエラーを取り除くためにどのように、どのような方法を使用すればよいですか。

if (message instanceof ObjectMessage) {
    ObjectMessage objMessage = (ObjectMessage) message;
    Object objReportMessage = objMessage.getObject();
....

Fortifyが報告した問題と推奨事項を以下に示します。次に、このエラーが上記のコードのobjMessage.getObject();の行を指すようにします。

動的コード評価:安全でない逆シリアル化(1問題)

Abstract実行時にユーザー制御のオブジェクトストリームをデシリアライズすると、攻撃者がサーバー上で任意のコードを実行したり、アプリケーションロジックを悪用したり、サービス拒否を引き起こす可能性があります。

説明 Javaシリアライゼーションは、オブジェクトグラフを、オブジェクト自体と、バイトストリームから再構築するために必要なメタデータを含むバイトストリームに変換します。開発者は、カスタムコードを作成して、逆シリアル化のプロセスJavaオブジェクト。逆シリアル化されたオブジェクトを別のオブジェクトまたはプロキシに置き換えることもできます。カスタマイズされた逆シリアル化プロセスは、オブジェクトがアプリケーションに戻されてキャストされる前に、オブジェクトの再構築中に行われます。予想されるタイプ。開発者が予想されるタイプを強制しようとするときまでに、コードはすでに実行されている可能性があります。カスタムの逆シリアル化ルーチンは、ランタイムクラスパスに存在する必要があるシリアル化可能なクラスで定義され、攻撃者が挿入できないため、これらの攻撃は、アプリケーション環境で使用可能なクラスに依存します。残念ながら、一般的なサードパーティのクラスまたはJDKクラスでさえ、JVMリソースを使い果たすために悪用される可能性があります。悪意のあるファイル、または任意のコードを実行します。特定のプロトコルは、Javaシリアル化をトランスポート層の舞台裏で使用します。RMIおよびJMXは、これらのプロトコルの例です。

例1:これは、1つ以上のパラメーターを持つメソッドを含む、公開できるRMIインターフェースの例です。これらのメソッドをリモートで呼び出すと、引数がサーバーで逆シリアル化され、攻撃者が悪意のあるオブジェクトグラフを挿入できるようになります。

public interface MyService extends Java.rmi.Remote {
public Object doSomething (Object arg0) throws RemoteException;
public Object doSomethingElse (Object arg0, Object arg1) throws
RemoteException;
...
}

例2:JMX MBeanもJavaシリアライゼーションを使用して呼び出し引数を送信します。以下の例では、MyManagedBeanクラスのメソッドがクライアントに公開されます。

MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=MyManagedBean");
MyManagedBean mbean = new MyManagedBean();
mbs.registerMBean(mbean, name);

推奨可能であれば、オブジェクトストリームの内容を検証せずに、信頼できないデータを逆シリアル化しないでください。逆シリアル化されているクラスを検証するには、先読み逆シリアル化パターンを使用する必要があります。オブジェクトストリームには、最初にクラス記述メタデータが含まれ、次にそのメンバーフィールドのシリアル化されたバイトが含まれます。 Javaシリアライゼーションプロセスでは、開発者はクラスの説明を読んで、オブジェクトのデシリアライゼーションを続行するか中止するかを決定できます。そのためには、Java.ioをサブクラス化する必要があります。 ObjectInputStreamは、クラスの検証と検証が2016年9月29日5:09 PM Copyright 2015 Hewlett Packard Enterprise Development LP 13で行われるべきであるresolveClass(ObjectStreamClass desc)メソッドのカスタム実装を提供します。 Apache Commons IO(org.Apache.commons.io.serialization.ValidatingObjectInputStream)など、簡単に使用できる先読みパターンの既存の実装です。常に厳密なホワイトリストアプローチを使用してください予想されるタイプのみを非シリアル化します。攻撃者は多くの利用可能なガジェットを使用してブラックリストを迂回できるため、ブラックリストアプローチは推奨されません。また、コード実行を達成するための一部のクラスは公に知られているものの、未知または非公開のクラスもあることに注意してください。 edなので、ホワイトリストアプローチが常に優先されます。ホワイトリストで許可されているクラスはすべて監査して、逆シリアル化しても安全であることを確認する必要があります。サービス拒否攻撃を回避するには、resolveObject(Object obj)メソッドをオーバーライドして、逆シリアル化されているオブジェクトの数をカウントし、しきい値を超えたときに逆シリアル化を中止することをお勧めします。ライブラリまたはフレームワークで逆シリアル化が行われる場合(JMX、RMI、JMS、HTTP呼び出しを使用する場合など)、上記の推奨事項は、開発者の制御が及ばないため、役に立ちません。このような場合、これらのプロトコルが次の要件を満たしていることを確認する必要があります。-公開されていない。 -認証を使用します。 -整合性チェックを使用します。 -暗号化を使用します。さらに、HPE Security Fortify Runtimeは、アプリケーションがObjectInputStreamから逆シリアル化を実行するたびに適用されるセキュリティ制御を提供し、アプリケーションコードだけでなく、ライブラリおよびフレームワークコードもこのタイプの攻撃から保護します。

8
Tech Noob

ValidatingObjectInputStream を見てください。基本的に、逆シリアル化を許可するクラスをホワイトリストに登録します(取得する情報に基づいてこれらを知っておく必要があります)。次にバリデーターはシリアル化されたデータへのメタデータをチェックし、ホワイトリスト内にないクラスを拒否します。

5
Aaron Lawyer

推奨事項のヒント部分を見ると、先読みObjectInputStreamが実装されている場合でも問題が報告されることが示されています。したがって、問題を修正できたとしても、結果を取り除くことはできません

ただし、コードがJMSを使用しており、JMSを使用している場合は、逆シリアル化を制御しません。これは、コピーして貼り付けた推奨事項によって認識されます。

ライブラリまたはフレームワークで逆シリアル化が行われる場合(JMX、RMI、JMS、HTTP呼び出しを使用する場合など)、上記の推奨事項は、開発者の制御が及ばないため、役に立ちません。このような場合は、これらのプロトコルが次の要件を満たしていることを確認する必要があります。

  • 公開されていません。
  • 認証を使用します。
  • 整合性チェックを使用します。
  • 暗号化を使用します。

したがって、あなたの本当の修正は、これらの4つの箇条書きが確実に守られるようにすることです。接続について調査する必要があります。要件や制限によっては、それができない場合があります。

8
wrestang

誰かがこの古い投稿にアクセスした場合に備えて、ObjectMessageからTextMessageに切り替え、シリアル化されたオブジェクトの代わりにJSONを送信するだけでこの問題を回避しました。

この投稿は非常に有益でした

//Sender
      ObjectMapper mapper = new ObjectMapper();
      TextMessage message = session.createTextMessage(mapper.writeValueAsString(foo));
      messageBus.send(message);

//Receiver
    ObjectMapper mapper = new ObjectMapper();
    try {
        Foobar foo= mapper.readValue(textMessage.getText(), new TypeReference<Foobar>(){});
        dataHandlerProcess(foo);
    } catch (IOException e) {
        logger.error("Could not parse Foobar JSON ",e );
        return;
    }
1
J. Allen