Type | Severity | Description | File | Confidence | |
---|---|---|---|---|---|
â–¶ | SQL Injection | Error | SQL Injection via string concatenation in ApiController.getUser user lookup query. | src/main/java/hawk/controller/ApiController.java | 10 |
Code Context
21
String query = "SELECT * FROM users WHERE username = '" + username + "'";
22
List<?> users = jdbcTemplate.queryForList(query);
Additional Details
Description:
SQL Injection via string concatenation in ApiController.getUser user lookup query.
Exploitable:
True
Explanation:
STEP 1 (Vulnerable function/params): ApiController.getUser(@RequestParam String username) constructs SQL via string concatenation and executes it using jdbcTemplate.queryForList().
STEP 2 (Callers): getUser is an HTTP GET handler mapped to /api/user-lookup by Spring MVC.
STEP 3 (Trace each caller): Spring invokes getUser for requests to GET /api/user-lookup.
STEP 4 (External input source): The method parameter username is annotated with @RequestParam and is populated directly from the HTTP query string (e.g., /api/user-lookup?username=...). This is attacker-controlled input.
STEP 5 (Validation/sanitization): No validation, sanitization, or parameterization is applied to username before it is concatenated into the SQL string.
STEP 6 (Sink): The concatenated query string is passed to jdbcTemplate.queryForList(query), which sends the SQL to the database.
PATH SUMMARY: HTTP Request (query parameter "username") -> Spring MVC binding (@RequestParam) -> ApiController.getUser(username) -> String concatenation into SQL (line 21) -> jdbcTemplate.queryForList(query) (line 22) -> Database.
Therefore, an attacker can supply input such as ' OR '1'='1 to alter the SQL logic, enabling Boolean/time-based injection and potentially stacked queries depending on DB/driver configuration.
Impact Assessment:
High. Confidentiality: Potential inference of data via boolean/time-based SQLi; if results were returned or error messages exposed, could enable broader data extraction. Integrity: If the DB/driver permits stacked queries or dangerous functions, attacker could modify or delete data. Availability: Crafted payloads may induce expensive queries or errors, leading to degraded service.
Attack Complexity:
Low
Attack Vectors:
• HTTP GET /api/user-lookup?username=' OR '1'='1
• HTTP GET /api/user-lookup?username=' UNION SELECT NULL--
• HTTP GET /api/user-lookup?username=';--
• Time-based payloads (e.g., username=' OR SLEEP(5)-- for MySQL or pg_sleep(5) for PostgreSQL)
Remediation:
Use parameterized queries. Example: String sql = "SELECT * FROM users WHERE username = ?"; List<Map<String,Object>> users = jdbcTemplate.queryForList(sql, username); Alternatively use NamedParameterJdbcTemplate with named parameters. Do not build SQL with string concatenation. Optionally apply strict server-side validation/allowlisting for username format and ensure the database user has least privileges. Consider disabling multi-statements at the driver level and enabling query parameterization enforcement.
|
|||||
â–¶ | SSRF | Error | Server-Side Request Forgery (SSRF): endpoint fetches attacker-supplied URL without validation. | src/main/java/hawk/controller/ApiController.java | 10 |
Code Context
28
public ResponseEntity<String> fetchUrl(@RequestParam String targetUrl) {
29
String response = restTemplate.getForObject(targetUrl, String.class);
30
return ResponseEntity.ok(response);
31
}
Additional Details
Description:
Server-Side Request Forgery (SSRF): endpoint fetches attacker-supplied URL without validation.
Exploitable:
True
Explanation:
Input tracing (MANDATED):
STEP 1 (Vulnerable function): ApiController.fetchUrl(@RequestParam String targetUrl) at src/main/java/hawk/controller/ApiController.java:28-30. It calls restTemplate.getForObject(targetUrl, String.class) directly with user input.
STEP 2 (Callers): This controller method is invoked by Spring MVC via @GetMapping("/client-request") with class-level @RequestMapping("/api"). There are no internal code callers.
STEP 3 (Parameter flow): targetUrl is bound from the HTTP query parameter "targetUrl" (e.g., GET /api/client-request?targetUrl=...). No transformations occur before use.
STEP 4 (External source): External HTTP request -> Spring parameter binding -> targetUrl -> restTemplate.getForObject(...).
STEP 5 (Validation/Sanitization): None. No checks on scheme/host/IP, no allowlist, no redirect controls, no DNS/IP range validation.
STEP 6 (Reachability): Endpoint path is /api/client-request. Security config (MultiHttpSecurityConfig, order 4) requires authentication for unspecified paths; thus this route is behind form login. However, it is reachable to any authenticated user, and the app defines in-memory users, making exploitation by a logged-in user feasible.
Conclusion: An authenticated user can fully control targetUrl leading to outbound requests to internal or protected hosts (localhost, 127.0.0.1, ::1, 169.254.169.254, RFC1918 ranges), enabling SSRF.
Impact Assessment:
High. Confidentiality: potential access to internal services and cloud metadata endpoints (token/credentials leakage). Integrity: may allow interacting with internal admin/debug endpoints to modify state if accessible. Availability: can be abused for blind/slow SSRF and internal port scans causing resource exhaustion.
Attack Complexity:
Low
Attack Vectors:
• GET /api/client-request?targetUrl=http://127.0.0.1:8080/admin
• GET /api/client-request?targetUrl=http://169.254.169.254/latest/meta-data/iam/security-credentials/
• GET /api/client-request?targetUrl=http://internal-service.local:80/health
Remediation:
Enforce strict outbound request validation: 1) Parse the user input as a URI and enforce an allowlist of exact hosts or domains you explicitly control; reject everything else. 2) Restrict schemes to https (and optionally http) only; reject others. 3) Resolve the hostname and block private/loopback/link-local/multicast ranges (e.g., 127.0.0.0/8, ::1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, fc00::/7) and ensure post-redirect targets also pass these checks. 4) Disable or strictly limit redirects on the HTTP client, or re-validate the final URL after redirects. 5) If only specific third-party APIs are needed, replace free-form URL input with a server-side identifier (ID -> URL mapping). 6) Apply egress network controls (firewall/proxy) to prevent calls to internal IP ranges. 7) Add connection and read timeouts, and logging/monitoring for denied attempts. Example fix: validate targetUrl against an allowlist and perform DNS resolution with IP range checks before executing RestTemplate calls.
|
|||||
â–¶ | Broken Access Control | Error | Broken access control: client-controlled isAdmin/currentUserId used for authorization in user-data endpoint. | src/main/java/hawk/controller/ApiController.java | 10 |
Code Context
35
public ResponseEntity<String> getUserData(@PathVariable int userId, @RequestParam int currentUserId, @RequestParam boolean isAdmin) {
36
if (userId == currentUserId || isAdmin) {
37
return ResponseEntity.ok("Here is the user data");
38
}
39
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");
40
}
Additional Details
Description:
Broken access control: client-controlled isAdmin/currentUserId used for authorization in user-data endpoint.
Exploitable:
True
Explanation:
MANDATORY INPUT TRACING
STEP 1: Vulnerable function and parameters
- Function: hawk.controller.ApiController.getUserData(int userId, int currentUserId, boolean isAdmin)
- Vulnerability: Authorization decision uses user-supplied currentUserId and isAdmin.
STEP 2: Find all function callers
- Caller: Spring MVC framework invokes this method on HTTP GET /api/user-data/{userId} due to @GetMapping("/user-data/{userId}"). No internal code calls it.
STEP 3–4: Trace each caller backwards to external input
- External source: HTTP request handled by Spring. Parameters are bound as follows:
- @PathVariable int userId <= URL path segment /api/user-data/{userId}
- @RequestParam int currentUserId <= HTTP query parameter currentUserId
- @RequestParam boolean isAdmin <= HTTP query parameter isAdmin
These are fully attacker-controlled by the client making the request.
STEP 5–7: Validation/sanitization along the path
- No server-side validation or sanitization of currentUserId or isAdmin prior to use in the authorization check. They are used directly in: if (userId == currentUserId || isAdmin) { ... }
- No use of authenticated principal or server-side role/claims in this check.
STEP 8: Reachability and constraints
- Security config (MultiHttpSecurityConfig, Order 4) requires authentication for requests not explicitly permitted. /api/user-data/** is not whitelisted, so an authenticated user is required. However, once authenticated with any user, the client can set isAdmin=true or set currentUserId to the target userId to pass the check. Thus, the code is reachable by any authenticated user and is trivially exploitable.
Conclusion: The inputs userId (path), currentUserId (query), and isAdmin (query) are attacker-controlled HTTP request data. They directly govern access, enabling horizontal privilege escalation by setting currentUserId to the target’s userId, and vertical privilege escalation by setting isAdmin=true.
Impact Assessment:
High. An authenticated low-privileged user can view any user’s data by manipulating request parameters. This is both horizontal (masquerading as another user by aligning currentUserId with userId) and vertical privilege escalation (isAdmin=true). Potential impact includes unauthorized disclosure of user data (Confidentiality), potential follow-on actions if extended (Integrity), and potential abuse at scale (Availability indirectly via mass scraping).
Attack Complexity:
Low
Attack Vectors:
• GET /api/user-data/42?currentUserId=42&isAdmin=false
• GET /api/user-data/42?currentUserId=1&isAdmin=true
• Any authenticated HTTP client can craft the above requests; no special headers or tokens beyond normal authentication are required.
Remediation:
Do not trust client-supplied identity or role flags. Use the authenticated principal and server-side roles from Spring Security. Remove currentUserId and isAdmin from the API contract.
- Option A (recommended): Use server-side principal and role checks:
@GetMapping("/user-data/{userId}")
public ResponseEntity<String> getUserData(@PathVariable int userId, Authentication auth) {
boolean isAdmin = auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
Integer principalId = ((YourUserDetails) auth.getPrincipal()).getId();
if (isAdmin || userId == principalId) { return ok(...); }
return status(UNAUTHORIZED).body("Unauthorized");
}
- Option B: Enforce with method security:
Enable @EnableGlobalMethodSecurity(prePostEnabled=true) and use @PreAuthorize("hasRole('ADMIN') or #userId == principal.id").
- Update clients to stop sending currentUserId and isAdmin; rely entirely on server-side context.
- Add integration tests asserting that requests cannot elevate by setting isAdmin/currentUserId in the query.
- Consider endpoint-level antMatchers/authorization rules to limit access if user IDs are sensitive (e.g., owner-only by policy).
|
|||||
â–¶ | IDOR | Error | IDOR: /api/user-data/{userId} authorizes access using attacker-controlled request parameters (currentUserId, isAdmin) instead of the authenticated principal. | src/main/java/hawk/controller/ApiController.java | 9 |
Code Context
34
@GetMapping("/user-data/{userId}")
35
public ResponseEntity<String> getUserData(@PathVariable int userId, @RequestParam int currentUserId, @RequestParam boolean isAdmin) {
36
if (userId == currentUserId || isAdmin) {
37
return ResponseEntity.ok("Here is the user data");
38
}
39
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");
40
}
Additional Details
Description:
IDOR: /api/user-data/{userId} authorizes access using attacker-controlled request parameters (currentUserId, isAdmin) instead of the authenticated principal.
Exploitable:
True
Explanation:
STEP 1: Vulnerable function and parameters
- Function: hawk.controller.ApiController.getUserData(int userId, int currentUserId, boolean isAdmin)
- Vulnerable authorization condition: if (userId == currentUserId || isAdmin)
- Attacker-controllable inputs: userId (path variable), currentUserId (query param), isAdmin (query param)
STEP 2: Find all function callers
- This is a Spring MVC endpoint mapped at GET /api/user-data/{userId}. It is invoked directly by the Spring DispatcherServlet based on HTTP requests. No internal application callers were found.
STEP 3-4: Trace callers back to external input source
- The parameters are bound directly from the HTTP request:
- userId <- HTTP path variable {userId}
- currentUserId <- HTTP query parameter ?currentUserId=
- isAdmin <- HTTP query parameter ?isAdmin=
- External Input Source: HTTP Request (high risk, attacker-controlled).
STEP 5-7: Validation/sanitization along the path
- No server-side validation ties currentUserId or isAdmin to the authenticated user identity or authorities.
- No lookup of the authenticated principal (e.g., SecurityContext) is performed. The decision relies solely on client-supplied values.
STEP 8: Can an attacker reach and control inputs?
- Yes. Any client can supply the path variable and query parameters in the HTTP request. According to MultiHttpSecurityConfig, this path falls under the default configuration requiring authentication; however, once authenticated as any user, the client can arbitrarily set currentUserId and/or isAdmin to bypass authorization.
Why this is exploitable:
- Privilege escalation: Setting isAdmin=true alone satisfies the condition, granting access regardless of actual role.
- IDOR: Even without isAdmin, a user can choose any target id and set currentUserId to the same value to pass the check (userId == currentUserId), because currentUserId is not derived from the session/principal.
Conclusion: The authorization decision is based on attacker-controlled inputs from the HTTP request and is not bound to the authenticated identity, making this a clear, trivially exploitable IDOR/authorization bypass.
Impact Assessment:
If exploited, an authenticated user can access arbitrary users' data by manipulating query parameters (confidentiality breach). If the endpoint returned sensitive PII or account details, this would enable horizontal privilege escalation and mass data exposure. Integrity and availability are not directly affected by this endpoint, but the pattern could extend to modification endpoints, compounding impact.
Attack Complexity:
Low
Attack Vectors:
• GET /api/user-data/42?currentUserId=42
• GET /api/user-data/42?isAdmin=true
• Enumeration of userId values combined with either vector above
Remediation:
Enforce server-side authorization bound to the authenticated principal; never accept authorization context from the client. Recommendations:
- Remove currentUserId and isAdmin from request parameters.
- Derive the current user from SecurityContext (e.g., inject Principal or Authentication) and compare it to the target userId server-side.
- Use method-level security, e.g., @PreAuthorize("hasRole('ADMIN') or #userId == principal.id") with a proper Principal that exposes the user id.
- For admin checks, rely on authorities (hasRole('ADMIN')) rather than a client-provided boolean.
- Optionally, centralize ownership checks in the service layer and retrieve user records by authenticated identity, not by a free-form userId unless the caller has explicit privileges.
- Add integration tests ensuring non-admin users cannot access other users’ resources and that admin status cannot be faked via request parameters.
|
|||||
â–¶ | SQL Injection | Error | Triage: SQL Injection in ApiController.login() via concatenating HTTP request parameter 'username' into SQL. | src/main/java/hawk/controller/ApiController.java | 10 |
Code Context
45
String userQuery = "SELECT COUNT(*) FROM users WHERE username = '" + username + "'";
46
Integer userCount = jdbcTemplate.queryForObject(userQuery, Integer.class);
Additional Details
Description:
Triage: SQL Injection in ApiController.login() via concatenating HTTP request parameter 'username' into SQL.
Exploitable:
True
Explanation:
Input tracing confirms exploitable SQL Injection. STEP 1 (Vulnerable function): hawk.controller.ApiController.login(@RequestParam String username, @RequestParam String password). STEP 2 (Vulnerable parameters): The local variable 'userQuery' directly concatenates 'username'. STEP 3 (Callers): This method is a Spring @PostMapping handler for POST /api/login; it is invoked by the Spring MVC framework. STEP 4 (Parameter passing): Spring binds the HTTP request parameter 'username' into the method parameter 'username'. STEP 5 (External input source): HTTP request parameter 'username' (attacker-controlled). STEP 6 (Validation/sanitization): No sanitization or parameterization applied before constructing SQL. STEP 7 (Sink): jdbcTemplate.queryForObject(userQuery, Integer.class) executes the concatenated SQL. Security configuration context: The endpoint /api/login falls under the default FormLogin security config (Order 4), which requires authentication and enforces CSRF for POST requests (csrf not disabled). However, this application registers in-memory users ("user"/"password", "janesmith"/"password"), making it trivial for an external actor to authenticate and obtain a CSRF token, then submit a crafted POST to /api/login. Therefore, the code is reachable by an attacker and the 'username' parameter is fully controllable. Practical impact via this specific sink: The query is 'SELECT COUNT(*) ... WHERE username = '<username>''. An attacker can alter the WHERE clause (e.g., username='x' OR '1'='1 --) to manipulate the count. Stacked queries are typically not allowed by JDBC drivers in executeQuery without special flags, limiting direct data extraction or DML. Still, boolean-based injection is achievable and may influence logic or enable inference via errors. No mitigating validation is present in the data flow.
Impact Assessment:
Confidentiality: Low to Moderate (this specific COUNT(*) sink limits direct data extraction but allows boolean-based inference; error-based or time-based techniques may be possible depending on DB error handling). Integrity: Low in this context (stacked queries likely disallowed; no direct DML from this sink). Availability: Low (could be abused for expensive boolean logic, but typical impact is limited). Business logic: The injection can force the username existence check to return non-zero counts, altering control flow and potentially aiding user enumeration.
Attack Complexity:
Medium
Attack Vectors:
• HTTP POST /api/login with crafted username parameter, e.g., username=' OR '1'='1 --
• Authenticated session required by security config (FormLogin) plus CSRF token for POST; in this app, default in-memory credentials make this trivial to obtain
Remediation:
Use parameterized queries/prepared statements. For example: Integer userCount = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users WHERE username = ?", Integer.class, username); Do not concatenate user input into SQL. Additionally, avoid revealing whether a username exists (return a single generic message) to reduce enumeration value. Consider placing /api endpoints intended for unauthenticated use under an antMatcher that permits anonymous access if appropriate, or remove this redundant login endpoint. Retain CSRF protection for state-changing endpoints. Add input validation only as a defense-in-depth measure; do not rely on escaping.
|
|||||
â–¶ | XSS | Error | Reflected XSS by echoing unsanitized input into HTML response. | src/main/java/hawk/controller/ApiController.java | 10 |
Code Context
55
public ResponseEntity<String> xssVuln(@RequestParam String input) {
56
return ResponseEntity.ok("<html><body>" + input + "</body></html>");
57
}
Additional Details
Description:
Reflected XSS by echoing unsanitized input into HTML response.
Exploitable:
True
Explanation:
STEP 1 – Vulnerable function and parameter: The vulnerable function is ApiController.xssVuln(@RequestParam String input) in src/main/java/hawk/controller/ApiController.java lines 55–56. The attacker-controlled parameter is input (annotated with @RequestParam) which is directly concatenated into the response body as HTML without encoding.
STEP 2 – All callers: This method is an HTTP handler mapped by Spring MVC via @RequestMapping("/api") on the class and @GetMapping("/content") on the method. It is invoked by the Spring DispatcherServlet when a client issues GET /api/content with a query parameter named input.
STEP 3 – Trace each caller backwards: There are no in-code callers; the only entry is the HTTP request handler. Spring binds the request parameter "input" from the query string or form data directly into the method parameter.
STEP 4 – External input source: HTTP Request (query parameter). Example: GET /api/content?input=<script>alert(1)</script>.
STEP 5 – Validation/sanitization: None. The value of input is not validated or encoded. It is concatenated into the HTML string and returned.
STEP 6 – Reachability and controls: The endpoint resides under /api/content. By default security configuration (src/main/java/hawk/MultiHttpSecurityConfig.java), endpoints not matching /api/jwt/**, /api/token/**, or /api/basic/** fall under the form-login configuration (Order 4). These routes require authentication, but the application configures in-memory users with known credentials (user/password, janesmith/password), making it trivial for an attacker to authenticate and reach the endpoint. No CSRF is relevant for GET. Therefore, an attacker can supply arbitrary input via HTTP and have it reflected.
Note on content-type: The method returns ResponseEntity.ok(String) without explicitly setting a content type. In typical Spring MVC defaults, StringHttpMessageConverter returns text/plain unless configured otherwise. If the response is served as text/plain with X-Content-Type-Options: nosniff, most modern browsers will not execute HTML. However, the code intends to generate HTML ("<html><body>" + input + "</body></html>") and many deployments configure or negotiate text/html for such endpoints. Regardless, the safe and correct fix is to apply context-appropriate HTML encoding or set a safe content type if HTML rendering is not intended. As implemented, the sink is an unencoded HTML context, so if rendered as text/html, it is directly exploitable reflected XSS.
Impact Assessment:
If exploited as HTML, an attacker can execute arbitrary JavaScript in the application origin, leading to session hijacking (steal cookies/JSESSIONID if not HttpOnly), arbitrary actions as the victim (CSRF-by-JS), defacement, or data exfiltration via same-origin requests. Confidentiality: High (read user-accessible data via authenticated APIs). Integrity: High (perform state-changing actions). Availability: Low (possible UI lockups but not primary).
Attack Complexity:
Low
Attack Vectors:
• HTTP GET: /api/content?input=%3Cscript%3Ealert(1)%3C/script%3E
• HTTP GET: /api/content?input=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E
• Embedded link in phishing/email that directs an authenticated user to /api/content with a malicious input parameter
Remediation:
Apply context-appropriate output encoding before placing user input into HTML. For example: return ResponseEntity.ok("<html><body>" + HtmlUtils.htmlEscape(input) + "</body></html>"); or better, render via a templating engine that auto-escapes (e.g., Thymeleaf). If HTML rendering is not required, return as plain text explicitly: return ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN).body(input); Also consider adding a strict Content Security Policy (e.g., Content-Security-Policy: default-src 'self'; script-src 'self') to mitigate impact, and ensure X-Content-Type-Options: nosniff is enabled (Spring Security default). Finally, validate/normalize input if appropriate and add unit/integration tests to prevent regression.
|
|||||
â–¶ | Broken Authentication | Error | Broken authentication in email verification: token is just userId and timestamp with only a time-window check. | src/main/java/hawk/controller/ApiController.java | 9 |
Code Context
72
public ResponseEntity<String> verify(@RequestParam String userId, @RequestParam long timestamp) {
73
long now = System.currentTimeMillis();
74
if (now - timestamp < 300000) {
75
return ResponseEntity.ok("Email verified for user: " + userId);
76
}
Additional Details
Description:
Broken authentication in email verification: token is just userId and timestamp with only a time-window check.
Exploitable:
True
Explanation:
STEP 1: Vulnerable function and parameters
- Function: ApiController.verify
- File: src/main/java/hawk/controller/ApiController.java
- Parameters: userId (String), timestamp (long)
STEP 2: Callers of the function
- This is a Spring MVC controller entrypoint mapped to GET /api/verify-email via @GetMapping("/verify-email"). It is invoked directly by HTTP requests; there are no in-repo callers other than the framework.
STEP 3 and 4: Trace each caller backwards to input
- Input source: HTTP query parameters userId and timestamp via @RequestParam. These values are attacker-supplied from the HTTP request.
STEP 5: Continue until external input source
- External source reached: HTTP request query string.
Security controls along the path
- No validation/sanitization beyond checking that (now - timestamp) < 300000 (5 minutes). There is no signature, MAC, randomness, or server-side stored token. The userId is not bound to the authenticated user identity.
- Access control: Per MultiHttpSecurityConfig (Order 4 FormLoginWebSecurityConfigurerAdapter), /api/verify-email is NOT in the permitAll list and is not matched by the JWT/Token/Basic security adapters, so it requires a logged-in session. Thus, exploitation requires an authenticated session. However, any authenticated user can supply arbitrary userId and a fresh timestamp to trigger a successful "verification" of another account. The inputs remain fully attacker-controlled once authenticated.
Exploitability conclusion
- Attacker-controlled parameters: userId and timestamp from HTTP request.
- Reachability: Yes, via GET /api/verify-email?userId=<victim>×tamp=<currentMillis> for any authenticated user.
- There is no server-side token verification, no binding of token to subject, and only a time window check. Therefore, the verification can be forged. In this code, the endpoint only returns a success message without persisting state, but if relied upon by a client or future code to mark accounts verified, it would allow verifying arbitrary accounts.
Impact Assessment:
If this endpoint were used to mark email verification state, an attacker with any authenticated account could verify arbitrary users' emails, impacting integrity of account state and potentially enabling privilege or feature escalation tied to verified status. In the current code, impact appears limited to spoofed success responses (no database update), but it could mislead clients that use the response to advance verification flows. Confidentiality: none directly. Integrity: potentially high if integrated with account state. Availability: none.
Attack Complexity:
Low
Attack Vectors:
• HTTP GET to /api/verify-email with arbitrary userId and a current timestamp (within 5 minutes)
• Automated scripts iterating userId values and current timestamps to mass-verify
Remediation:
Replace the scheme with secure, verifiable tokens: 1) Generate a cryptographically random, single-use token per verification request, store it server-side (or as an HMAC-signed token with a server-secret) with associated userId and expiry. 2) In the verify handler, require the token parameter only, look up or validate it server-side, ensure it matches the intended user, enforce expiry, and invalidate after use. 3) Do not accept userId and timestamp directly from the client as proof. 4) Bind the verification action to the token's subject, not the session user, or allow anonymous verification via the emailed token. 5) Log and rate-limit verification attempts. Example: issue UUIDv4 token stored in DB with userId and expiresAt; verify by token lookup and delete on success. Alternatively, use HMAC(userId || expiresAt || nonce) with a server-side secret and verify signature and expiry.
|
|||||
â–¶ | Broken Access Control | Error | Broken access control: /api/secure-download authorizes solely by token length. | src/main/java/hawk/controller/ApiController.java | 9 |
Code Context
80
@GetMapping("/secure-download")
81
public ResponseEntity<String> download(@RequestParam String token) {
82
if (token.length() > 10) {
83
return ResponseEntity.ok("Here's your file");
84
}
85
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token");
86
}
Additional Details
Description:
Broken access control: /api/secure-download authorizes solely by token length.
Exploitable:
True
Explanation:
STEP 1 – Vulnerable function and parameters:
- Function: hawk.controller.ApiController.download(@RequestParam String token)
- Vulnerable check: token.length() > 10 is used as the sole authorization gate.
- Attacker-controllable variable: token (directly from HTTP request query parameter)
STEP 2 – Find all function callers:
- There are no in-repo code callers; the method is invoked by Spring MVC via the @GetMapping("/secure-download") route under class-level @RequestMapping("/api").
STEP 3/4 – Trace each caller backwards to external input:
- External input source: HTTP request to GET /api/secure-download with query parameter token. Spring binds the query parameter to the method parameter token without any sanitization or validation.
STEP 5 – Validation/Sanitization along the path:
- No validation other than a length check (>10). There is no cryptographic verification, lookup against server-side state, expiration, or association with a user/resource. No sanitization impacts authorization.
STEP 6 – Reachability and constraints:
- Security chain: MultiHttpSecurityConfig (Order 4) secures all requests not matched by more specific chains and requires authentication (.anyRequest().authenticated()). /api/secure-download is not included in any permitAll list and is not under /api/jwt/**, /api/token/**, or /api/basic/**. Therefore, an attacker must be authenticated to reach this method.
- Default users are configured in code (user/password and janesmith/password), making authentication trivial in typical dev/test deployments. Even without default creds, any authenticated user can supply an arbitrary token.
Conclusion: The code is reachable by an authenticated user, and the token parameter is fully attacker-controlled via HTTP. Authorization is effectively absent because any token longer than 10 characters grants access.
Impact Assessment:
Unauthorized access to the protected download endpoint by any authenticated user simply by supplying any token longer than 10 characters. In the provided code the response is a static string, but in a real implementation this pattern would allow disclosure of protected files or data, impacting confidentiality (high) and potentially integrity/availability if downloads trigger side effects.
Attack Complexity:
Low
Attack Vectors:
• HTTP GET /api/secure-download?token=AAAAAAAAAAA (any string length > 10)
• Authenticated session (Form login) using default credentials (user/password) if deployed as-is
Remediation:
Replace the length-based check with proper authorization. Recommended: (1) Require authentication and role/permission checks for this endpoint; (2) Use cryptographically strong, server-issued tokens mapped to server-side records (e.g., a DB table with token value hash, owner, scope, resource, and expiration) and validate by lookup; or validate signed JWTs with audience/scope/exp and resource binding; (3) Enforce short expirations and one-time use where appropriate; (4) Avoid using user-controlled flags for authorization decisions; (5) Add comprehensive tests that reject arbitrary tokens and verify only valid server-recognized tokens grant access.
|