WIP
This commit is contained in:
parent
ea9c1a453d
commit
7a0820bf89
@ -15,7 +15,7 @@ import io.jsonwebtoken.Jwts;
|
||||
* @since 4/23/17.
|
||||
*/
|
||||
@AssignmentPath("/JWT/secret")
|
||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3", "jwt-secret-hint4", "jwt-secret-hint5"})
|
||||
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
|
||||
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
|
||||
|
||||
private static final String JWT_SECRET = "victory";
|
||||
|
@ -101,4 +101,72 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:JWT_refresh.adoc"></div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:JWT_refresh_assignment"></div>
|
||||
|
||||
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/>
|
||||
<script th:src="@{/lesson_js/bootstrap.min.js}" language="JavaScript"></script>
|
||||
<script th:src="@{/lesson_js/jwt-signing.js}" language="JavaScript"></script>
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<form class="attack-form" accept-charset="UNKNOWN"
|
||||
method="POST"
|
||||
successCallback="jwtSigningCallback"
|
||||
action="/WebGoat/JWT/refresh/reset"
|
||||
enctype="application/json;charset=UTF-8">
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="well">
|
||||
<div class="pull-right">
|
||||
<div class="dropdown">
|
||||
<button type="button" data-toggle="dropdown" class="btn btn-default dropdown-toggle"
|
||||
title="Change user">
|
||||
<i class="fa fa-user"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-left">
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Guest')"
|
||||
th:text="Guest">current</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Tom')"
|
||||
th:text="Tom">current</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Jerry')"
|
||||
th:text="Jerry">current</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
onclick="javascript:login('Sylvester')"
|
||||
th:text="Sylvester">current</a></li>
|
||||
</ul>
|
||||
<button type="button" class="btn btn-default fa fa-refresh" title="Refresh votes"
|
||||
onclick="javascript:getVotings()"/>
|
||||
<button type="submit" class="btn btn-default fa fa-trash-o" title="Reset votes"/>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-right">Welcome back, <b><span id="name"></span></b></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3>Vote for your favorite</h3>
|
||||
</div>
|
||||
<div id="votesList" class="list-group">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br/>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</html>
|
@ -8,4 +8,8 @@ jwt-change-token-hint1=Select a different user and look at the token you receive
|
||||
jwt-change-token-hint2=Decode the token and look at the contents
|
||||
jwt-change-token-hint3=Change the contents of the token and replace the cookie before sending the request for getting the votes
|
||||
jwt-change-token-hint4=Change the admin field to true in the token
|
||||
jwt-change-token-hint5=Submit the token by changing the algorithm to None and remove the signature
|
||||
jwt-change-token-hint5=Submit the token by changing the algorithm to None and remove the signature
|
||||
|
||||
jwt-secret-hint1=Save the token and try to verify the token locally
|
||||
jwt-secret-hint2=Download a word list dictionary (https://github.com/first20hours/google-10000-english)
|
||||
jwt-secret-hint3=Write a small program or use HashCat for brute forcing the token according the word list
|
||||
|
@ -10,7 +10,7 @@ In general there are two type of tokens: access token and a refresh token. The a
|
||||
calls towards the server. Access tokens have a limited life span, that's where the refresh token comes in. Once
|
||||
the access token is no longer valid a request can me made towards the server to get a new access token by presenting
|
||||
the refresh token. The refresh token can expire but their life span is much longer. This solves the problem of a user
|
||||
having to authenticate again with their credentials. Whether you should use a refresh token and access token depends
|
||||
having to authenticate again with their credentials. Whether you should use a refresh token and access token depends,
|
||||
below can find a couple of points to keep in mind while choosing which tokens to use.
|
||||
|
||||
So a normal flow can look like:
|
||||
@ -31,18 +31,16 @@ The server returns:
|
||||
```
|
||||
|
||||
As you can see the refresh token is a random string which the server can keep track of (in memory or store in a database)
|
||||
With storing the information you can match the refresh token to the specific user to which the refresh token was
|
||||
granted to. So in this case whenever the access token is still valid we can speak of a "stateless" session, there is
|
||||
With storing you can match the refresh token to the specific user the refresh token was granted to.
|
||||
So in this case whenever the access token is still valid we can speak of a "stateless" session, there is
|
||||
no burden on the server side to setup the user session, the token is self contained.
|
||||
When the access token is no longer valid the server needs to query for the stored refresh token to make sure the token
|
||||
is not blocked in any way.
|
||||
|
||||
Whenever the attacker gets a hold on an access token it is only valid for a certain amount of time (say 10 minutes). The
|
||||
attacker then needs the refresh token to get a new access token. That is why the refresh token needs better protection.
|
||||
|
||||
It is also possible to make the refresh token stateless but this means it will become more difficult to see if
|
||||
the user revoked the tokens.
|
||||
|
||||
After the server made all the validations it must return a new refresh token and a new access token to the client. The
|
||||
client can use the new access token to make the API call.
|
||||
|
||||
@ -53,11 +51,9 @@ Regardless of the chosen solution you should store enough information on the ser
|
||||
is still trusted. You can think of many things, like store the ip address, keep track of how many times the refresh
|
||||
token is used (using the refresh token multiple times in the valid time window of the access token might indicate strange
|
||||
behavior, you can revoke all the tokens an let the user authenticate again).
|
||||
|
||||
It is also a good to keep track of which access token belonged to which refresh token. Otherwise an attacker might
|
||||
be able to get a new access token for a different user with the refresh token of the attacker
|
||||
(see https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/ for a nice write up about how this attack works)
|
||||
|
||||
Also a good thing to check for is the ip address or geolocation of the user. If you need to give out a new token check
|
||||
whether the location is still the same if not revoke all the tokens and let the user authenticate again.
|
||||
|
||||
@ -72,14 +68,10 @@ server you can chose to only use the access token.
|
||||
|
||||
As stated above using an access token and a separate refresh token gives some leverage for the server not to check
|
||||
the access token over and over. Only perform the check when the user needs a new access token.
|
||||
|
||||
It is certainly possible to only use an access token, at the server you store the exact same information you would
|
||||
store for a refresh token, see previous paragraph. This way you need to check the token each time but this might
|
||||
be suitable depending on the application.
|
||||
|
||||
In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
|
||||
be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least
|
||||
use a hash function to store them in your database).
|
||||
Another check is to make use there is only one access token
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
== Refreshing a token
|
||||
|
||||
=== Assignment
|
||||
|
||||
In this assignment Tom no longer has a valid token, try to find a way to create a new access token and use that token
|
||||
to vote.
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
Each JWT token should at least be signed before sending it to a client, if a token is not signed the client application
|
||||
would be able to change the contents of the token. The signing specifications are defined https://tools.ietf.org/html/rfc7515[here]
|
||||
the specific algorithms you can use are described https://tools.ietf.org/html/rfc7518[here]
|
||||
|
||||
It basically comes down you use "HMAC with SHA-2 Functions" or "Digital Signature with RSASSA-PKCS1-v1_5/ECDSA/RSASSA-PSS" function
|
||||
for signing the token.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user