Compare commits

..

9 Commits

11 changed files with 102 additions and 53 deletions

View File

@ -143,7 +143,6 @@ public class JWTVotesEndpoint extends AssignmentEndpoint {
Claims claims = (Claims) jwt.getBody(); Claims claims = (Claims) jwt.getBody();
boolean isAdmin = Boolean.valueOf((String) claims.get("admin")); boolean isAdmin = Boolean.valueOf((String) claims.get("admin"));
if (!isAdmin) { if (!isAdmin) {
votes.values().forEach(vote -> vote.reset());
return trackProgress(failed().feedback("jwt-only-admin").build()); return trackProgress(failed().feedback("jwt-only-admin").build());
} else { } else {
votes.values().forEach(vote -> vote.reset()); votes.values().forEach(vote -> vote.reset());

View File

@ -16,8 +16,10 @@
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/> <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/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-signing.js}" language="JavaScript"></script> <script th:src="@{/lesson_js/jwt-voting.js}" language="JavaScript"></script>
<div class="attack-container"> <div class="attack-container">
<div class="attack-feedback"></div>
<div class="attack-output"></div>
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div> <div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN" <form class="attack-form" accept-charset="UNKNOWN"
method="POST" method="POST"
@ -37,16 +39,16 @@
</button> </button>
<ul class="dropdown-menu dropdown-menu-left"> <ul class="dropdown-menu dropdown-menu-left">
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Guest')" onclick="javascript:loginVotes('Guest')"
th:text="Guest">current</a></li> th:text="Guest">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Tom')" onclick="javascript:loginVotes('Tom')"
th:text="Tom">current</a></li> th:text="Tom">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Jerry')" onclick="javascript:loginVotes('Jerry')"
th:text="Jerry">current</a></li> th:text="Jerry">current</a></li>
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
onclick="javascript:login('Sylvester')" onclick="javascript:loginVotes('Sylvester')"
th:text="Sylvester">current</a></li> th:text="Sylvester">current</a></li>
</ul> </ul>
<button type="button" class="btn btn-default fa fa-refresh" title="Refresh votes" <button type="button" class="btn btn-default fa fa-refresh" title="Refresh votes"
@ -70,8 +72,7 @@
</form> </form>
<br/> <br/>
<div class="attack-feedback"></div>
<div class="attack-output"></div>
</div> </div>
</div> </div>
@ -110,7 +111,7 @@
<link rel="stylesheet" type="text/css" th:href="@{/lesson_css/jwt.css}"/> <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/bootstrap.min.js}" language="JavaScript"></script>
<script th:src="@{/lesson_js/jwt-refresh.js}" language="JavaScript"></script> <script th:src="@{/lesson_js/jwt-buy.js}" language="JavaScript"></script>
<div class="attack-container"> <div class="attack-container">
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div> <div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
<form class="attack-form" accept-charset="UNKNOWN" <form class="attack-form" accept-charset="UNKNOWN"
@ -135,9 +136,11 @@
<tr> <tr>
<td class="col-sm-8 col-md-6"> <td class="col-sm-8 col-md-6">
<div class="media"> <div class="media">
<img class="media-object" src="http://icons.iconarchive.com/icons/custom-icon-design/flatastic-2/72/product-icon.png" style="width: 72px; height: 72px;"></img> <img class="media-object" th:src="@{/images/product-icon.png}"
style="width: 72px; height: 72px;"></img>
<div class="media-body"> <div class="media-body">
<h4 class="media-heading"><a href="#">Learn defending your application with WebGoat</a></h4> <h4 class="media-heading"><a href="#">Learn to defend your application with
WebGoat</a></h4>
<h5 class="media-heading"> by <a href="#">WebGoat Publishing</a></h5> <h5 class="media-heading"> by <a href="#">WebGoat Publishing</a></h5>
<span>Status: </span><span <span>Status: </span><span
class="text-success"><strong>In Stock</strong></span> class="text-success"><strong>In Stock</strong></span>
@ -147,8 +150,11 @@
<td class="col-sm-1 col-md-1" style="text-align: center"> <td class="col-sm-1 col-md-1" style="text-align: center">
<input type="text" class="form-control" id="quantity1" value="3"></input> <input type="text" class="form-control" id="quantity1" value="3"></input>
</td> </td>
<td class="col-sm-1 col-md-1 text-center"><strong>$4.87</strong></td> <td class="col-sm-1 col-md-1 text-center"><strong>$
<td class="col-sm-1 col-md-1 text-center"><strong>$14.61</strong></td> <span id="piecePrice1">4.87</span></strong>
</td>
<td class="col-sm-1 col-md-1 text-center"><strong>$<span
id="totalPrice1">14.61</span></strong></td>
<td class="col-sm-1 col-md-1"> <td class="col-sm-1 col-md-1">
<button type="button" class="btn btn-danger"> <button type="button" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span> Remove <span class="glyphicon glyphicon-remove"></span> Remove
@ -158,7 +164,9 @@
<tr> <tr>
<td class="col-md-6"> <td class="col-md-6">
<div class="media"> <div class="media">
<img class="media-object" src="http://icons.iconarchive.com/icons/custom-icon-design/flatastic-2/72/product-icon.png" style="width: 72px; height: 72px;"></img> <img class="media-object"
th:src="@{/images/product-icon.png}"
style="width: 72px; height: 72px;"></img>
<div class="media-body"> <div class="media-body">
<h4 class="media-heading"><a href="#">Pentesting for professionals</a></h4> <h4 class="media-heading"><a href="#">Pentesting for professionals</a></h4>
<h5 class="media-heading"> by <a href="#">WebWolf Publishing</a></h5> <h5 class="media-heading"> by <a href="#">WebWolf Publishing</a></h5>
@ -166,11 +174,12 @@
</div> </div>
</div> </div>
</td> </td>
<td class="col-md-1" style="text-align: center"> <td class="col-sm-1 col-md-1" style="text-align: center">
<input type="text" class="form-control" id="quantity2" value="2"></input> <input type="text" class="form-control" id="quantity2" value="2"></input>
</td> </td>
<td class="col-md-1 text-center"><strong>$4.99</strong></td> <td class="col-sm-1 col-md-1 text-center"><strong>$<span id="piecePrice2">4.99</span></strong>
<td class="col-md-1 text-center"><strong>$9.98</strong></td> </td>
<td class="col-sm-1 col-md-1 text-center"><strong>$<span id="totalPrice2">9.98</span></strong></td>
<td class="col-md-1"> <td class="col-md-1">
<button type="button" class="btn btn-danger"> <button type="button" class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span> Remove <span class="glyphicon glyphicon-remove"></span> Remove
@ -185,8 +194,8 @@
<td>  </td> <td>  </td>
<td><h5>Subtotal<br></br>Estimated shipping</h5> <td><h5>Subtotal<br></br>Estimated shipping</h5>
<h3>Total</h3></td> <h3>Total</h3></td>
<td class="text-right"><h5><strong>$24.59<br></br>$6.94</strong></h5> <td class="text-right"><h5><strong>$<span id="subtotalJwt">24.59</span><br></br>$6.94</strong></h5>
<h3>$31.53</h3></td> <h3>$<span id="totalJwt">31.53</span></h3></td>
</tr> </tr>
<tr> <tr>
<td>  </td> <td>  </td>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,36 @@
$(document).ready(function () {
$("#quantity1").on("blur", function () {
var quantity = $("#quantity1").val();
if (!$.isNumeric(quantity) || quantity < 0) {
$("#quantity1").val("1");
quantity = 1;
}
var piecePrice = $("#piecePrice1").text();
$('#totalPrice1').text((quantity * piecePrice).toFixed(2));
updateTotal();
});
$("#quantity2").on("blur", function () {
var quantity = $("#quantity2").val();
if (!$.isNumeric(quantity) || quantity < 0) {
$("#quantity2").val("1");
quantity = 1;
}
var piecePrice = $("#piecePrice2").text();
$('#totalPrice2').text((quantity * piecePrice).toFixed(2));
updateTotal();
})
})
function updateTotal() {
var price1 = parseFloat($('#totalPrice1').text());
var price2 = parseFloat($('#totalPrice2').text());
var subTotal = price1 + price2;
$('#subtotalJwt').text(subTotal.toFixed(2));
var total = subTotal + 6.94;
$('#totalJwt').text(total.toFixed(2));
}

View File

@ -1,8 +1,8 @@
$(document).ready(function () { $(document).ready(function () {
login('Guest'); loginVotes('Guest');
}) })
function login(user) { function loginVotes(user) {
$("#name").text(user); $("#name").text(user);
$.ajax({ $.ajax({
url: 'JWT/votings/login?user=' + user, url: 'JWT/votings/login?user=' + user,

View File

@ -1,8 +1,11 @@
:linkattrs:
== Refreshing a token == Refreshing a token
=== Introduction === Introduction
In this section we touch upon refreshing an access token. There are many solutions some might In this section we touch upon refreshing an access token.
=== Types of tokens === Types of tokens
@ -31,7 +34,7 @@ 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) 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 you can match the refresh token to the specific user the refresh token was granted to. in order to match the refresh token to the 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 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. 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 When the access token is no longer valid the server needs to query for the stored refresh token to make sure the token
@ -51,7 +54,7 @@ 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 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 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). 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 Also 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 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) (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 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
@ -73,6 +76,13 @@ store for a refresh token, see previous paragraph. This way you need to check th
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). use a hash function to store them in your database).
=== JWT a good idea?
There are a lot of resources available which question the usecase for using JWT token for client to server authentication
with regards to cookies. The best place to use a JWT token is between server to server communication. In a normal web
application you are better of using plain old cookies. See for more information:
- http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/[stop-using-jwt-for-sessions, window="_blank"]
- http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work/[stop-using-jwt-for-sessions-part-2-why-your-solution-doesnt-work, window="_blank"]
- http://cryto.net/~joepie91/blog/attachments/jwt-flowchart.png[flowchart, window="_blank"]

View File

@ -1,5 +1,10 @@
:linkattrs:
== Refreshing a token == Refreshing a token
It is important to implement a good strategy for refreshing an access token. This assignment is based on a vulnerability
found in a private bug bounty program on Bugcrowd, you can read the full write up https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/[here, window="_blank"]
=== Assignment === Assignment
From a breach of last year the following logfile is available link:images/logs.txt[here] From a breach of last year the following logfile is available link:images/logs.txt[here]

View File

@ -21,11 +21,10 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-4">
<div class="col-md-2"> <div style="padding: 20px;" id="password-login-2">
<h4 style="border-bottom: 1px solid #c5c5c5;"><i class="glyphicon glyphicon-user"></i> Account <h4 style="border-bottom: 1px solid #c5c5c5;"><i class="glyphicon glyphicon-user"></i> Account
Access</h4> Access</h4>
<div style="padding: 20px;" id="form-olvidado">
<fieldset> <fieldset>
<div class="form-group input-group"> <div class="form-group input-group">
<span class="input-group-addon">@</span> <span class="input-group-addon">@</span>
@ -42,7 +41,7 @@
Access Access
</button> </button>
<p class="help-block"> <p class="help-block">
<a class="pull-right text-muted" href="#" id="olvidado"> <a class="pull-right text-muted" href="#" id="olvidado" onclick="showPasswordReset()">
<small>Forgot your password?</small> <small>Forgot your password?</small>
</a> </a>
</p> </p>
@ -50,7 +49,7 @@
</fieldset> </fieldset>
</div> </div>
<div style="display: none;" id="form-olvidado"> <div style="display: none;" id="password-reset-2">
<h4 class="">Forgot your password?</h4> <h4 class="">Forgot your password?</h4>
<fieldset> <fieldset>
@ -63,7 +62,7 @@
<button type="submit" class="btn btn-primary btn-block" id="btn-olvidado">Continue <button type="submit" class="btn btn-primary btn-block" id="btn-olvidado">Continue
</button> </button>
<p class="help-block"> <p class="help-block">
<a class="text-muted" href="#" id="acceso"> <a class="text-muted" href="#" id="acceso" onclick="showPassword()">
<small>Account Access</small> <small>Account Access</small>
</a> </a>
</p> </p>
@ -99,7 +98,7 @@
action="/WebGoat/PasswordReset/questions" action="/WebGoat/PasswordReset/questions"
enctype="application/json;charset=UTF-8"> enctype="application/json;charset=UTF-8">
<div class="container-fluid"> <div class="container-fluid">
<div class="col-md-2"> <div class="col-md-4">
<article class="card-body"> <article class="card-body">
<a href="" class="float-right btn btn-outline-primary">Sign up</a> <a href="" class="float-right btn btn-outline-primary">Sign up</a>
<a href="" class="float-right btn btn-outline-primary">Login</a> <a href="" class="float-right btn btn-outline-primary">Login</a>
@ -143,7 +142,7 @@
enctype="application/json;charset=UTF-8"> enctype="application/json;charset=UTF-8">
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-4">
<h4 style="border-bottom: 1px solid #c5c5c5;"> <h4 style="border-bottom: 1px solid #c5c5c5;">
<i class="glyphicon glyphicon-user"></i> <i class="glyphicon glyphicon-user"></i>
Account Access Account Access

View File

@ -1,22 +1,13 @@
$(document).ready(function() {
$('#olvidado').click(function(e) {
e.preventDefault();
$('div#form-olvidado').toggle('500');
});
$('#acceso').click(function(e) {
e.preventDefault();
$('div#form-olvidado').toggle('500');
});
});
function showPasswordReset() { function showPasswordReset() {
console.log("clicking")
$('#password-reset').show(); $('#password-reset').show();
$('#password-login').hide(); $('#password-login').hide();
$('#password-reset-2').show();
$('#password-login-2').hide();
} }
function showPassword() { function showPassword() {
console.log("clicking")
$('#password-login').show(); $('#password-login').show();
$('#password-reset').hide(); $('#password-reset').hide();
$('#password-login-2').show();
$('#password-reset-2').hide();
} }

View File

@ -2,9 +2,9 @@
The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating strings making it susceptible to String SQL injection: The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating strings making it susceptible to String SQL injection:
------------------------------------------------------- ------------------------------------------------------------
"select * from users where name = " + userName + "'"; "select * from users where LAST_NAME = " + userName + "'";
------------------------------------------------------- ------------------------------------------------------------
Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use 'Smith' to see the data for one user. Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use 'Smith' to see the data for one user.

View File

@ -2,8 +2,8 @@
The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating a number making it susceptible to Numeric SQL injection: The query in the code builds a dynamic query as seen in the previous example. The query in the code builds a dynamic query by concatenating a number making it susceptible to Numeric SQL injection:
------------------------------------------------------- --------------------------------------------------
"select * from users where employee_id = " + userID; "select * from users where USERID = " + userID;
------------------------------------------------------- --------------------------------------------------
Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use '101' to see the data for one user. Using the form below try to retrieve all the users from the users table. You shouldn't need to know any specific user name to get the complete list, however you can use '101' to see the data for one user.