summaryrefslogtreecommitdiff
path: root/docs/chapters/jwt-attacks.tex
diff options
context:
space:
mode:
Diffstat (limited to 'docs/chapters/jwt-attacks.tex')
-rw-r--r--docs/chapters/jwt-attacks.tex59
1 files changed, 59 insertions, 0 deletions
diff --git a/docs/chapters/jwt-attacks.tex b/docs/chapters/jwt-attacks.tex
new file mode 100644
index 0000000..5a67c4f
--- /dev/null
+++ b/docs/chapters/jwt-attacks.tex
@@ -0,0 +1,59 @@
+Prendiamo ad esempio un'applicazione che si interfaccia ad un backend usando JSON Web Tokens\cite{JWT:1} per la validazione dell'autorizzazione.
+Se prendiamo il token qui sotto
+\begin{lstlisting}
+eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
+eyJ1c2VyX2lkIjoxfQ.
+jYyRJbb0WImFoUUdcslQQfwnXTHJzne-6tsPd8Hrw0I
+\end{lstlisting}
+sappiamo che la prima parte si riferisce all'header:
+\begin{lstlisting}
+$ echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d
+{"alg":"HS256","typ":"JWT"}
+\end{lstlisting}
+che la seconda al payload
+\begin{lstlisting}
+$ echo "eyJ1c2VyX2lkIjoxfQ" | base64 -d
+{"user_id":1}
+\end{lstlisting}
+e la terza all'HS256 \cite{HMACSHA:1} della stringa dei due più un secret. Sappiamo che è HS256 dall'header.
+Questo token quindi è passato come header HTTP alla chiave d'autorizzazione.
+\begin{lstlisting}
+POST /v1/users/2/ HTTP/2
+Host: example.com
+Referer: https://example.com
+Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxfQ.jYyRJbb0WImFoUUdcslQQfwnXTHJzne-6tsPd8Hrw0I
+[...]
+\end{lstlisting}
+
+Ipotizziamo che l'endpoint \underline{https://example.com/v1/users/2/} di questa API che vogliamo attaccare guardi l'autenticazione da un cookie che gli si passa e poi guardi l'autorizzazione per accedere all'utente con quell'ID solo se corrisponde all'utente dell'autorizzazione. L'autorizzazione procederebbe con successo ma arrivando all'autorizzazione e decodificato il JWT avremmo un responso del tipo:
+\begin{lstlisting}
+HTTP/2 401 Unauthorized
+\end{lstlisting}
+Quindi potremmo ritoccare il token perché presupponiamo che il flow di autorizzazione sia proprio quello descritto nel paragrafo sopra.
+
+Se scoprissimo qual è il secret usato per fare l'hashing potremmo creare un nuovo payload:
+\begin{lstlisting}
+$ echo '{"user_id":2}' | base64
+eyJ1c2VyX2lkIjoyfQo=
+\end{lstlisting}
+Il padding ("=") viene omesso in realtà come da standard.
+
+Diamo per buono che il secret sia la stringa \textbf{secret}. Tramite un software di cui il sito JWT.io \cite{JWT:2} stesso predispone si può creare e verificare la firma del token. Alla fine avremo proprio
+\begin{lstlisting}
+eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
+eyJ1c2VyX2lkIjoyfQ.
+9YCOE7tXJFvXEkLKezdd42NArXH6JXLtHbQu-KrwQSA
+\end{lstlisting}
+che, passato alla richiesta, avremo finalmente il responso con i dati dell'utente con ID 2:
+\begin{lstlisting}
+HTTP/2 200 OK
+[...]
+\end{lstlisting}
+Questo potrebbe essere un bell'attacco, peccato però che tutti (spero) i backend che adoperano l'uso di JWT (spesso anche per autenticazione) usano un payload del tipo:
+\begin{lstlisting}
+$ echo "eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjY0NjE1NDcxLCJqdGkiOiIyMGM3Nzk2YTljM2Y0Yjk4YmM3ODdkNDRmNzRiNGE0YyIsInVzZXJfaWQiOjF9" | base64 -d
+{"token_type":"access","exp":1664615471,"jti":"20c7796a9c3f4b98bc787d44f74b4a4c","user_id":1}
+\end{lstlisting}
+e da questo, anche se riuscissimo a scoprirne il \textbf{secret} per la verifica, dovremmo far conto sia con la timestamp di scadenza che con l'ID (JTI). Il payload di questo listato è stato generato da un'app realizzata usando dj-rest-auth \cite{DJ-REST-AUTH:1} il quale alla base usa la libreria PyJWT \cite{PYJWT:1}.
+
+Come ampiamente discusso da Portswigger nel loro articolo \cite{JWT-ATTACK:1} sono numerosi i possibili attacchi a JWT, molti dei quali, in realtà, vengono eseguiti modificando l'header, come ad esempio l'attacco a JWK \cite{JWK:1}; niente che una buona libreria aggiornata non possa prevenire. \ No newline at end of file