2012-04-17 14 views
9

でのアドバイスの実行を避け、私はAuthenticator.authenticate方法が重要である側面は無効化/ AspectJの

public aspect Hack { 

pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass); 

boolean around(String user, String pass): authHack(user,pass) { 
    out("$$$ " + user + ":" + pass + " $$$"); 
    return false; 
} 

} 

があるとします。ハックはこのメソッドへの呼び出しを傍受します。

ハックのアドバイスのauthHackのアドバイスをキャンセル/無効にする第2の側面を書くことはできますか?

..

+0

大きな質問です。読んでいる人には、Yaneeveは素晴らしい答えを出しますが、そこで止まらないでください。答えkreigaexはそれを実にして、それをさらに一歩進めます。 – cb4

答えて

9

自分の状況をシミュレートするためには、私が書いた次認証コード:

public class Authenticator { 

    public boolean authenticate(String user, String pass) { 
     System.out.println("User: '" + user + "', pass: '" + pass + "'"); 
     return true; 
    } 

} 

これは私のメインクラスです。

public class Main { 

    public static void main(String[] args) { 

     Authenticator authenticator = new Authenticator(); 

     boolean status = authenticator.authenticate("Yaneeve", "12345"); 
     System.out.println("Status: '" + status + "'"); 
    } 

} 

出力は:

User: 'Yaneeve', pass: '12345' 
Status: 'true' 
public aspect HackTheHack { 

    declare precedence: "HackTheHack", "Hack"; 

    pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass); 

    boolean around(String user, String pass): authHack(user,pass) { 
     boolean status = false; 
     try { 
      Class<?> klass = Class.forName("Authenticator"); 
      Object newInstance = klass.newInstance(); 
      Method authMethod = klass.getDeclaredMethod("authenticate", String.class, String.class); 
      status = (Boolean) authMethod.invoke(newInstance, user, pass); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } catch (NoSuchMethodException e) { 
      e.printStackTrace(); 
     } catch (SecurityException e) { 
      e.printStackTrace(); 
     } catch (IllegalAccessException e) { 
      e.printStackTrace(); 
     } catch (IllegalArgumentException e) { 
      e.printStackTrace(); 
     } catch (InvocationTargetException e) { 
      e.printStackTrace(); 
     } catch (InstantiationException e) { 
      e.printStackTrace(); 
     } 
     return status; 
    } 
} 
:私は、次のHackTheHackアスペクトを作成していた

:解決のための今すぐ

$$$ Yaneeve:12345 $$$ 
Status: 'false' 

public aspect Hack { 

    pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass); 

    boolean around(String user, String pass): authHack(user,pass) { 
     System.out.println("$$$ " + user + ":" + pass + " $$$"); 
     return false; 
    } 
} 

今すぐ出力がある:私はあなたのハックの様相を追加しました

出力が再びあります:

User: 'Yaneeve', pass: '12345' 
Status: 'true' 

ハックの局面では、元のポイントカットは実行が実際に反射をキャッチして「実行」「呼び出し」としなかった場合のみ有効です。

説明:

私はハック前HackTheHackを呼び出すためにアスペクトの優先順位を使用した:

declare precedence: "HackTheHack", "Hack"; 

I次いで反射を使用する(注及び方法の繰り返しのルックアップを低減するように最適化されるべきであることができる)、単純にハックアラウンドのアドバイスなしで元のメソッドを呼び出す。

  1. authHackポイントカット:これは2つのことを可能に行われていた​​(両方の面で)用途call()代わりのexecution()
  2. 私はHackTheHack
  3. に私は希望

proceed()を呼び出していませんでした正しいトラックに私を入れたManning's AspectJ in Action, Second Editionにあなたを紹介する:

6.3.1アドバイスの注文

これまで見てきたように、複数のアスペクトがシステムに存在する場合、さまざまな側面のアドバイスは、単一のジョインポイントに適用されることがあります。 この場合、AspectJは アドバイスが適用される順序を決定するために次の優先ルールを使用します。後で、優先順位を制御する方法が表示されます。

1優先順位の高いアスペクトは、優先順位の低いアスペクトの前の結合 ポイントのbeforeアドバイスを実行します。

2優先順位の高いアスペクトは、優先順位の低い アスペクトの後のジョインポイントでアフターアドバイスを実行します。

3優先度の高い側のaroundアドバイスは、下位優先度の のaroundアドバイスを囲みます。この種の構成は、より高い優先順位のアドバイスが、優先順位の低いアドバイスがproceed()の呼び出しを制御することによって実行するかどうかを制御することを可能にする。 より高い優先順位の アスペクトがそのアドバイス本体でproceed()を呼び出さない場合、 下位優先順位のアスペクトは実行されませんが、アドバイスされたジョインポイント も実行されません。

