diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java
index e84bbf809..dfebaa03d 100644
--- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java
+++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTFinalEndpoint.java
@@ -22,7 +22,12 @@
package org.owasp.webgoat.lessons.jwt;
-import io.jsonwebtoken.*;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwsHeader;
+import io.jsonwebtoken.Jwt;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SigningKeyResolverAdapter;
import io.jsonwebtoken.impl.TextCodec;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -31,34 +36,12 @@ import org.owasp.webgoat.container.LessonDataSource;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AssignmentHints;
import org.owasp.webgoat.container.assignments.AttackResult;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.PathVariable;
+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;
-/**
- *
- *
- *
- * {
- * "typ": "JWT",
- * "kid": "webgoat_key",
- * "alg": "HS256"
- * }
- * {
- * "iss": "WebGoat Token Builder",
- * "iat": 1524210904,
- * "exp": 1618905304,
- * "aud": "webgoat.org",
- * "sub": "jerry@webgoat.com",
- * "username": "Jerry",
- * "Email": "jerry@webgoat.com",
- * "Role": [
- * "Cat"
- * ]
- * }
- *
- *
- * @author nbaars
- * @since 4/23/17.
- */
@RestController
@AssignmentHints({
"jwt-final-hint1",
diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java
index 945bb57da..4efc9db09 100644
--- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java
+++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpoint.java
@@ -49,10 +49,6 @@ import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
-/**
- * @author nbaars
- * @since 4/23/17.
- */
@RestController
@AssignmentHints({
"jwt-refresh-hint1",
@@ -85,9 +81,7 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint {
}
private Map createNewTokens(String user) {
- Map claims = new HashMap<>();
- claims.put("admin", "false");
- claims.put("user", user);
+ Map claims = Map.of("admin", "false", "user", user);
String token =
Jwts.builder()
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
@@ -114,6 +108,9 @@ public class JWTRefreshEndpoint extends AssignmentEndpoint {
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
if ("Tom".equals(user)) {
+ if ("none".equals(jwt.getHeader().get("alg"))) {
+ return ok(success(this).feedback("jwt-refresh-alg-none").build());
+ }
return ok(success(this).build());
}
return ok(failed(this).feedback("jwt-refresh-not-tom").feedbackArgs(user).build());
diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java
index dac1ef5cc..0e688c049 100644
--- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java
+++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTSecretKeyEndpoint.java
@@ -42,10 +42,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
-/**
- * @author nbaars
- * @since 4/23/17.
- */
@RestController
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {
diff --git a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java
index 632449822..02a935498 100644
--- a/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java
+++ b/src/main/java/org/owasp/webgoat/lessons/jwt/JWTVotesEndpoint.java
@@ -58,10 +58,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
-/**
- * @author nbaars
- * @since 4/23/17.
- */
@RestController
@AssignmentHints({
"jwt-change-token-hint1",
diff --git a/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties b/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties
index 70ac7a4a1..ed05f46b2 100644
--- a/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties
+++ b/src/main/resources/lessons/jwt/i18n/WebGoatLabels.properties
@@ -21,6 +21,7 @@ jwt-refresh-hint2=The token from the access log is no longer valid, can you find
jwt-refresh-hint3=The endpoint for refreshing a token is 'JWT/refresh/newToken'
jwt-refresh-hint4=Use the found access token in the Authorization: Bearer header and use your own refresh token
jwt-refresh-not-tom=User is not Tom but {0}, please try again
+jwt-refresh-alg-none=Nicely found! You solved the assignment with 'alg: none' can you also solve it by using the refresh token?
jwt-final-jerry-account=Yikes, you are removing Jerry's account, try to delete the account of Tom
jwt-final-not-tom=Username is not Tom try to pass a token for Tom
@@ -30,4 +31,4 @@ jwt-final-hint2=The 'kid' (key ID) header parameter is a hint indicating which k
jwt-final-hint3=The key can be located on the filesystem in memory or even reside in the database
jwt-final-hint4=The key is stored in the database and loaded while verifying a token
jwt-final-hint5=Using a SQL injection you might be able to manipulate the key to something you know and create a new token.
-jwt-final-hint6=Use: hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS -- as the kid in the header and change the contents of the token to Tom and hit the endpoint with the new token
\ No newline at end of file
+jwt-final-hint6=Use: hacked' UNION select 'deletingTom' from INFORMATION_SCHEMA.SYSTEM_USERS -- as the kid in the header and change the contents of the token to Tom and hit the endpoint with the new token
diff --git a/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java b/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java
index 36eb18305..028717706 100644
--- a/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java
+++ b/src/test/java/org/owasp/webgoat/lessons/jwt/JWTRefreshEndpointTest.java
@@ -29,6 +29,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Jwts;
import java.util.HashMap;
import java.util.Map;
import org.hamcrest.CoreMatchers;
@@ -43,14 +44,14 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
public class JWTRefreshEndpointTest extends LessonTest {
@BeforeEach
- public void setup() {
+ void setup() {
when(webSession.getCurrentLesson()).thenReturn(new JWT());
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
when(webSession.getUserName()).thenReturn("unit-test");
}
@Test
- public void solveAssignment() throws Exception {
+ void solveAssignment() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// First login to obtain tokens for Jerry
@@ -96,7 +97,26 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception {
+ void solutionWithAlgNone() throws Exception {
+ String tokenWithNoneAlgorithm =
+ Jwts.builder()
+ .setHeaderParam("alg", "none")
+ .addClaims(Map.of("admin", "true", "user", "Tom"))
+ .compact();
+
+ // Now checkout with the new token from Tom
+ mockMvc
+ .perform(
+ MockMvcRequestBuilders.post("/JWT/refresh/checkout")
+ .header("Authorization", "Bearer " + tokenWithNoneAlgorithm))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.lessonCompleted", is(true)))
+ .andExpect(
+ jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-refresh-alg-none"))));
+ }
+
+ @Test
+ void checkoutWithTomsTokenFromAccessLogShouldFail() throws Exception {
String accessTokenTom =
"eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
mockMvc
@@ -108,7 +128,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void checkoutWitRandomTokenShouldFail() throws Exception {
+ void checkoutWitRandomTokenShouldFail() throws Exception {
String accessTokenTom =
"eyJhbGciOiJIUzUxMiJ9.eyJpLXQiOjE1MjYxMzE0MTEsImV4cCI6MTUyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.DCoaq9zQkyDH25EcVWKcdbyVfUL4c9D4jRvsqOqvi9iAd4QuqmKcchfbU8FNzeBNF9tLeFXHZLU4yRkq-bjm7Q";
mockMvc
@@ -121,7 +141,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void flowForJerryAlwaysWorks() throws Exception {
+ void flowForJerryAlwaysWorks() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
var loginJson = Map.of("user", "Jerry", "password", PASSWORD);
@@ -146,7 +166,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void loginShouldNotWorkForJerryWithWrongPassword() throws Exception {
+ void loginShouldNotWorkForJerryWithWrongPassword() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
var loginJson = Map.of("user", "Jerry", "password", PASSWORD + "wrong");
@@ -159,7 +179,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void loginShouldNotWorkForTom() throws Exception {
+ void loginShouldNotWorkForTom() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
var loginJson = Map.of("user", "Tom", "password", PASSWORD);
@@ -172,7 +192,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void newTokenShouldWorkForJerry() throws Exception {
+ void newTokenShouldWorkForJerry() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
Map loginJson = new HashMap<>();
loginJson.put("user", "Jerry");
@@ -201,7 +221,7 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void unknownRefreshTokenShouldGiveUnauthorized() throws Exception {
+ void unknownRefreshTokenShouldGiveUnauthorized() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
Map loginJson = new HashMap<>();
loginJson.put("user", "Jerry");
@@ -229,21 +249,21 @@ public class JWTRefreshEndpointTest extends LessonTest {
}
@Test
- public void noTokenWhileCheckoutShouldReturn401() throws Exception {
+ void noTokenWhileCheckoutShouldReturn401() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.post("/JWT/refresh/checkout"))
.andExpect(status().isUnauthorized());
}
@Test
- public void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception {
+ void noTokenWhileRequestingNewTokenShouldReturn401() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.post("/JWT/refresh/newToken"))
.andExpect(status().isUnauthorized());
}
@Test
- public void noTokenWhileLoginShouldReturn401() throws Exception {
+ void noTokenWhileLoginShouldReturn401() throws Exception {
mockMvc
.perform(MockMvcRequestBuilders.post("/JWT/refresh/login"))
.andExpect(status().isUnauthorized());