feature: Add extra feedback once someone solves JWT refresh lesson differently
One can solve this lesson by using `alg:none` instead of using the refresh token flow. Instead of adding a check to force using the refresh token we opt for giving the user extra feedback.
This commit is contained in:
parent
73b8c431fc
commit
ecfc321f14
@ -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;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* {
|
||||
* "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"
|
||||
* ]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author nbaars
|
||||
* @since 4/23/17.
|
||||
*/
|
||||
@RestController
|
||||
@AssignmentHints({
|
||||
"jwt-final-hint1",
|
||||
|
@ -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<String, Object> createNewTokens(String user) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("admin", "false");
|
||||
claims.put("user", user);
|
||||
Map<String, Object> 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());
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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<String, Object> 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<String, Object> 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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user