+0

非常に良い答えです。私はいくつか質問があります:1) '優先順位を宣言しています:" HackTheHack "、" Hack ";'必要な2) 'HackTheHack'をすべての面を先読みに設定できますか? 3)ハックが '実行 'を使用したらどうなるでしょうか? – emesx

+1

@elmesありがとう)。あなたの質問に答える。 1)YES。 2)私はそう思う...次の宣言が私のために働いた、それを試してください:優先順位を宣言する: "HackTheHack"、 "*"; 3)それは動作しません。もしHackTheHackがexecution()を使っていれば無限のコールループになります。もしHackTheHackがcall()(そしてHack execution())を使っていれば、それはちょうどあなたが望んでいないHackのコードを呼び出しています。 .. – Yaneeve

+1

基本的にAspectJにはいくつかの制限があります。単純に '実行'を使う 'Hack'を変更すると、* immortal * ..;)になります。 – emesx

-1

を私はaround authHackアドバイスの実行をキャッチすることができますが、私は、認証を継続したい場合、私は再びAuthenticator.authenticateを呼び出す必要があり、これは無限ループを作成し、私はあなたが進む()が欠けていると思いますコール。あなたはおそらくしたいことは、このようなものです:

public aspect Hack { 

    pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass); 

    boolean around(String user, String pass): authHack(user,pass) { 
     out("$$$ " + user + ":" + pass + " $$$"); 
     boolean result = proceed(user,pass); 
     return result; 
    } 

} 
+1

* authHackのアドバイスをキャンセル/無効にする第2の側面を書く*ハックを修正しないようにする – Queequeg

+0

@Queequeg私はあなたが私の答えを落としたことを理解していない。あなたがより良い代替手段を提供していないことを確認してください。 – anjosc

+0

@anjosc、Queequegが書いたように、あなたの答えは質問の全体を逃してしまいます。 authHackアドバイスをハックを修正しないように取り消す/無効にする第2の態様 "である。ところで、個人的な経験から、それが悪い理由であるとコメントするのに十分親切だった人よりも、あなたの答えを落としていた人は違うかもしれません。ここの運が良かったのです。 – Yaneeve

3

