入れ子クラスとは、クラスあるいはインタフェースの本体内で宣言されたクラスである[JLS 2005]。セマンティクスをよく理解しないまま入れ子クラスを使用すると、間違いが発生しやすい。(入れ子クラスのセマンティクスとして)よく言われるのは、入れ子クラスだけがその外側にあるクラスのコンテンツにアクセスできる、というものである。入れ子クラスがpublic宣言されている、あるいは入れ子クラスがpublicメソッドやコンストラクタを持つ場合、外側のクラスのprivateフィールドは、入れ子クラスからアクセスできるのはもちろん、同一パッケージの他のクラスからもアクセスすることができる。そのため、入れ子クラスは、外側のクラスのprivateメンバを、外部のクラスやパッケージからアクセスできるようにしてはならない。
また、Java言語仕様「§8.3 フィールド宣言」には以下のような記載がある[JLS 2005]。
スーパークラスのprivateフィールドが、サブクラスでアクセス可能となる場合があることに注意されたい(たとえば双方のクラスが同じクラスのメンバである場合)。しかしながらprivateフィールドがサブクラスへと継承されることはない。
違反コード
以下の違反コード例では、private宣言されている(x,y)座標を内部クラスのgetPoint()メソッドを通じて外部に公開している。そのため、同じパッケージに属するAnotherClassクラスはこの情報にアクセスすることができる。
class Coordinates { private int x; private int y; public class Point { public void getPoint() { System.out.println("(" + x + "," + y + ")"); } } } class AnotherClass { public static void main(String[] args) { Coordinates c = new Coordinates(); Coordinates.Point p = c.new Point(); p.getPoint(); } }
適合コード
内部のクラスやそのメソッドとコンストラクタを隠蔽するには、内部のクラスの宣言に private 修飾子をつける。
class Coordinates { private int x; private int y; private class Point { private void getPoint() { System.out.println("(" + x + "," + y + ")"); } } } class AnotherClass { public static void main(String[] args) { Coordinates c = new Coordinates(); Coordinates.Point p = c.new Point(); // コンパイルできない p.getPoint(); } }
AnotherClass は、private 宣言されたクラスにアクセスしようとしているので、コンパイルエラーになる。
リスク評価
Java 言語では、内部クラスが存在する場合、private メンバへのアクセス制限が弱められ、セキュリティ上の問題となる可能性がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
OBJ08-J | 中 | 中 | 中 | P8 | L2 |
自動検出
private でないメソッドやコンストラクタを定義するprivateでない内部クラスを検出するのは容易である。
関連ガイドライン
MITRE CWE | CWE-492. Use of Inner Class Containing Sensitive Data |
参考文献
[JLS 2005] | §8.1.3, Inner Classes and Enclosing Instances |
§8.3, Field Declarations | |
[Long 2005] | §2.3, Inner Classes |
[McGraw 1999] | Securing Java, Getting Down to Business with Mobile Code |
翻訳元
これは以下のページを翻訳したものです。
OBJ08-J. Do not expose private members of an outer class from within a nested class (revision 103)