web-dev-qa-db-ja.com

setAccessibleを「正当な」使用のみに制限する方法は?

Java.lang.reflect.AccessibleObject.setAccessibleのパワーについて学べば学ぶほど、それが何ができるのかと驚くことになります。これは、質問に対する私の答えから適応されています( リフレクションを使用して、単体テスト用の静的な最終File.separatorCharを変更する )。

import Java.lang.reflect.*;

public class EverythingIsTrue {
   static void setFinalStatic(Field field, Object newValue) throws Exception {
      field.setAccessible(true);

      Field modifiersField = Field.class.getDeclaredField("modifiers");
      modifiersField.setAccessible(true);
      modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

      field.set(null, newValue);
   }
   public static void main(String args[]) throws Exception {      
      setFinalStatic(Boolean.class.getField("FALSE"), true);

      System.out.format("Everything is %s", false); // "Everything is true"
   }
}

本当にとんでもないことをすることができます:

public class UltimateAnswerToEverything {
   static Integer[] ultimateAnswer() {
      Integer[] ret = new Integer[256];
      Java.util.Arrays.fill(ret, 42);
      return ret;
   }   
   public static void main(String args[]) throws Exception {
      EverythingIsTrue.setFinalStatic(
         Class.forName("Java.lang.Integer$IntegerCache")
            .getDeclaredField("cache"),
         ultimateAnswer()
      );
      System.out.format("6 * 9 = %d", 6 * 9); // "6 * 9 = 42"
   }
}

おそらく、API設計者は、setAccessibleが悪用される可能性を認識していますが、それを提供する正当な用途があることを認めたに違いありません。だから私の質問は:

  • setAccessibleの本当に正当な用途は何ですか?
    • Javaは、そもそもこの必要性がないように設計されているでしょうか?
    • そのような設計の負の結果(もしあれば)はどうなりますか?
  • setAccessibleを正当な用途のみに制限できますか?
    • SecurityManagerのみを使用していますか?
      • どのように機能しますか?ホワイトリスト/ブラックリスト、粒度など?
      • アプリケーションで設定する必要があるのは一般的ですか?
    • setAccessibleの構成に関係なく、SecurityManager- proofになるようにクラスを記述できますか?
      • それとも、私は構成を管理する人に翻弄されていますか?

もう一つ重要な質問があると思います。これについて心配する必要はありますか?

私のクラスはどれも、強制力のあるプライバシーのようには見えません。シングルトンパターン(そのメリットについての疑念は別として)を実施することは不可能です。上記のスニペットが示すように、Javaの基本的な動作のいくつかの基本的な仮定でさえ、保証されるほどではありません。

これらの問題は本当ではありませんか?


わかりました。setAccessibleのおかげで、Java文字列は[〜#〜] not [〜#〜]不変です。

import Java.lang.reflect.*;

public class MutableStrings {
   static void mutate(String s) throws Exception {
      Field value = String.class.getDeclaredField("value");
      value.setAccessible(true);
      value.set(s, s.toUpperCase().toCharArray());
   }   
   public static void main(String args[]) throws Exception {
      final String s = "Hello world!";
      System.out.println(s); // "Hello world!"
      mutate(s);
      System.out.println(s); // "HELLO WORLD!"
   }
}

これが大きな懸念だと思うのは私だけですか?

99

これについて心配する必要がありますか?

それは完全にあなたが書いているプログラムの種類とどんな種類のアーキテクチャに依存します。

Foo.jarと呼ばれるソフトウェアコンポーネントを世界中の人々に配布している場合、とにかく完全に彼らの慈悲にかかっています。彼らはあなたの.jar内のクラス定義を変更できます(リバースエンジニアリングまたは直接のバイトコード操作により)。彼らはあなた自身のJVMなどでコードを実行することができます。この場合、心配することは何の役にも立ちません。

HTTPを介して人やシステムとのみインターフェイスするWebアプリケーションを作成していて、アプリケーションサーバーを制御している場合も、問題ではありません。あなたの会社の仲間のコーダーがあなたのシングルトンパターンを破るコードを作成するかもしれませんが、それは彼らが本当に望んでいる場合のみです。

将来の仕事がSun Microsystems/Oracleでコードを書いており、Javaコアまたは他の信頼できるコンポーネントのコードを書くことを任されている場合、それはあなたが知っておくべきことです。いずれにしても、おそらくあなたは Secure Coding Guidelines を内部文書とともに読むようになるでしょう。

Javaアプレットを作成する場合は、セキュリティフレームワークに注意する必要があります。setAccessibleを呼び出そうとする署名のないアプレットは、SecurityExceptionになります。

setAccessibleは、従来の整合性チェックを回避する唯一のものではありません。非API、コアJava Sun.misc.Unsafeと呼ばれるクラスがあり、メモリへの直接アクセスを含む、必要なことをほとんど実行できます。ネイティブコード(JNI)はこれを回避できます。同様に制御の種類。

サンドボックス環境(Java Applets、JavaFX)など)では、各クラスにUnsafe、setAccessible、およびネイティブ実装の定義へのアクセス許可とアクセスのセットがあり、SecurityManagerによって制御されます。