実際、ユーザー@Yaneeveは素晴らしい解決策を提示しましたが、いくつかの欠点があります。それだけで

  • ニーズの反映、事前に
  • ニーズdeclare precedence、ハックのクラスとパッケージ名を知っている
  • ニーズ(大丈夫、それは使用することによって回避することができ、ないexecution()ため、call()のために働く

    • 優先順位宣言では*)。

    私はより安定したソリューションを提供しています。私は少しより現実的であるためにソースコードを変更した:

    認証:

    は、オーセンティケータは、ユーザのデータベースを有している(簡単にするためにハードコードされた)実際のユーザとパスワードとを比較します。

    package de.scrum_master.app; 
    
    import java.util.HashMap; 
    import java.util.Map; 
    
    public class Authenticator { 
        private static final Map<String, String> userDB = new HashMap<>(); 
    
        static { 
         userDB.put("alice", "aaa"); 
         userDB.put("bob", "bbb"); 
         userDB.put("dave", "ddd"); 
         userDB.put("erin", "eee"); 
        } 
    
        public boolean authenticate(String user, String pass) { 
         return userDB.containsKey(user) && userDB.get(user).equals(pass); 
        } 
    } 
    

    アプリケーション:

    アプリケーションは、エントリポイントを有しており、結果を印刷する、いくつかのユーザを認証しようとする:

    package de.scrum_master.app; 
    
    public class Application { 
        public static void main(String[] args) { 
         Authenticator authenticator = new Authenticator(); 
         System.out.println("Status: " + authenticator.authenticate("alice", "aaa")); 
         System.out.println("Status: " + authenticator.authenticate("bob", "xxx")); 
         System.out.println("Status: " + authenticator.authenticate("dave", "ddd")); 
         System.out.println("Status: " + authenticator.authenticate("erin", "xxx")); 
         System.out.println("Status: " + authenticator.authenticate("hacker", "xxx")); 
        } 
    } 
    

    次のようにアプリケーションの出力である:

    Status: true 
    Status: false 
    Status: true 
    Status: false 
    Status: false 
    

    認証ロガー側面:

    私は後でちょうどハッキング側面と同様に、認証方法にaround()アドバイスを法的側面を追加します。

    package de.scrum_master.aspect; 
    
    import de.scrum_master.app.Authenticator; 
    
    public aspect AuthenticationLogger { 
        pointcut authentication(String user) : 
         execution(boolean Authenticator.authenticate(String, String)) && args(user, *); 
    
        boolean around(String user): authentication(user) { 
         boolean result = proceed(user); 
         System.out.println("[INFO] Authentication result for '" + user + "' = " + result); 
         return result; 
        } 
    } 
    

    出力は次のようになります。

    [INFO] Authentication result for 'alice' = true 
    Status: true 
    [INFO] Authentication result for 'bob' = false 
    Status: false 
    [INFO] Authentication result for 'dave' = true 
    Status: true 
    [INFO] Authentication result for 'erin' = false 
    Status: false 
    [INFO] Authentication result for 'hacker' = false 
    Status: false 
    

    あなたが見ることができるように、「ステータス」と「認証結果は」限り、システムがハッキングされていなかったものと同じです。ここで驚きはありません。

    ハッカーアスペクト:

    は、今私たちがシステムをハックしてみましょう。私たちはいつでも、特定のユーザーのために常に真(正の認証結果)を返すか、常に真を返すことができます。私たちも、proceed()元のコールに、私たちはその副作用を持つようにしたいことができれば、我々はまだ、常に私たちは、この例では何をすべきかである、trueを返すことができます。

    package de.scrum_master.hack; 
    
    import de.scrum_master.app.Authenticator; 
    
    public aspect Hack { 
        declare precedence : *, Hack; 
        pointcut authentication() : 
         execution(boolean Authenticator.authenticate(String, String)); 
    
        boolean around(): authentication() { 
         System.out.println("Hack is active!"); 
         proceed(); 
         return true; 
        } 
    } 
    

    出力変化に:

    Hack is active! 
    [INFO] Authentication result for 'alice' = true 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'bob' = true 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'dave' = true 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'erin' = true 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'hacker' = true 
    Status: true 
    

    ヒープのアスペクトは、アドバイスの優先順位の最後のものであると宣言しています(つまり、ネストされたシリーズ内の最も内側のシェルが同じジョインポイントで呼び出されると、その戻り値はロガーのアスペクトに伝達されます。なぜなら、ロガーは、既に操作された認証結果内部から受け取っています。

    我々はdeclare precedence : Hack, *;に宣言を変更する場合は、次のように出力されます

    Hack is active! 
    [INFO] Authentication result for 'alice' = true 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'bob' = false 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'dave' = true 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'erin' = false 
    Status: true 
    Hack is active! 
    [INFO] Authentication result for 'hacker' = false 
    Status: true 
    

    すなわち、ロガーは元の結果をログに記録し、ハッカーのアスペクトに伝播します。ハッカーのアスペクトは、最初に優先順位があり、したがってコールチェーン全体を制御しているため、最後に処理できます。最終的な発言は、ハッカーが通常望むものですが、この場合、ログに記録されているもの(一部の認証は真、一部は誤り)とアプリケーションの実際の動作(ハッキングされているため常に真実)との間に不一致が見られます。

    アンチハッカーの様相:

    、最後のではなく、少なくとも、我々はアドバイスの実行を傍受し、それらが可能なハッカーの側面から来るかもしれないかどうかを判断します。良いニュースは:AspectJにはadviceexecution()という名前のポイントカットがあります。:-)

    アドバイスの実行ジョインポイントには、thisJoinPoint.getArgs()で確認できる引数があります。残念ながら、AspectJはargs()でパラメータにバインドできません。インターセプトされたアドバイスがaround()タイプの場合、最初のadviceexecution()パラメータはAroundClosureオブジェクトになります。このクロージャーオブジェクトに対してrun()メソッドを呼び出して、正しい引数(getState()で確定できます)を指定すると、実際のアドバイスボディは実行されず、暗黙的にproceed()が呼び出されます。これにより、傍受されたアドバイスが事実上無効になります!

    package de.scrum_master.aspect; 
    
    import org.aspectj.lang.SoftException; 
    import org.aspectj.runtime.internal.AroundClosure; 
    
    public aspect AntiHack { 
        pointcut catchHack() : 
         adviceexecution() && ! within(AntiHack) && !within(AuthenticationLogger); 
    
        Object around() : catchHack() { 
         Object[] adviceArgs = thisJoinPoint.getArgs(); 
         if (adviceArgs[0] instanceof AroundClosure) { 
          AroundClosure aroundClosure = (AroundClosure) adviceArgs[0]; 
          Object[] closureState = aroundClosure.getState(); 
          System.out.println("[WARN] Disabling probable authentication hack: " + thisJoinPointStaticPart); 
          try { 
           return aroundClosure.run(closureState); 
          } catch (Throwable t) { 
           throw new SoftException(t); 
          } 
         } 
         return proceed(); 
        } 
    } 
    

    出力結果は次のとおりです。

    [WARN] Disabling probable authentication hack: adviceexecution(boolean de.scrum_master.hack.Hack.around(AroundClosure)) 
    [INFO] Authentication result for 'alice' = true 
    Status: true 
    [WARN] Disabling probable authentication hack: adviceexecution(boolean de.scrum_master.hack.Hack.around(AroundClosure)) 
    [INFO] Authentication result for 'bob' = false 
    Status: false 
    [WARN] Disabling probable authentication hack: adviceexecution(boolean de.scrum_master.hack.Hack.around(AroundClosure)) 
    [INFO] Authentication result for 'dave' = true 
    Status: true 
    [WARN] Disabling probable authentication hack: adviceexecution(boolean de.scrum_master.hack.Hack.around(AroundClosure)) 
    [INFO] Authentication result for 'erin' = false 
    Status: false 
    [WARN] Disabling probable authentication hack: adviceexecution(boolean de.scrum_master.hack.Hack.around(AroundClosure)) 
    [INFO] Authentication result for 'hacker' = false 
    Status: false 
    

    あなたが見ることができるように、

    • 結果は
    • 、つまり私たちはそれを効果的に無効にし、今ハッカーの様相ない場合と同じです
    • ハッカーのアスペクトのクラスまたはパッケージ名を知る必要はありませんでしたが、私たちのcatchHack()ポイントカットでは、既知のアスペクトのホワイトリストを指定していますこれbefore()after()アドバイスがAroundClosure Sなしの署名を持っているので、ない無効にすること、すなわち実行変わらず、
    • 我々は唯一around()アドバイスをターゲットにしている必要があります。ターゲットメソッドヒューリスティックと

    アンチハッカーのアドバイス:

    残念ながら、私の周りの閉鎖の対象となる方法を決定する方法を発見したので、アドバイスを防止ハッカーのアドバイスの範囲を限定する正確な方法はありません具体的には、ハッキングから保護する方法をターゲットにしています。この例では、ヒューリスティック(我々はそれがAuthenticatorインスタンスであるかどうかを確認する必要があります)最初のパラメータとして

    • アドバイスの対象オブジェクトで構成されていAroundClosure.getState()によって返される配列の内容をチェックすることにより、範囲を絞り込むことができ、
    • ターゲットメソッド呼び出しのパラメータ(Authenticator.authenticate()の場合、2つのStringが必要です)。

    この知識はドキュメント化されていません(アドバイスの実行の引数の内容と同じです)。試行錯誤でわかりました。とにかく、この変更は、ヒューリスティックを有効にします。

    package de.scrum_master.aspect; 
    
    import org.aspectj.lang.SoftException; 
    import org.aspectj.runtime.internal.AroundClosure; 
    
    import de.scrum_master.app.Authenticator; 
    
    public aspect AntiHack { 
        pointcut catchHack() : 
         adviceexecution() && ! within(AntiHack) && !within(AuthenticationLogger); 
    
        Object around() : catchHack() { 
         Object[] adviceArgs = thisJoinPoint.getArgs(); 
         if (adviceArgs[0] instanceof AroundClosure) { 
          AroundClosure aroundClosure = (AroundClosure) adviceArgs[0]; 
          Object[] closureState = aroundClosure.getState(); 
          if (closureState.length == 3 
            && closureState[0] instanceof Authenticator 
            && closureState[1] instanceof String 
            && closureState[2] instanceof String 
          ) { 
           System.out.println("[WARN] Disabling probable authentication hack: " + thisJoinPointStaticPart); 
           try { 
            return aroundClosure.run(closureState); 
           } catch (Throwable t) { 
            throw new SoftException(t); 
           } 
          } 
         } 
         return proceed(); 
        } 
    } 
    

    出力は上記と同じまま、しかし、ハッカーの局面では、複数のアドバイスや複数のハッカーの側面がある場合、あなたは違いが表示されます。このバージョンはスコープを絞り込んでいます。もしあなたがこれを望むなら、それはあなた次第です。私はあなたがより単純なバージョンを使用することをお勧めします。その場合、常に最新のホワイトリストを持つようにポイントカットを更新するように注意するだけで済みます。

    大変申し訳ありませんが、私はこの問題を魅力的であると判断し、解決策をできるだけ良く説明しようとしました。

  • +0

    優れた答えです。受け入れられた答えの後に読書を止める者は、治療を欠いている! – cb4

    関連する問題