Japan Computer Emergency Response Team Coordination Center 電子署名者 : Japan Computer Emergency Response Team Coordination Center DN : c=jp, st=tokyo, l=chiyoda-ku, email=office@jpcert.or.jp, o=japan Computer Emergency Response Team Coordination Center, cn=japan Computer Emergency Response Team Coordination Center 日付 : 2013.06.26 14:36:21 +09'00' Javaセキュアコーディングセミナー東京 第4回 メソッドとセキュリティ 2012年12月16日(日) JPCERTコーディネーションセンター 脆弱性解析チーム 熊谷 裕志 戸田 洋三 1
本 資 料 について 本 セミナーに 使 用 するテキストの 著 作 権 はJPCERT/CCに 帰 属 します 事 前 の 承 諾 を 受 けた 場 合 を 除 いて 本 資 料 に 含 有 される 内 容 ( 一 部 か 全 部 かを 問 わな い)を 複 製 公 開 送 信 頒 布 譲 渡 貸 与 使 用 許 諾 転 載 再 利 用 できません 本 セミナーに 関 するお 問 い 合 わせ JPCERTコーディネーションセンター セキュアコーディング 担 当 E-mail:secure-coding@jpcert.or.jp TEL:03-3518-4600 2
本 セミナーについて 4 回 連 続 セミナーです 第 1 回 9 月 9 日 ( 日 ) オブジェクトの 生 成 と 消 滅 におけるセキュリティ 第 2 回 10 月 14 日 ( 日 ) 数 値 データの 取 扱 いと 入 力 値 検 査 第 3 回 11 月 11 日 ( 日 ) 入 出 力 (ファイル,ストリーム)と 例 外 時 の 動 作 第 4 回 12 月 16 日 メソッドとセキュリティ 開 発 環 境 持 参 3
今 日 の 時 間 割 講 義 (13:30--15:00) メソッド/セキュリティ (13:30--14:35) ファイナライザ (14:45--15:15) ハンズオン (15:15--16:30) 15:25--15:55 15:55--16:30 4
メソッド 5
メソッドとは プログラミング 言 語 Java 第 4 版 1.8メソッドとパラメータ 6
メソッドとは Java 言 語 仕 様 第 3 版 8.4.2 メソッドのシグネチャ 7
メソッドとは class Person { private int id; private String name; public Person(int i, String s){ id=i; name=s; public int id(){ return id; public String name(){ return name; public static void main(string[] args){ person p = new person(1, taro ); System.out.println(p.name() + : + p.id()); クラスPersonに メソッドid()とname()とmain(String[])が 定 義 されている 8
メソッドオーバーロードを乱用しない Java言語ではメソッドのオーバーロードが可能 2つのメソッドは 異なる数のパラメータか異なる型のパラメータ を持っていれば つまり異なるシグネチャであれば 同じ名前を 持つことができます メソッドの1つの名前が複数の意味を持つの で この機能はオーバーロード(overloading)と呼ばれます 1つのメソッドを呼び出す場合に オーバーロードされているメソ ッドから最も一致するメソッドを探すために コンパイラーは引 数の型を使用します プログラミング言語Java第4版 2.8メソッドのオーバーロード 9
メソッドオーバーロードを 乱 用 しない オーバーロードを 使 った 単 純 なサンプルコード class overload { public void id(string s){ System.out.println("String"); public void id(integer i){ System.out.println("Integer"); public static void main(string[] args) { overload o = new overload(); o.id("choichoi"); o.id(42); 実 行 例 $ java overload String Integer $ 10
メソッドオーバーロードを 乱 用 しない メソッドのオーバーロード メソッド 名 が 同 じでも 引 数 リストが 異 なれば 異 なるメ ソッド シグネチャで 区 別 される 呼 び 出 すメソッドはコンパイル 時 に 決 まる メソッドのオーバーライド 呼 び 出 すメソッドは 実 行 時 に 決 定 される 11
メソッドオーバーロードを 乱 用 しない メソッド id(int i)を 追 加 class Overload { public void id(string s){ System.out.println("String"); public void id(integer i){ System.out.println("Integer"); public void id(int i){ System.out.println("int"); public static void main(string[] args) { Overload o = new Overload(); o.id("choichoi"); o.id(42); 追 加 されたメソッド 実 行 例 $ java Overload String int $ 呼 び 出 すメソッドが 変 わっ てしまった! 12
メソッドオーバーロードを 乱 用 しない メソッドのオーバーロードを 乱 用 すると 動 作 が 分 かりにくく 誤 解 を 招 く デバッグしにくい コードの 保 守 が 困 難 になる 13
メソッドオーバーロードを 乱 用 しない public class Overloader { private static String display(arraylist<integer> arrlist) { return "ArrayList"; private static String display(linkedlist<string> llist) { return "LinkedList"; private static String display(list<?> list) { return "List is not recognized"; public static void main(string[] args) { List<?>[] invokeall = new List<?>[] { new ArrayList<Integer>(), new LinkedList<String>(), new Vector<Integer>() ; for (List<?> i : invokeall) { System.out.println(display(i)); コンパイル 時 の 型 はList 3つ 全 てについて List is not recognized と 出 力 される 違 反 コード display()メソッドがオーバ ーロードされている 14
メソッドオーバーロードを乱用しない public class Overloader { 適合コード private static String display(list<?> l) { return (l instanceof ArrayList? "Arraylist" : (l instanceof LinkedList? "LinkedList" : "List is not recognized")); 実行時に引数の型を public static void main(string[] args) { List<?>[] invokeall = new List<?>[] { new ArrayList<Integer>(), new LinkedList<String>(), new Vector<Integer>() ; for (List<?> i : invokeall) { System.out.println(display(i)); 15 識別するには instanceof()を使う
メソッドオーバーロードを乱用しない // Effective Java, 項目 41 違反コード public class SetList { public static void main(string[] args){ Set<Integer> set = new TreeSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i=-3; i<3; i++){ set.add(i); list.add(i); for (int i=0; i<3; i++){ set.remove(i); list.remove(i); System.out.println(set + " " + list); 16 ArrayListには動作の異なる2つのremove() メソッドが提供されている
メソッドオーバーロードを 乱 用 しない 実 行 例 $ java SetList [-3, -2, -1] [-2, 0, 2] $ [-3,-2,-1] [-3,-2,-1]にならないのはなぜ? 17
メソッドオーバーロードを乱用しない set.remove(i)の動作 [-3,-2,-1,0,1,2] [-3,-2,-1,1,2] set.remove(0) (int)0がautoboxingにより (Integer)0になる set.remove(1) (int)1がautoboxingにより (Integer)1になる [-3,-2,-1,2] set.remove(2) (int)2がautoboxingにより (Integer)2になる [-3,-2,-1] 18
メソッドオーバーロードを乱用しない list.remove(i)の動作 [-3,-2,-1,0,1,2] [-2,-1,0,1,2] list.remove(0) remove(int)が呼び出される list.remove(1) remove(int)が呼び出される [-2,0,1,2] list.remove(2) remove(int)が呼び出される [-2,0,2] 19
メソッドオーバーロードを 乱 用 しない // Effective Java, 項 目 41 public class SetList { public static void main(string[] args){ Set<Integer> set = new TreeSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); 適 合 コード for (int i=-3; i<3; i++){ set.add(i); list.add(i); for (int i=0; i<3; i++){ set.remove(i); list.remove((integer)i); // あるいは (Integer.ValueOf(i)) System.out.println(set + " " + list); 20
まとめ オーバーロードを 乱 用 するとコードの 可 読 性 が 下 がり メソッドを 誤 用 する 危 険 が 増 す なるべくオーバーロードを 避 け 異 なる 名 前 のメ ソッドを 実 装 するほうが 安 全 Effective Java, 項 目 41 21
セキュリティ 22
privateのフィールドやメソッドにアクセス? public class Example { private int i = 3; private int j = 4; private void zeroi() { this.i = 0; Example e = new Example(); System.out.println("" + e.i); e.i = 10; e.zeroi(); 他のクラスからprivateのiやj zeroi()にアクセスできる 23
リフレクション http://docs.oracle.com/javase/jp/6/api/java/lang/reflect/package-summary.html 24
リフレクションを 使 うと try { Class<Example> c = Example.class; Example example = new Example(); Field field = c.getdeclaredfield("i"); field.setaccessible(true); System.out.println("" + field.get(example)); catch (Exception ex) { ex.printstacktrace(system.out); privateのフィールドに アクセスできる setaccessibleを 有 効 にするとリフレクションを 使 っ て 通 常 アクセスできないところにアクセスできる 25
リフレクションを 使 うと try { Class<Example> c = Example.class; Example example = new Example(); Method method = c.getdeclaredmethod("zeroi"); method.setaccessible(true); Object ret = method.invoke(example); Field field = c.getdeclaredfield("i"); field.setaccessible(true); privateのメソッドにも アクセスできる System.out.println("" + field.get(example)); catch (Exception ex) { ex.printstacktrace(system.out); 26
リフレクション セキュリティマネージャで 制 限 することができる 27
セキュリティマネージャ http://docs.oracle.com/javase/jp/6/api/java/lang/securitymanager.html 28
Java : セキュリティモデル サンドボックスによって保護されている セキュリティポリシーにもとづいて操作を許可する 許可されていない操作をすると例外が発生 例えば Javaアプレットは ローカルのリソースにはアクセス出来ない ダウンロード元のサーバとのみ通信可 29
セキュリティマネージャを 使 う 違 反 コード import java.util.hashtable; class SensitiveHash { Hashtable<Integer,String> ht = new Hashtable<Integer,String>(); public void removeentry(object key) { ht.remove(key); // 中 略 removeentry()がpublic 悪 意 ある 攻 撃 者 が 自 由 に 呼 び 出 せる 30
セキュリティマネージャを 使 う import java.util.hashtable; 適 合 コード class SensitiveHash { private Hashtable<Integer,String> ht = new Hashtable<Integer,String>(); public final void removeentry(object key) { check("removekeypermission"); ht.remove(key); private void check(string directive) { SecurityManager sm = System.getSecurityManager(); if (sm!= null) { sm.checksecurityaccess(directive); // 中 略 31
セキュリティポリシーファイル 指 定 した 署 名 で 署 名 されているクラスに 許 可 を 与 える 指 定 したディレクトリからロードされたクラスに 許 可 を 与 える grant SignedBy hogehoge codebase "file:${user.dir/sensitive" { ; permission java.security.securitypermission "removekeypermission"; http://docs.oracle.com/javase/jp/6/technotes/guides/security/spec/security-spec.doc3.html#20128 32
policytool 付 属 のPolicy Toolユーティリティを 使 用 して ポ リシーファイルを 作 成 することもできる http://docs.oracle.com/javase/jp/6/technotes/guides/security/policyguide.html 33
実 行 してみる $ java -Djava.security.manager -Djava.security.policy=my.policy - classpath./sensitive:./ UseHash 34
セキュリティマネージャを 使 う import java.util.hashtable; import java.security.accesscontroller; import java.security.securitypermission; 適 合 コード class SensitiveHash { private Hashtable<Integer,String> ht = new Hashtable<Integer,String>(); public final void removeentry(object key) { check("removekeypermission"); ht.remove(key); private void check(string directive) { SecurityPermission sp = new SecurityPermission(directive); AccessController.checkPermission(sp); // 中 略 AccessController#checkPermissionを 呼 び 出 すこの 方 法 が 推 奨 されている 35
ファイナライザ 36
finalize()メソッド クラス Object には, finalize と呼ばれる protected メソッドが用意されており, 他のクラス からこのメソッドをオーバーライドすることがで きる あるオブジェクトに対して起動可能な特定 の finalize 定義は, そのオブジェクトのファイナラ イザ(finalizer)と呼ばれる Java言語仕様第3版 12.6 クラス インスタンスのファイナライズ 37
finalize()メソッド public class Object {...... protected void finalize() throws Throwable...... JavaSE6 API仕様 クラスObject, finalizeメソッドの説明 38
finalize()メソッドを 使 わない finalizeメソッドの 利 用 に 関 しては 数 々の 問 題 が 存 在 するため その 利 用 は 例 外 的 場 合 に 限 るべき 実 行 に 関 して 無 保 証 並 行 実 行 の 可 能 性 例 外 の 扱 い リソース 一 般 の 後 処 理 には 使 えない 39
実 行 されるかどうか 無 保 証 finalizeメソッドは 実 行 されるとは 限 らない メモリに 余 裕 があればGCは 働 かない finalize メソッドも 実 行 されない オブジェクトの 状 態 をファイルに 保 存 するなどの 終 了 処 理 をfinalizeメソッドで 実 行 してはいけない 実 行 タイミングが 重 要 な 処 理 をfinalizeメソッドで 実 行 してはいけない 40
実行順序や並行実行の可能性, 例外の扱い finalizeメソッドの実行順序 Java言語仕様第3版 12.6.2 ファイナライザの起動は順序付けられていない 複数の(オブジェクトの)finalizeメソッドの実行順序は指定できない 複数の(オブジェクトの)finalizeメソッドが並列に実行されるかも finalizeメソッドのなかからスローされた例外は無視される finalize メソッドの実行自体は中断 41
リソース 一 般 の 後 処 理 には 使 えない finalizeメソッドとリソース 管 理 finalizeメソッドの 実 行 はメモリの 使 用 状 況 に 依 存 メモリ 以 外 のリソースの 空 き 状 況 は 関 係 しない 結 果 : 空 きメモリが 潤 沢 でも 他 のリソースが 枯 渇 する 可 能 性 GCが 実 行 されない finalizeメソッドも 実 行 されない DoS 攻 撃 の 危 険 性 注 意!! finalizeメソッドは C++ の destructor とは 違 います 42
リソース 一 般 の 後 処 理 には 使 えない リソース 一 般 の 後 処 理 には Closeable インタフェースと try-withresources 構 文 を 活 用 すべき リソース... 使 用 開 始 時 にオープン/ 使 用 終 了 時 にクローズするもの ストリームのclose メソッド, Timerのcancel メソッド, Graphics の dispose メソッドなど Closeable インタフェースを 実 装... close() メソッドを 持 っている try-with-resources て 東 京 セミナ part3 でちらっと 紹 介 したよね! 43
finalize()を 使 う 場 合 の 注 意 点 時 間 のかかる 処 理 を 行 うべきではない 明 示 的 に 行 うべきクローズ 処 理 の 最 終 チェック 手 段 として 使 う finalize()メソッドをオーバライドする 場 合, 親 クラスのfinalize() 呼 び 出 しを 忘 れずに 行 う 44
finalizer guardian サブクラスで finalize() メソッドをオーバライドして いる 状 況 で, 親 クラスの finalize() 呼 び 出 しを 保 証 す るための 手 法 public class Foo { private final Object finalizerguardian = new Object() { @Override protected void finalize() throws Throwable {... 外 側 のオブジェクトをファイナライズする... ;... 45
ファイナライザに 関 連 するコーディングルール FIO04-J. 不 要 になったらリソースを 解 放 する FIO14-J. プログラムの 終 了 時 には 適 切 なクリーンアップ を 行 う MET12-J. ファイナライザは 使 わない 46
攻 撃 手 法 の 紹 介 : ファイナライザー 攻 撃 47
概 要 ライセンス 認 証 を 行 うサンプルアプリケーションに 対 し Java コードを 追 加 するだけで 認 証 回 避 を 行 う 攻 撃 手 法 finalize()メソッドを 悪 用 するため ファイナライザー 攻 撃 と 呼 ばれる 手 法 48
サンプルアプリケーションの 構 成 アプリ 本 体 : Application クラス アプリ 本 体 の 実 行 に 先 立 って ライセンス 認 証 を 行 う LicenseManagerクラス: ライセンス 情 報 の 確 認 SecuritySystemクラス: 認 証 完 了 したことを 記 録 LicenseManager コンストラクタ 中 に ライセンス 確 認 処 理 SecuritySystem LicenseManagerの インスタンスを 登 録 Application 本 体 実 行 49
サンプルアプリケーション(1/2) public class LicenseManager { public LicenseManager() { if (!licensevalidation()) { throw new SecurityException("License Invalid!"); private boolean licensevalidation() { // ライセンスファイルをリードしてチェックし ライセンスが正当ならtrueを返す return false; ここでは必ず認証失敗するようなコードにしている public class SecuritySystem { private static LicenseManager licensemanager = null; public static void register(licensemanager lm) { // licensemanagerが初期化されていない場合のみ登録 if (licensemanager == null) { if (lm == null) { System.out.println("License Manager invalid!"); System.exit(1); licensemanager = lm; Heinz M. Kabutz. Exceptional Constructors - Ressurecting the dead. Java Specialists Newsletter. 2001 50
サンプルアプリケーション(2/2) public class Application { public static void main(string[] args) { LicenseManager lm; try { lm = new LicenseManager(); catch(securityexception ex) { lm = null; SecuritySystem.register(lm); System.out.println("Now let s get things started"); 正 しいライセンス 情 報 を 持 っていないと... LicenseManagerのインスタンス 生 成 時 に 例 外 発 生 LicenseManager のインスタンスを 登 録 しないと... SecuritySystem.register() で 例 外 発 生 51
サンプルアプリケーション 実 行 例 % ls *.java Application.java SecuritySystem.java % javac *.java % java Application License Manager invalid! % LicenseManager.java たしかに 認 証 失 敗 している 52
サンプルアプリケーションを 攻 撃 する 攻 撃 目 的 LicenseManager のセキュリティチェックを 回 避 し Application.main() を 実 行 する 前 提 条 件 これらのクラスはすべて 変 更 できないものとする 攻 撃 方 針 LicenseManagerのサブクラスを 作 成 し 攻 撃 者 のアプリ( 後 述 の AttackerApp)に 脆 弱 なアプリApplicationを 読 み 込 む 問 題 LicenseManagerのサブクラスを 作 っても サブクラスでは 親 クラスでスローされる 例 外 をキャッチできない 53
親 クラスでスローされる 例 外 を 悪 用 できれば public class MyApplication { public static void main(string[] args) { MyLicenseManager lm; try { lm = new MyLicenseManager(); catch(securityexception ex) { lm = null; SecuritySystem.register(lm); // Applicationのメインメソッドを 呼 ぶ Application.main(args); public class MyLicenseManager extends LicenseManager { public MyLicenseManager() { System.out.println("Created MyLicenseManager"); MyApplication を 実 行 すると License Manager invalid! このやり 方 ではうまく 攻 撃 できない 54
finalize()メソッドの 悪 用 SecuritySystem に LicenseManager のインスタンスを 登 録 できれば 勝 ち しかもサンプルアプリケーションではLicenseManagerの インスタンスの 中 身 はチェックしていない LicenseManagerのインスタンス 欲 しい ライセンス 情 報 を 持 っていない 場 合 コンストラクタ 実 行 中 に 例 外 が 発 生 するので 生 成 途 中 で 捨 てられている finalize()を 使 えば GCされる 直 前 に 拾 うことができる! 55
ファイナライザー 攻 撃 を 行 うコード public class LicenseManagerInterceptor extends LicenseManager { private static LicenseManagerInterceptor instance = null; public static LicenseManagerInterceptor make() { try { new LicenseManagerInterceptor(); catch(exception ex) { // 例 外 を 無 視 try { synchronized(licensemanagerinterceptor.class) { while (instance == null) { System.gc(); LicenseManagerInterceptor.class.wait(100); catch(interruptedexception ex) { return null; return instance; public void finalize() { System.out.println("In finalize of " + this); synchronized(licensemanagerinterceptor.class) { instance = this; LicenseManagerInterceptor.class.notify(); public LicenseManagerInterceptor() { System.out.println("Created LicenseManagerInterceptor"); finalize()メソッド を 追 加 攻 撃 コード 56
ファイナライザー 攻 撃 を 行 うコード 攻 撃 コード public class AttackerApp { public static void main(string[] args) { LicenseManagerInterceptor lm = LicenseManagerInterceptor.make(); SecuritySystem.register(lm); // now we call the other application Application.main(args); LicenseManagerInterceptor.make()の 返 り 値 は GC 直 前 に 拾 い 上 げたLicenseManagerInterceptor のインスタンス 57
ファイナライザ 攻 撃 の 流 れ LicenseManagerInterceptor 親 クラスLicenseManagerの ライセンス 確 認 処 理 で 例 外 発 生 例 外 を 捕 捉 し GCが finalize()メソッドを 起 動 するのを 待 つ 初 期 化 途 中 だったLicenseManager のインスタンスを 取 得 やがて GCによって finalize() 起 動 SecuritySystem LicenseManagerの インスタンスを 登 録 Application 本 体 実 行 SecuritySystemにはすでに LicenseManagerのインスタンスが 登 録 されているため ライセンス 認 証 は 完 了 したと 思 わされる 58
攻 撃 コード 実 行 例 % ls Application.class LicenseManager.class SecuritySystem.class AttackerApp.java LicenseManagerInterceptor.java % javac *.java % java AttakerApp In finalize of LicenseManagerInterceptor@7dcb3cd Now let s get things started % 59
ファイナライザ 攻 撃 対 策 finalize()メソッドを 上 書 きされないように 定 義 重 要 なインスタンスは 初 期 化 の 完 了 を 必 ず 確 認 サブクラス 化 による 悪 用 を 防 ぐため クラスをfinal 宣 言 60