「Javaアクセス修飾子は、セキュリティメカニズムを意図したものではありません。」

それは、Javaコードが実行されている場所に大きく依存します。コアJavaクラスは、サンドボックスを実施するセキュリティメカニズムとしてアクセス修飾子を使用します。

setAccessibleの真に正当な用途は何ですか?

Javaコアクラスは、セキュリティ上の理由でプライベートのままにする必要があるものにアクセスする簡単な方法として使用します。例として、Java Serialization framework誰かがSystem.setErrに言及し、それは良い例でしょうが、奇妙なことに、SystemクラスメソッドsetOut/setErr/setInはすべて、最終フィールドの値を設定するためにネイティブコードを使用します。

別の明白な合法的な使用法は、オブジェクトの内部を覗く必要があるフレームワーク(永続性、Webフレームワーク、インジェクション)です。

私の意見では、デバッガーは通常同じJVMプロセスで実行されるのではなく、他の手段(JPDA)を使用したJVMとのインターフェースであるため、このカテゴリーに分類されません。

Could Javaは、そもそもこの必要性がないように設計されていますか?

それはよく答えるにはかなり深い質問です。はい、と思いますが、それほど望ましいものではないかもしれない他のメカニズムを追加する必要があります。

setAccessibleを正当な使用のみに制限できますか?

最も簡単なOOTB=適用できる制限は、SecurityManagerを持ち、特定のソースからのコードにのみsetAccessibleを許可することです。これは、Java -標準のJava Java_HOMEからのクラスはsetAccessibleを実行できますが、foo.comからの署名されていないアプレットクラスはsetAccessibleを実行できません。前述のように、このパーミッションはバイナリです。 setAccessibleに特定のフィールド/メソッドを変更させ、他のフィールド/メソッドを許可しないようにする明確な方法はありません。ただし、SecurityManagerを使用すると、リフレクションの有無にかかわらず、クラスが特定のパッケージを完全に参照できないようにすることができます。 。

SecurityManagerの構成に関係なく、setAccessible-proofになるようにクラスを記述できますか? ...それとも、構成を管理する人に翻弄されていますか?

あなたはできませんし、あなたは間違いなくそうです。

104
Sami Koivu
  • setAccessibleの本当に正当な用途は何ですか?

単体テスト、JVMの内部(例:System.setError(...)の実装)など。

  • Javaは、そもそもこの必要性がないように設計されていますか?
  • そのような設計の負の結果(もしあれば)はどうなりますか?

多くのことは実装できません。たとえば、さまざまなJava持続性、シリアル化、および依存性注入はリフレクションに依存しています。実行時のJavaBeans規則に依存するものはほとんど何でもです。

  • setAccessibleを正当な使用のみに制限できますか?
  • SecurityManagerのみを使用していますか?

はい。

  • どのように機能しますか?ホワイトリスト/ブラックリスト、粒度など?

それは許可に依存しますが、setAccessibleを使用する許可はバイナリだと思います。細分性が必要な場合は、制限するクラスに対して、異なるセキュリティマネージャーと異なるクラスローダーを使用する必要があります。より詳細なロジックを実装するカスタムセキュリティマネージャーを実装できると思います。

  • アプリケーションで設定する必要があるのは一般的ですか?

