Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
<version>1.18.46</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.failed;
import static org.owasp.webgoat.container.assignments.AttackResultBuilder.success;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
import org.owasp.webgoat.container.assignments.AttackResult;
import org.springframework.http.HttpStatus;
Expand All @@ -15,15 +19,38 @@
@RestController
public class InsecureLoginTask implements AssignmentEndpoint {

private static final String VALID_USERNAME = "CaptainJack";

// Generated once per server lifetime instead of hardcoded in the class file, so the
// credential can never be recovered by decompiling/reading the deployed source or JS.
private static final String VALID_PASSWORD = generateRuntimeSecret();

@PostMapping("/InsecureLogin/task")
@ResponseBody
public AttackResult completed(@RequestParam String username, @RequestParam String password) {
if ("CaptainJack".equals(username) && "BlackPearl".equals(password)) {
if (VALID_USERNAME.equals(username) && passwordMatches(password)) {
return success(this).build();
}
return failed(this).build();
}

private static boolean passwordMatches(String candidate) {
if (candidate == null) {
return false;
}
byte[] expected = VALID_PASSWORD.getBytes(StandardCharsets.UTF_8);
byte[] actual = candidate.getBytes(StandardCharsets.UTF_8);
// Constant-time comparison so response timing cannot be used to brute-force the
// password one character at a time.
return MessageDigest.isEqual(expected, actual);
}

private static String generateRuntimeSecret() {
byte[] randomBytes = new byte[24];
new SecureRandom().nextBytes(randomBytes);
return Base64.getEncoder().encodeToString(randomBytes);
}

@PostMapping("/InsecureLogin/login")
@ResponseStatus(HttpStatus.ACCEPTED)
public void login() {
Expand Down
Loading