WIP
This commit is contained in:
@ -0,0 +1,22 @@
|
||||
package org.owasp.webgoat.jwt;
|
||||
|
||||
import org.owasp.webgoat.assignments.AssignmentEndpoint;
|
||||
import org.owasp.webgoat.assignments.AttackResult;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class JWTDecodeEndpoint extends AssignmentEndpoint {
|
||||
|
||||
@PostMapping("/JWT/decode")
|
||||
@ResponseBody
|
||||
public AttackResult decode(@RequestParam("jwt-encode-user") String user) {
|
||||
if ("user".equals(user)) {
|
||||
return success(this).build();
|
||||
} else {
|
||||
return failed(this).build();
|
||||
}
|
||||
}
|
||||
}
|
@ -84,62 +84,6 @@ public class JWTFinalEndpoint extends AssignmentEndpoint {
|
||||
private JWTFinalEndpoint(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@PostMapping(path="/JWT/encode",produces=MediaType.TEXT_HTML_VALUE)
|
||||
@ResponseBody
|
||||
public String encode(@RequestParam("jsonHeader") String jsonHeader,
|
||||
@RequestParam("jsonPayload") String jsonPayload,
|
||||
@RequestParam("jsonSecret") String jsonSecret) throws NoSuchAlgorithmException {
|
||||
|
||||
//System.out.println(jsonHeader);
|
||||
//System.out.println(jsonPayload);
|
||||
String encodedHeader;
|
||||
String encodedPayload;
|
||||
String encodedSignature;
|
||||
try {
|
||||
encodedHeader = TextCodec.BASE64URL.encode(jsonHeader);
|
||||
encodedPayload = TextCodec.BASE64URL.encode(jsonPayload);
|
||||
if (jsonHeader.toLowerCase().contains("none")) {
|
||||
encodedSignature="";
|
||||
} else {
|
||||
encodedSignature = TextCodec.BASE64URL.encode(getJWTSignature(jsonHeader, encodedHeader, encodedPayload, jsonSecret));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
encodedHeader="";
|
||||
encodedPayload="signature type not supported in this tool, try jwt.io";
|
||||
encodedSignature = "";
|
||||
}
|
||||
String result = "{\"encodedHeader\":\""+encodedHeader+"\",\"encodedPayload\":\""+encodedPayload+"\",\"encodedSignature\":\""+encodedSignature+"\"}";
|
||||
//System.out.println(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] getJWTSignature(String jsonHeader, String encodedHeader, String encodedPayload, String jsonSecret) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
String message = encodedHeader+"."+encodedPayload;
|
||||
String algorithm = "HmacSHA256";
|
||||
if (jsonHeader.equals("HS512")) {
|
||||
algorithm = "HmacSHA512";
|
||||
}
|
||||
Mac macInstance = Mac.getInstance(algorithm);
|
||||
SecretKeySpec secret_key = new SecretKeySpec(TextCodec.BASE64.decode(jsonSecret), algorithm);
|
||||
macInstance.init(secret_key);
|
||||
|
||||
return macInstance.doFinal(message.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@PostMapping(path="/JWT/decode",produces=MediaType.TEXT_HTML_VALUE)
|
||||
@ResponseBody
|
||||
public String decode(@RequestParam("jwtToken") String jwtToken) throws NoSuchAlgorithmException {
|
||||
try {
|
||||
String encodedHeader = jwtToken.substring(0, jwtToken.indexOf("."));
|
||||
String encodedPayload = jwtToken.substring(jwtToken.indexOf(".")+1, jwtToken.lastIndexOf("."));
|
||||
String jsonHeader = TextCodec.BASE64URL.decodeToString(encodedHeader);
|
||||
String jsonPayload = TextCodec.BASE64URL.decodeToString(encodedPayload);
|
||||
return "{\"jsonHeader\":\""+jsonHeader.replace("\"", "\\\"")+"\",\"jsonPayload\":\""+jsonPayload.replace("\"", "\\\"").replace("\t","").replace("\r", "").replace("\n", "")+"\"}";
|
||||
} catch (Exception e) {
|
||||
return "{\"jsonHeader\":\"\",\"jsonPayload\":\"\"}";
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/JWT/final/follow/{user}")
|
||||
public @ResponseBody
|
||||
|
@ -1,61 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<header>
|
||||
<script>
|
||||
$(document).ready(
|
||||
function(){
|
||||
$("#secrettoken").load('/WebGoat/JWT/secret/gettoken');
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</header>
|
||||
<body>
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:JWT_plan.adoc"></div>
|
||||
</div>
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:JWT_structure.adoc"></div>
|
||||
<form id="encode" class="attack-form" method="POST" name="form" action="/WebGoat/JWT/encode" >
|
||||
<table width="100%"><tbody>
|
||||
<tr><td>JWT header: </td><td width="100%"><input class="form-control" id="jsonHeader" name="jsonHeader" value='{ "alg":"HS256", "typ":"JWT"}' type="TEXT"/></td></tr>
|
||||
|
||||
<tr><td>JWT payload: </td><td width="100%">
|
||||
<textarea class="form-control" rows="7" id="jsonPayload" name="jsonPayload" >
|
||||
{
|
||||
"exp": 1416471934, "user_name": "user",
|
||||
"scope": [ "read", "write" ],
|
||||
"authorities": [ "ROLE_ADMIN", "ROLE_USER" ],
|
||||
"jti": "9bc92a44-0b1a-4c5e-be70-da52075b9a84",
|
||||
"client_id": "my-client-with-secret"
|
||||
}
|
||||
</textarea>
|
||||
</td></tr>
|
||||
<tr><td align="center">.</td></tr>
|
||||
<tr><td>encryption key: </td><td width="100%"><input class="form-control" id="jsonSecret" name="jsonSecret" value="secret" type="TEXT"/></td></tr>
|
||||
<tr><td><input name="SUBMIT" value="generate JWT token" type="SUBMIT"/></td></tr>
|
||||
</tbody></table>
|
||||
</form>
|
||||
<form id="decode" class="attack-form" method="POST" name="form" action="/WebGoat/JWT/decode" >
|
||||
<table width=100%><tbody>
|
||||
<tr><td>JWT token: </td><td width="100%">
|
||||
<span id="encodedHeader" style="color: orange;font-family:courier">eyAgImFsZyI6IkhTMjU2IiwgICJ0eXAiOiJKV1QifQ</span>
|
||||
<span style="color: black;font-family:courier"><b>.//header</b></span><br/>
|
||||
<div id="encodedPayload" style="color: green;font-family:courier;width:50%">eyAgCQ0KCSJleHAiOiAxNDE2NDcxOTM0LCAgInVzZXJfbmFtZSI6ICJ1c2Vy
|
||||
IiwgIA0KCSJzY29wZSI6IFsgICAgInJlYWQiLCAgICAid3JpdGUiICBdLCAg
|
||||
DQoJImF1dGhvcml0aWVzIjogWyAgICAiUk9MRV9BRE1JTiIsICAgICJST0xF
|
||||
X1VTRVIiICBdLCAgDQoJImp0aSI6ICI5YmM5MmE0NC0wYjFhLTRjNWUtYmU3
|
||||
MC1kYTUyMDc1YjlhODQiLCAgDQoJImNsaWVudF9pZCI6ICJteS1jbGllbnQt
|
||||
d2l0aC1zZWNyZXQiDQp9DQoJCQk<span style="color: black;font-family:courier"><b>. //payload</b></span></div>
|
||||
<span id="encodedSignature" style="color: blue;font-family:courier">gc32RepP65NANPLQP31Aq7QPbpWKBOiaS9UXczYVZLE</span><span style="color: black;font-family:courier"><b>//signature</b></span></td></tr>
|
||||
<tr><td>JWT token: </td><td width="100%"><input class="form-control" id="jwtToken" name="jwtToken" value='' type="text"/></td></tr>
|
||||
<tr><td><input name="SUBMIT" value="decode JWT token" type="SUBMIT"/></td></tr>
|
||||
</tbody></table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:JWT_decode.adoc"></div>
|
||||
<div class="attack-container">
|
||||
<img th:src="@{/images/wolf-enabled.png}" class="webwolf-enabled"/>
|
||||
<form id="decode" class="attack-form" method="POST" name="form" action="/WebGoat/JWT/decode">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="col-lg-10">
|
||||
<span>
|
||||
<span>
|
||||
Username:
|
||||
</span>
|
||||
<input type="text" name="jwt-encode-user">
|
||||
<button type="SUBMIT">Submit</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</form>
|
||||
<div class="attack-feedback"></div>
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:JWT_login_to_token.adoc"></div>
|
||||
</div>
|
||||
@ -63,7 +41,6 @@ $(document).ready(
|
||||
<div class="adoc-content" th:replace="doc:JWT_signing.adoc"></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-voting.js}" language="JavaScript"></script>
|
||||
<div class="attack-container">
|
||||
<div class="attack-feedback"></div>
|
||||
@ -125,7 +102,7 @@ $(document).ready(
|
||||
|
||||
<div class="lesson-page-wrapper">
|
||||
<div class="adoc-content" th:replace="doc:JWT_weak_keys"></div>
|
||||
<div id="secrettoken" ></div>
|
||||
<div id="secrettoken"></div>
|
||||
|
||||
<div class="attack-container">
|
||||
<div class="assignment-success"><i class="fa fa-2 fa-check hidden" aria-hidden="true"></i></div>
|
||||
@ -225,9 +202,11 @@ $(document).ready(
|
||||
<td class="col-sm-1 col-md-1" style="text-align: center">
|
||||
<input type="text" class="form-control" id="quantity2" value="2"></input>
|
||||
</td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$<span id="piecePrice2">4.99</span></strong>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$<span
|
||||
id="piecePrice2">4.99</span></strong>
|
||||
</td>
|
||||
<td class="col-sm-1 col-md-1 text-center"><strong>$<span id="totalPrice2">9.98</span></strong></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">
|
||||
<button type="button" class="btn btn-danger">
|
||||
<span class="glyphicon glyphicon-remove"></span> Remove
|
||||
@ -242,7 +221,8 @@ $(document).ready(
|
||||
<td> </td>
|
||||
<td><h5>Subtotal<br></br>Estimated shipping</h5>
|
||||
<h3>Total</h3></td>
|
||||
<td class="text-right"><h5><strong>$<span id="subtotalJwt">24.59</span><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>$<span id="totalJwt">31.53</span></h3></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -332,44 +312,6 @@ $(document).ready(
|
||||
<div class="attack-output"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
string_chop = function(str, size){
|
||||
if (str == null) return [];
|
||||
str = String(str);
|
||||
size = ~~size;
|
||||
var strArray = str.match(new RegExp('.{1,' + size + '}', 'g'));
|
||||
var strResult = "";
|
||||
var i;
|
||||
for (i = 0; i < strArray.length; i++) {
|
||||
strResult = strResult + strArray[i] + "\n";
|
||||
}
|
||||
return strResult;
|
||||
}
|
||||
|
||||
$('#encode').submit(function () {
|
||||
$.post('/WebGoat/JWT/encode', $('#encode').serialize(), function (data, textStatus) {
|
||||
var obj = JSON.parse(data);
|
||||
document.getElementById("jwtToken").value=obj.encodedHeader+"."+obj.encodedPayload+"."+obj.encodedSignature;
|
||||
document.getElementById("encodedHeader").innerHTML=obj.encodedHeader;
|
||||
document.getElementById("encodedPayload").innerHTML=string_chop(obj.encodedPayload, 80);
|
||||
document.getElementById("encodedSignature").innerHTML=obj.encodedSignature;
|
||||
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#decode').submit(function () {
|
||||
$.post('/WebGoat/JWT/decode', $('#decode').serialize(), function (data, textStatus) {
|
||||
var obj = JSON.parse(data);
|
||||
document.getElementById("jsonHeader").value=obj.jsonHeader;
|
||||
document.getElementById("jsonPayload").value=obj.jsonPayload;
|
||||
document.getElementById("jsonSignature").value="";
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,12 @@
|
||||
== Decoding a JWT token
|
||||
|
||||
Let's try decoding a JWT token, for this you can use the webWolfLink:jwt[noLink] inside WebWolf.
|
||||
Given the following token:
|
||||
|
||||
[source]
|
||||
----
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDcwOTk2MDgsInVzZXJfbmFtZSI6InVzZXIiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI5YmM5MmE0NC0wYjFhLTRjNWUtYmU3MC1kYTUyMDc1YjlhODQiLCJjbGllbnRfaWQiOiJteS1jbGllbnQtd2l0aC1zZWNyZXQifQ.N9TsIXpvMoICVeGI9mEOPVlYODjMOCis--yB-34BOOw
|
||||
----
|
||||
|
||||
Copy and paste the following token and decode the token, can you find the user inside the token?
|
||||
|
@ -1,10 +1,17 @@
|
||||
== Structure of a JWT token
|
||||
|
||||
Let's take a look at the structure of a JWT token.
|
||||
Let's take a look at the structure of a JWT token:
|
||||
|
||||
The token is base64-url-encoded and consists of three parts `header.claims.signature`.
|
||||
[role="lesson-image"]
|
||||
image::images/jwt_token.png[JWT]
|
||||
|
||||
The token is base64 encoded and consists of three parts:
|
||||
|
||||
- header
|
||||
- claims
|
||||
- signature
|
||||
|
||||
Both header and claims consist are respresented by a JSON object. The header describes the cryptographic operations applied to the JWT and optionally, additional properties of the JWT.
|
||||
The claims represent a JSON object whose members are the claims conveyed by the JWT.
|
||||
|
||||
Based on the algorithm the signature will be added to the token. This way you can verify that someone did not modify the token (one change to the token will invalidate the signature).
|
||||
|
||||
You can use the below forms as a simple way to encode and decode JWT tokens. Or you can make use of the more extensive options at https://jwt.io[jwt.io,window=_blank].
|
||||
You can revisit this page for the assignments to come and use it as part of your attempts to solve it.
|
||||
|
@ -1,16 +1,6 @@
|
||||
package org.owasp.webgoat.jwt;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -21,9 +11,15 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import io.jsonwebtoken.Jwt;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import lombok.SneakyThrows;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class JWTFinalEndpointTest extends LessonTest {
|
||||
@ -77,22 +73,4 @@ public class JWTFinalEndpointTest extends LessonTest {
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-invalid-token"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testJWTTestTools() {
|
||||
|
||||
//JWTFinalEndpoint jwtFinalEndpoint = new JWTFinalEndpoint(null);
|
||||
String jsonHeader = "{\"alg\":\"HS256\"}";
|
||||
String jsonPayload = "{\"iss\":\"OWASP\"}";
|
||||
String jsonSecret = "secret";
|
||||
String jwtToken = jwtFinalEndpoint.encode(jsonHeader, jsonPayload, jsonSecret).replace(":", "")
|
||||
.replace("encodedHeader", "").replace("encodedPayload", "").replace("encodedSignature", "")
|
||||
.replace("{", "").replace("}", "").replace("\"", "").replace(",", ".");
|
||||
|
||||
Jwt jwt = Jwts.parser().setSigningKey(jsonSecret).parse(jwtToken);
|
||||
String revert = jwtFinalEndpoint.decode(jwtToken);
|
||||
//System.out.println("revert: "+revert);
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user