Skip to content

Commit

Permalink
Added testcases for all JWT endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
nbaars committed May 23, 2018
1 parent e0cf5b4 commit 8d7ecb1
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package org.owasp.webgoat.plugin;

import com.google.common.collect.Lists;
import org.owasp.webgoat.assignments.AssignmentEndpoint;
import org.owasp.webgoat.assignments.AssignmentHints;
import org.owasp.webgoat.assignments.AssignmentPath;
import org.owasp.webgoat.assignments.AttackResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

/**
* @author nbaars
Expand All @@ -18,23 +23,29 @@
@AssignmentHints({"jwt-secret-hint1", "jwt-secret-hint2", "jwt-secret-hint3"})
public class JWTSecretKeyEndpoint extends AssignmentEndpoint {

private static final String JWT_SECRET = "victory";
public static final String JWT_SECRET = "victory";
private static final String WEBGOAT_USER = "WebGoat";
private static final List<String> expectedClaims = Lists.newArrayList("iss", "iat", "exp", "aud", "sub", "username", "Email", "Role");

@PostMapping()
public void login(@RequestParam String token) {
@PostMapping
@ResponseBody
public AttackResult login(@RequestParam String token) {
try {
Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJwt(token);
Jwt jwt = Jwts.parser().setSigningKey(JWT_SECRET).parse(token);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("username");

if (WEBGOAT_USER.equalsIgnoreCase(user)) {
trackProgress(success().build());
if (!claims.keySet().containsAll(expectedClaims)) {
return trackProgress(failed().feedback("jwt-secret-claims-missing").build());
} else {
trackProgress(failed().feedback("jwt-secret.not-correct").feedbackArgs(user).build());
String user = (String) claims.get("username");

if (WEBGOAT_USER.equalsIgnoreCase(user)) {
return trackProgress(success().build());
} else {
return trackProgress(failed().feedback("jwt-secret-incorrect-user").feedbackArgs(user).build());
}
}
} catch (Exception e) {
trackProgress(failed().feedback("jwt-invalid-token").output(e.getMessage()).build());
return trackProgress(failed().feedback("jwt-invalid-token").output(e.getMessage()).build());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import javax.annotation.PostConstruct;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
Expand All @@ -37,7 +39,7 @@
@AssignmentHints({"jwt-change-token-hint1", "jwt-change-token-hint2", "jwt-change-token-hint3", "jwt-change-token-hint4", "jwt-change-token-hint5"})
public class JWTVotesEndpoint extends AssignmentEndpoint {

private static final String JWT_PASSWORD = "victory";
public static final String JWT_PASSWORD = "victory";
private static String validUsers = "TomJerrySylvester";

private static int totalVotes = 38929;
Expand Down Expand Up @@ -65,11 +67,10 @@ public void initVotes() {
@GetMapping("/login")
public void login(@RequestParam("user") String user, HttpServletResponse response) {
if (validUsers.contains(user)) {
Map<String, Object> claims = Maps.newHashMap();
Claims claims = Jwts.claims().setIssuedAt(Date.from(Instant.now().plus(Duration.ofDays(10))));
claims.put("admin", "false");
claims.put("user", user);
String token = Jwts.builder()
.setIssuedAt(new Date(System.currentTimeMillis() + TimeUnit.DAYS.toDays(10)))
.setClaims(claims)
.signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, JWT_PASSWORD)
.compact();
Expand All @@ -96,11 +97,11 @@ public MappingJacksonValue getVotes(@CookieValue(value = "access_token", require
Jwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);
Claims claims = (Claims) jwt.getBody();
String user = (String) claims.get("user");
boolean isAdmin = Boolean.valueOf((String) claims.get("admin"));
if ("Guest".equals(user) || !validUsers.contains(user)) {
value.setSerializationView(Views.GuestView.class);
} else {
value.setSerializationView(Views.UserView.class);
}
value.setSerializationView(isAdmin ? Views.AdminView.class : Views.UserView.class);
} catch (JwtException e) {
value.setSerializationView(Views.GuestView.class);
}
Expand Down Expand Up @@ -132,7 +133,8 @@ public ResponseEntity<?> vote(@PathVariable String title, @CookieValue(value = "
}

@PostMapping("reset")
public @ResponseBody AttackResult resetVotes(@CookieValue(value = "access_token", required = false) String accessToken) {
public @ResponseBody
AttackResult resetVotes(@CookieValue(value = "access_token", required = false) String accessToken) {
if (StringUtils.isEmpty(accessToken)) {
return trackProgress(failed().feedback("jwt-invalid-token").build());
} else {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,4 @@ public interface GuestView {

public interface UserView extends GuestView {
}

public interface AdminView extends UserView {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ public class Vote {
private final String imageBig;
@JsonView(Views.UserView.class)
private int numberOfVotes;
@JsonView(Views.AdminView.class)
@Setter
private String flag;
@JsonView(Views.UserView.class)
private boolean votingAllowed = true;
@JsonView(Views.UserView.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ jwt-change-token-hint5=Submit the token by changing the algorithm to None and re
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
jwt-secret-claims-missing=You are missing some claims, you should keep all the claims in the token
jwt-secret-incorrect-user=The user is {0}, you need to change it to WebGoat

jwt-refresh-hint1=Look at the access log you will find a token there
jwt-refresh-hint2=The token from the access log is no longer valid, can you find a way to refresh it?
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 refresh token
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-final-jerry-account=Yikes, you are removing Jerry's account, try to delete the account of Tom
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package org.owasp.webgoat.plugin;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.hamcrest.CoreMatchers;
import org.joda.time.Days;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.owasp.webgoat.plugins.LessonTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import java.time.Duration;
import java.time.Instant;
import java.util.Date;

import static io.jsonwebtoken.SignatureAlgorithm.*;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;
import static org.owasp.webgoat.plugin.JWTSecretKeyEndpoint.JWT_SECRET;
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 JWTSecretKeyEndpointTest extends LessonTest {

@Before
public void setup() {
JWT jwt = new JWT();
when(webSession.getCurrentLesson()).thenReturn(jwt);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
when(webSession.getUserName()).thenReturn("unit-test");
}

private Claims createClaims(String username) {
Claims claims = Jwts.claims();
claims.put("admin", "true");
claims.put("user", "Tom");
claims.setExpiration(Date.from(Instant.now().plus(Duration.ofDays(1))));
claims.setIssuedAt(Date.from(Instant.now().plus(Duration.ofDays(1))));
claims.setIssuer("iss");
claims.setAudience("aud");
claims.setSubject("sub");
claims.put("username", username);
claims.put("Email", "[email protected]");
claims.put("Role", new String[]{"user"});
return claims;
}

@Test
public void solveAssignment() throws Exception {
Claims claims = createClaims("WebGoat");
String token = Jwts.builder().setClaims(claims).signWith(HS512, JWT_SECRET).compact();

mockMvc.perform(MockMvcRequestBuilders.post("/JWT/secret")
.param("token", token))
.andExpect(status().isOk())
.andExpect(jsonPath("$.lessonCompleted", is(true)));
}

@Test
public void solveAssignmentWithLowercase() throws Exception {
Claims claims = createClaims("webgoat");
String token = Jwts.builder().setClaims(claims).signWith(HS512, JWT_SECRET).compact();

mockMvc.perform(MockMvcRequestBuilders.post("/JWT/secret")
.param("token", token))
.andExpect(status().isOk())
.andExpect(jsonPath("$.lessonCompleted", is(true)));
}

@Test
public void oneOfClaimIsMissingShouldNotSolveAssignment() throws Exception {
Claims claims = createClaims("WebGoat");
claims.remove("aud");
String token = Jwts.builder().setClaims(claims).signWith(HS512, JWT_SECRET).compact();

mockMvc.perform(MockMvcRequestBuilders.post("/JWT/secret")
.param("token", token))
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-secret-claims-missing"))));
}

@Test
public void incorrectUser() throws Exception {
Claims claims = createClaims("Tom");
String token = Jwts.builder().setClaims(claims).signWith(HS512, JWT_SECRET).compact();

mockMvc.perform(MockMvcRequestBuilders.post("/JWT/secret")
.param("token", token))
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-secret-incorrect-user", "default", "Tom"))));
}

@Test
public void incorrectToken() throws Exception {
Claims claims = createClaims("Tom");
String token = Jwts.builder().setClaims(claims).signWith(HS512, "wrong_password").compact();

mockMvc.perform(MockMvcRequestBuilders.post("/JWT/secret")
.param("token", token))
.andExpect(status().isOk())
.andExpect(jsonPath("$.feedback", CoreMatchers.is(messages.getMessage("jwt-invalid-token"))));
}
}
Loading

0 comments on commit 8d7ecb1

Please sign in to comment.