In realtà l'utente @Yaneeve ha presentato una soluzione piacevole, ma presenta alcune carenze, ad es. si
Ho una soluzione più stabile per voi. Ho modificato il codice sorgente per essere un po 'più realistico:
L'autenticatore ha un database utente (hard-coded per semplicità) e in realtà a confronto utenti e password.
L'applicazione dispone di un punto di ingresso e cerca di autenticare alcuni utenti, la stampa dei risultati:
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;
}
}
L'uscita diventa:
[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
Come si può vedere, "stato" e "risultato dell'autenticazione" sono gli stessi fino a quando il sistema non è stato violato. Nessuna sorpresa qui.
Hacker aspetto:
Ora cerchiamo di hackerare il sistema. Possiamo sempre restituire true (risultato di autenticazione positivo) o sempre vero per un determinato utente, qualunque cosa ci piaccia. Possiamo anche proceed()
alla chiamata originale, se vogliamo avere i suoi effetti collaterali, ma possiamo ancora sempre ritornare vero, che è quello che facciamo in questo esempio:
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;
}
}
L'uscita a:
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
Poiché l'aspetto dell'hacker si dichiara essere l'ultimo in precedenza di consulenza (ovvero la shell più interna in una serie annidata di chiamate proceed()
sullo stesso joinpoint, il suo valore di ritorno verrà propagato nella catena di chiamate all'aspetto del logger, che è il motivo per cui il logger stampa il risultato dell'autenticazione già manipolato dopo lo ha ricevuto dall'aspetto interno.
Se cambiamo la dichiarazione declare precedence : Hack, *;
l'uscita è la seguente:
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
Vale a dire il logger ora registra il risultato originale e lo diffonde nella catena di chiamate all'aspetto dell'hacker che può manipolarlo proprio alla fine perché è il primo nella precedenza e quindi nel controllo dell'intera catena di chiamate. Avere l'ultima parola è ciò che un hacker di solito vorrebbe, ma in questo caso mostrerebbe una discrepanza tra ciò che viene registrato (alcune autenticazioni vere, alcune false) e il comportamento effettivo dell'applicazione (sempre vero perché è stato violato).
Anti aspetto degli hacker:
Ora, ultimo ma non meno importante che vogliamo intercettare le esecuzioni di consulenza e determinare se essi potrebbero provenire da possibili aspetti di hacker. La buona notizia è: AspectJ ha un punto di riferimento chiamato adviceexecution()
- nomen est omen.:-)
I punti di join dell'esecuzione di un consiglio hanno argomenti che possono essere determinati tramite thisJoinPoint.getArgs()
. Sfortunatamente AspectJ non può collegarli ai parametri tramite args()
. Se il suggerimento intercettato è di tipo around()
, il primo parametro adviceexecution()
sarà un oggetto AroundClosure
. Se si chiama il metodo run()
su questo oggetto di chiusura e si specificano gli argomenti corretti (che possono essere determinati tramite getState()
), l'effetto è che il corpo del consiglio effettivo non verrà eseguito, ma verrà chiamato solo implicitamente proceed()
. Ciò disattiva efficacemente il consiglio intercettato!
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();
}
}
L'output risultante è:
[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
Come si può vedere,
- il risultato è ora lo stesso senza l'aspetto di hacker, vale a dire che in effetti disabilitato esso,
- non era necessario conoscere la classe dell'aspetto hacker o il nome del pacchetto, ma nel nostro pointcut
catchHack()
si specifica una lista bianca di aspetti noti che dovrebbe non essere disabilitate, cioè correre invariato,
- siamo solo di mira
around()
consiglio, perché before()
e after()
consigli hanno le firme senza AroundClosure
s.
Anti consiglio degli hacker con il metodo di destinazione euristica:
Purtroppo non ho trovato alcun modo per determinare il metodo di mira dalla chiusura in giro, quindi non c'è modo esatto per limitare il campo di applicazione di anti del consiglio hacker di consulenza in particolare il targeting del metodo che vogliamo proteggere dall'hacking. In questo esempio si può restringere l'ambito dal euristicamente controllare il contenuto della matrice restituita da AroundClosure.getState()
che consiste
- oggetto di destinazione del consiglio come primo parametro (bisogna verificare se è un'istanza
Authenticator
),
- i parametri della chiamata del metodo di destinazione (per
Authenticator.authenticate()
ci devono essere due String
s).
Questa conoscenza è priva di documenti (proprio come il contenuto degli argomenti dell'esecuzione del consiglio), l'ho scoperto per tentativi ed errori. In ogni caso, questa modifica consente l'euristica:
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();
}
}
L'uscita rimane lo stesso come sopra, ma se ci sono consigli più sotto l'aspetto di hacker o anche molteplici aspetti di hacker si vedrà la differenza. Questa versione sta riducendo l'ambito. Se vuoi questo o no dipende da te. Ti suggerisco di usare la versione più semplice. In tal caso, devi solo fare attenzione ad aggiornare il pointcut per avere sempre una whitelist aggiornata.
Siamo spiacenti per la risposta lunga, ma ho trovato il problema affascinante e ho cercato di spiegare la mia soluzione nel miglior modo possibile.
Ottima domanda. A quelli che leggono, Yaneeve dà una bella risposta ma non si ferma qui. La risposta fornita da kreigaex lo rende reale e fa un ulteriore passo avanti. – cb4