番号。

  • setAccessible構成に関係なくSecurityManager- proofになるようにクラスを作成できますか?
    • それとも、私は構成を管理する人に翻弄されていますか?

いいえ、できません。そうです。

他の選択肢は、ソースコード分析ツールを介してこれを「実施」することです。例えばカスタムpmdまたはfindbugsルール。または、(たとえば)grep setAccessible ...で識別されるコードの選択的なコードレビュー。

フォローアップに応じて

私のクラスはどれも、強制力のあるプライバシーのようには見えません。シングルトンパターン(そのメリットについての疑念は別として)を実施することは不可能です。

それがあなたを心配するなら、私はあなたが心配する必要があると思います。しかし、実際には、設計上の決定を尊重するために、他のプログラマにforceを強制しようとするべきではありません。人々がリフレクションを使用してシングルトンの複数のインスタンスを無償で作成するほど愚かである場合(たとえば)、彼らは結果とともに生きることができます。

一方、機密情報を開示から保護するという意味を含む「プライバシー」を意味する場合は、間違ったツリーを探しています。 Javaアプリケーションで機密データを保護する方法は、機密データを扱うセキュリティサンドボックスに信頼できないコードを許可しないことです。Javaアクセス修飾子は意図されていませんセキュリティメカニズムになります。

<String example>-これが大きな懸念だと思うのは私だけですか?

おそらくonlyの1つではありません:-)。しかし、IMO、これは懸念事項ではありません。信頼できないコードはサンドボックスで実行する必要があるという事実は受け入れられています。信頼できるコード/信頼できるプログラマがこのようなことをしている場合、問題は予想外に変更可能な文字列よりも深刻です。 (論理爆弾、 隠密チャンネル などによるデータの流出などを考えてください)

開発または運用チームの「悪役」の問題に対処する(または軽減する)方法があります。しかし、それらは高価で制限的であり、ほとんどのユースケースでは過剰です。

15
Stephen C

この観点では、反射は確かに安全性/セキュリティに直交しています。

どのように反射を制限できますか?

Javaには、セキュリティモデルの基盤としてセキュリティマネージャとClassLoaderがあります。あなたの場合、 Java.lang.reflect.ReflectPermission

しかし、これは反射の問題を完全には解決しません。使用可能なリフレクション機能は、現在のケースではないきめ細かい認可スキームに従う必要があります。例えば。特定のフレームワークでリフレクション(Hibernateなど)を使用できるようにしますが、残りのコードは使用できません。または、デバッグの目的で、プログラムが読み取り専用でのみ反映することを許可します。

将来主流になる可能性のあるアプローチの1つは、いわゆるミラーを使用して、クラスから反射機能を分離することです。 ミラー:メタレベル機能の設計原則 を参照してください。ただし、この問題に取り組むさまざまな その他の研究 があります。しかし、動的言語の場合、静的言語よりも問題が深刻であることに同意します。

反射がもたらす超大国を心配する必要がありますか?はい、いいえ。

はいJavaプラットフォームはClassloaderとセキュリティマネージャで保護されることになっているという意味で。反省は違反と見なすことができます。

Noほとんどのシステムはとにかく完全に安全ではないという意味で。多くのクラスは頻繁にサブクラス化される可能性があり、それだけでシステムを悪用する可能性があります。もちろん、クラスをfinalまたは sealed にして、他のjarでサブクラス化できないようにすることができます。しかし、これによると、いくつかのクラスのみが正しく保護されています(たとえば、String)。

これを参照してください 最終クラスについての回答 素敵な説明について。 Sami Koiv のブログも参照してくださいJavaセキュリティをハッキングする。

Javaのセキュリティモデルは、いくつかの点で不十分であると見なすことができます。 NewSpeak などの一部の言語は、依存関係の反転によって明示的に与えられます(デフォルトでは何もありません)。

セキュリティはとにかくrelativeであることに注意することも重要です。言語レベルでは、たとえば、モジュールフォームがCPUを100%消費したり、OutOfMemoryExceptionまでのすべてのメモリを消費したりすることを防ぐことはできません。このような懸念は、他の手段で対処する必要があります。将来的にはJavaリソース使用率クォータで拡張されますが、明日はそうではありません:)

私はこのテーマをさらに広げることができましたが、私は自分の主張を述べたと思います。

11
ewernli