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


100

の力について学べば学ぶほど、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ますか?
      • それとも、構成を管理する人のなすがままになっていますか?

私はもう1つの重要な質問だと思います:これについて心配する必要がありますか???

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

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


さて、私は確認しました:のおかげでsetAccessible、Java文字列は不変ではありません

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!"
   }
}

これが大きな懸念であると考えるのは私だけですか?


30
あなたは彼らがC ++で何ができるかを見るべきです!(ああ、おそらくC#です。)
Tom Hawtin-2010年

回答:


105

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

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

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

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

将来の仕事がSun Microsystems / Oracleでコードを書くことであり、Javaコアまたは他の信頼できるコンポーネントのコードを書くことを任されている場合、それはあなたが知っておくべきことです。しかし、心配することはあなたの髪を失うだけです。いずれにせよ、おそらくそれらはあなたに内部文書とともにセキュアコーディングガイドラインを読ませるでしょう。

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

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

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

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

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

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

Javaコアクラスは、セキュリティ上の理由で非公開にしておく必要のあるものにアクセスする簡単な方法としてそれを使用します。例として、Javaシリアル化フレームワークはそれを使用して、オブジェクトを逆シリアル化するときにプライベートオブジェクトコンストラクターを呼び出します。誰かがSystem.setErrに言及しましたが、それは良い例ですが、奇妙なことに、SystemクラスのメソッドsetOut / setErr / setInはすべて、最終フィールドの値を設定するためにネイティブコードを使用します。

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

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

Javaは、そもそもこの必要性がないように設計されているのでしょうか?

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

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

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

SecurityManagerの構成に関係なく、クラスをsetAccessible-proofに設定できますか?...または、設定を管理する人のなすがままになっていますか?

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


「foo.jarと呼ばれるソフトウェアコンポーネントを世界中の人々に配布する場合は、とにかく完全に彼らのなすがままです。彼らは.jar内のクラス定義を(リバースエンジニアリングまたは直接のバイトコード操作によって)変更する可能性があります。独自のJVMなどでコードを実行できます。この場合、心配することは役に立ちません。」これはまだ事実ですか?ソフトウェアの改ざんを難しくすることについてのアドバイスはありますか(うまくいけば大幅に)?このため、Javaデスクトップアプリケーションをウィンドウの外に捨てるのは困難です。
セロ

@naaz何も変わっていないと言わざるを得ない。それは単にアーキテクチャがあなたに対して働いているというだけです。自分のマシンで実行しているソフトウェアのうち、私が完全に制御できるものはどれでも変更できます。最強の抑止力は合法的な抑止力かもしれませんが、それがすべてのシナリオで機能するとは思いません。Javaバイナリは最も簡単に元に戻すことができると思いますが、経験のある人は何でも改ざんします。難読化は大きな変更を行うことを困難にすることで役立ちますが、ブール値(著作権チェックなど)のようなものは、バイパスするのは簡単です。代わりに、重要なパーツをサーバーに配置してみることもできます。
サミコイブ2017年

デヌーボはどうですか?
セロヤン

15
  • 本当に正当な用途はsetAccessible何ですか?

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

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

多くのことは実行不可能です。たとえば、さまざまなJavaの永続化、シリアル化、依存関係の注入はリフレクションに依存しています。そして、実行時にJavaBeansの規則に依存するほとんどすべてのもの。

  • setAccessible正当な使用のみに制限できますか?
  • それだけSecurityManagerですか?

はい。

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

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

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

番号。

  • クラスを次のように書くことはできますか setAccessibleSecurityManager構成に関係なく証明ますか?
    • それとも、構成を管理する人のなすがままになっていますか?

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

他の代替手段は、ソースコード分析ツールを介してこれを「強制」することです。たとえば、カスタムpmdまたはfindbugsルール。またはによって識別されたコードの選択的なコードレビューgrep setAccessible ...

フォローアップへの対応

私のクラスのどれも、強制力のあるプライバシーのいかなる類似もまったくありません。シングルトンパターン(そのメリットについて疑問を投げかける)は、今では実施することが不可能です。

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

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

<文字列の例>-これが大きな懸念であると考えるのは私だけですか?

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

開発チームや運用チームの「悪役」の問題に対処(または緩和)する方法はいくつかあります。しかし、それらはコストがかかり、制限が多く、ほとんどのユースケースでは過剰です。


1
永続化の実装などにも役立ちます。リフレクションはデバッガーにとっては実際には役に立ちません(ただし、本来はそうすることを目的としていました)。SecurityManager取得できるのはアクセス権のチェックだけなので、これを(サブクラス)に変更することはできません。実際にできることは、accを見て、おそらく現在のスレッドをチェックしてスタックを歩くだけです)。grep java.lang.reflect.
トム・ホーティン-10

@Stephen C:+1ですが、先ほど追加した質問へのご意見もお待ちしております。前もって感謝します。
polygenelubricants 2010年

grepはひどい考えであることに注意してください。Javaでコードを動的に実行する方法はたくさんあるので、1つは見逃してしまいます。コードをブラックリストに登録することは、一般的に失敗のレシピです。
アンチモン2013

「Javaアクセス修飾子は、セキュリティメカニズムを意図したものではありません。」-実際、そうです。Javaは、信頼できるコードと信頼できないコードが同じ仮想マシンで共存できるように設計されています。これは、当初からコア要件の一部でした。ただし、システムがロックダウンされたSecurityManagerで実行されている場合のみ。コードをサンドボックス化する機能は、アクセス指定子の適用に完全に依存しています。
Jules

@Jules-ほとんどのJavaエキスパートはそれに同意しません。例えばstackoverflow.com/questions/9201603/...
スティーブンC

11

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

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

JavaにはセキュリティマネージャーがありClassLoader、そのセキュリティモデルの基礎となっています。あなたの場合、あなたはを見る必要があると思いますjava.lang.reflect.ReflectPermission

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

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

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

はい、JavaプラットフォームはClassloaderセキュリティマネージャで保護されているはずです。リフレクションをいじくる能力は、違反と見ることができます。

ほとんどのシステムはとにかく完全に安全ではないという意味ではありません。多くのクラスは頻繁にサブクラス化される可能性があり、それだけでシステムを乱用する可能性があります。もちろん、クラスを作成finalしたり、他のjarでサブクラス化できないようにシールしたりできます。しかし、これに従って正しく保護されているクラスはわずかです(例:String)。

良い説明については、最終クラスに関するこの回答を参照してください。セキュリティに関するJavaハッキングの詳細については、Sami Koivuのブログも参照してください。

Javaのセキュリティモデルは、いくつかの点で不十分であると見なすことができます。NewSpeakなどの一部の言語は、モジュール化に対してさらに根本的なアプローチを採用しており、依存関係の反転(デフォルトでは何もない)によって明示的に与えられたものにのみアクセスできます。

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

私はこのテーマについてもっと詳しく説明することができましたが、私は自分の主張をしたと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.