ニーズ(大丈夫、それは使用することによって回避することができ、ない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();
}
}
出力は上記と同じまま、しかし、ハッカーの局面では、複数のアドバイスや複数のハッカーの側面がある場合、あなたは違いが表示されます。このバージョンはスコープを絞り込んでいます。もしあなたがこれを望むなら、それはあなた次第です。私はあなたがより単純なバージョンを使用することをお勧めします。その場合、常に最新のホワイトリストを持つようにポイントカットを更新するように注意するだけで済みます。
大変申し訳ありませんが、私はこの問題を魅力的であると判断し、解決策をできるだけ良く説明しようとしました。
大きな質問です。読んでいる人には、Yaneeveは素晴らしい答えを出しますが、そこで止まらないでください。答えkreigaexはそれを実にして、それをさらに一歩進めます。 – cb4