Command Injection Prevention
Command Injection Prevention
Command injection occurs when user input is incorporated into system commands without proper sanitization. APIs that interact with system commands, process files, or execute external programs are particularly vulnerable. Preventing command injection requires careful design and multiple security layers.
// Java command injection prevention examples
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class SecureCommandExecutor {
private static final Set<String> ALLOWED_COMMANDS = new HashSet<>(Arrays.asList(
"convert", "ffmpeg", "pdftotext", "zip", "unzip"
));
private static final Pattern SAFE_FILENAME = Pattern.compile("^[a-zA-Z0-9._-]+$");
private static final Pattern SAFE_PARAMETER = Pattern.compile("^[a-zA-Z0-9._=-]+$");
public static class CommandResult {
public final int exitCode;
public final String output;
public final String error;
public CommandResult(int exitCode, String output, String error) {
this.exitCode = exitCode;
this.output = output;
this.error = error;
}
}
/**
* Securely execute image conversion
*/
public CommandResult convertImage(String inputFile, String outputFormat)
throws SecurityException, IOException {
// Validate input filename
if (!SAFE_FILENAME.matcher(inputFile).matches()) {
throw new SecurityException("Invalid input filename");
}
// Validate output format
Set<String> allowedFormats = new HashSet<>(Arrays.asList("jpg", "png", "gif", "webp"));
if (!allowedFormats.contains(outputFormat.toLowerCase())) {
throw new SecurityException("Invalid output format");
}
// Build safe output filename
String outputFile = inputFile.replaceFirst("\\.[^.]+$", "") + "." + outputFormat;
// Use ProcessBuilder for safe command execution
ProcessBuilder pb = new ProcessBuilder(
"convert",
inputFile,
"-resize", "800x600>", // Limit size for safety
"-strip", // Remove metadata
outputFile
);
// Set working directory to sandbox
pb.directory(new File("/tmp/image-sandbox"));
// Set environment variables
Map<String, String> env = pb.environment();
env.clear(); // Clear all environment variables
env.put("PATH", "/usr/bin:/bin"); // Minimal PATH
return executeCommand(pb);
}
/**
* Securely execute commands with timeout
*/
private CommandResult executeCommand(ProcessBuilder pb) throws IOException {
Process process = pb.start();
// Read output and error streams
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
try (BufferedReader outputReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
BufferedReader errorReader = new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
// Set timeout
boolean finished = process.waitFor(30, TimeUnit.SECONDS);
if (!finished) {
process.destroyForcibly();
throw new IOException("Command timed out");
}
// Read output
String line;
while ((line = outputReader.readLine()) != null) {
output.append(line).append("\n");
}
while ((line = errorReader.readLine()) != null) {
error.append(line).append("\n");
}
return new CommandResult(
process.exitValue(),
output.toString(),
error.toString()
);
} catch (InterruptedException e) {
process.destroyForcibly();
throw new IOException("Command interrupted", e);
}
}
/**
* Example of UNSAFE command execution - NEVER DO THIS
*/
public void unsafeExample(String userInput) {
// NEVER DO THIS - Direct command execution with user input
Runtime.getRuntime().exec("convert " + userInput + " output.jpg");
// This allows injection like: input.jpg; rm -rf /
}
}
// Spring Boot REST controller
@RestController
@RequestMapping("/api/convert")
public class ImageConversionController {
private final SecureCommandExecutor executor = new SecureCommandExecutor();
@PostMapping("/image")
public ResponseEntity<?> convertImage(
@RequestParam("file") MultipartFile file,
@RequestParam("format") String format) {
try {
// Save uploaded file with safe name
String safeFilename = UUID.randomUUID().toString() + "_" +
file.getOriginalFilename().replaceAll("[^a-zA-Z0-9._-]", "");
Path tempFile = Files.createTempFile("upload_", safeFilename);
file.transferTo(tempFile.toFile());
// Execute conversion
CommandResult result = executor.convertImage(
tempFile.getFileName().toString(),
format
);
if (result.exitCode == 0) {
// Return converted file
Resource resource = new FileSystemResource(convertedFile);
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_JPEG)
.body(resource);
} else {
return ResponseEntity.status(500)
.body(Map.of("error", "Conversion failed", "details", result.error));
}
} catch (SecurityException e) {
return ResponseEntity.status(400)
.body(Map.of("error", e.getMessage()));
} catch (IOException e) {
return ResponseEntity.status(500)
.body(Map.of("error", "Processing failed"));
}
}
}
Input validation is not a one-time activity but an ongoing security practice that must evolve with your API. The next chapter explores API key management, building on the secure foundation that proper input validation provides.## API Key Management Best Practices
API keys serve as the primary authentication mechanism for many APIs, making their proper management critical for security. Poor API key practices can lead to unauthorized access, data breaches, and abuse of services. This chapter provides comprehensive guidance on implementing secure API key systems, from generation and storage to rotation and revocation, ensuring your API keys strengthen rather than weaken your security posture.