Command Injection in Spring Boot with Hmac Signatures
Command Injection in Spring Boot with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Command injection occurs when untrusted data is concatenated into a system command and the resulting string is executed by a shell or runtime. In Spring Boot applications that use Hmac Signatures to validate request integrity, a common pattern is to include a user-controlled parameter—such as a filename, path, or identifier—in the data that is signed. If the server later uses that same parameter in a shell command without proper validation, an attacker can supply payloads that break out of the expected argument boundaries and execute arbitrary commands.
Consider a scenario where a Spring Boot endpoint accepts a fileId query parameter, includes it in the signed string (alongside timestamp and other fields), and then passes the fileId to a shell command for processing. If the developer constructs the command via string concatenation or interpolation, an attacker can supply something like abc; cat /etc/passwd or abc && id. Because the Hmac Signature verifies integrity but not safety, the server may trust the parameter and forward it directly to Runtime.getRuntime().exec() or ProcessBuilder, leading to command injection.
The Hmac Signature mechanism itself does not introduce the vulnerability; rather, the risk arises when the signed data includes values that the application later uses in dangerous contexts. For example, if the signature covers a filePath field and the server uses that field in a shell command such as tar -xzf {filePath}, an attacker can attempt to append shell metacharacters to achieve command injection. Because the signature validates that the field has not been tampered with, the developer may incorrectly assume that the content is safe, overlooking the need for input validation and command construction safeguards.
In Spring Boot, this often manifests in code that uses String.split, custom parsers, or external scripts where untrusted values are interpolated into commands. Even if the Hmac Signature ensures the request has not been modified in transit, the application must treat all inputs—including those covered by the signature—as untrusted when building commands. OWASP API Top 10 A03:2023 Injection remains relevant here, as API endpoints that process signed requests can still be abused through improper command construction.
Real-world analogues include cases where directory traversal or path manipulation combined with command execution leads to unauthorized file access or remote code execution. For instance, an attacker might supply a filename like ../../../etc/passwd | ls if the server builds a shell command by simple concatenation. Because the Hmac Signature may still verify, the server proceeds to execute the command, exposing sensitive data or enabling further compromise. Proper mitigation requires strict input validation, avoiding shell metacharacters, using parameterized commands or APIs that bypass the shell, and ensuring that signed fields are not directly used in dangerous contexts.
Hmac Signatures-Specific Remediation in Spring Boot — concrete code fixes
To mitigate command injection while continuing to use Hmac Signatures in Spring Boot, redesign the flow so that signed parameters are never concatenated into shell commands. Instead, use structured APIs and strict allowlists. Below are concrete, safe patterns and code examples.
- Validate and restrict parameter values before use: enforce an allowlist of permitted values for identifiers used in commands. For example, if the signed parameter is a file ID, map it to a known safe path on the server rather than passing the raw value to a shell.
- Use Java’s
ProcessBuilderwith a list of arguments instead of shell strings, and avoid invoking/bin/sh -c. This prevents the shell from interpreting metacharacters. - Keep sensitive operations outside the shell: perform file extraction, parsing, or data transformations using Java libraries rather than invoking external utilities.
Example of an unsafe pattern to avoid:
String unsafeCmd = "tar -xzf " + userSuppliedFileId;
Runtime.getRuntime().exec(unsafeCmd);
Example of a safe remediation using ProcessBuilder and an allowlist mapping:
import java.util.List;
import java.util.Map;
import java.util.HashMap;
@Service
public class FileProcessingService {
// Allowlist mapping from signed fileId to safe filesystem path
private static final Map<String, String> ALLOWED_FILES = Map.of(
"report2024", "/data/reports/report2024.tar.gz",
"backup01", "/data/backups/backup01.tar.gz"
);
public void extractFile(String fileId) {
String safePath = ALLOWED_FILES.get(fileId);
if (safePath == null) {
throw new IllegalArgumentException("Invalid file identifier");
}
// Use ProcessBuilder with a list; no shell involved
ProcessBuilder pb = new ProcessBuilder(
"tar", "-xzf", safePath, "-C", "/data/extract"
);
pb.redirectErrorStream(true);
try {
Process p = pb.start();
int exitCode = p.waitFor();
if (exitCode != 0) {
// handle failure
}
} catch (Exception e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Extraction failed", e);
}
}
}
If you must sign and verify parameters that influence behavior, ensure the signed scope is limited and that runtime usage does not involve shell interpretation. For example, sign a contract that includes an action enum and a resource ID, then map the combination to a server-side routine:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class HmacUtil {
private static final String HMAC_ALGORITHM = "HmacSHA256";
public static String computeHmac(String data, String secret) throws Exception {
Mac mac = Mac.getInstance(HMAC_ALGORITHM);
SecretKeySpec key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_ALGORITHM);
mac.init(key);
return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes(StandardCharsets.UTF_8)));
}
}
In your controller, verify the Hmac Signature, then route to a predefined handler instead of building a command from raw values:
@RestController
@RequestMapping("/api/files")
public class FileController {
@GetMapping("/extract")
public ResponseEntity<String> extract(
@RequestParam String fileId,
@RequestParam String timestamp,
@RequestParam String signature) throws Exception {
String dataToSign = fileId + timestamp;
String expected = HmacUtil.computeHmac(dataToSign, System.getenv("HMAC_SECRET"));
if (!expected.equals(signature)) {
return ResponseEntity.status(403).body("Invalid signature");
}
// Safe routing based on allowlist, no shell usage
fileProcessingService.extractFile(fileId);
return ResponseEntity.ok("Extraction scheduled");
}
}
By combining Hmac Signatures with strict input validation, allowlists, and shell-agnostic execution, you preserve integrity checks while eliminating command injection risk. middleBrick scans can help identify such injection-prone endpoints in unauthenticated testing, and the Pro plan’s continuous monitoring can detect regressions if parameters or handlers change.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |