TL;DR
Code injection happens when malicious code is inserted into your C/C++ program, often through user input. This guide shows you how to protect against it using safe functions, input validation, and secure coding practices.
1. Understand Code Injection
Code injection exploits vulnerabilities where untrusted data (like what a user types) is treated as code. Common types include:
- Format String Bugs: Using user input directly in functions like
printfwithout proper sanitisation. - SQL Injection: Injecting malicious SQL commands into database queries. (Less common in pure C/C++, but relevant if your program interacts with a database).
- Command Injection: Injecting shell commands into system calls.
2. Use Safe Functions
Avoid dangerous functions that don’t perform bounds checking or sanitisation.
- Instead of
strcpy, usestrncpy:strncpylimits the number of characters copied, preventing buffer overflows.char dest[20]; const char *src = "This is a long string"; sncpy(dest, src, sizeof(dest) - 1); dest[sizeof(dest) - 1] = ' '; // Always null-terminate! - Instead of
gets, usefgets:fgetslimits the number of characters read from input.char buffer[100]; fgets(buffer, sizeof(buffer), stdin); - Avoid
sprintfandvsprintf: These are prone to format string vulnerabilities. Use safer alternatives likesnprintf.char buffer[100]; snprintf(buffer, sizeof(buffer), "The value is %d", value);
3. Input Validation
Never trust user input! Always validate it before using it.
- Check Length: Ensure the input isn’t longer than expected.
if (strlen(input) > MAX_INPUT_LENGTH) { // Handle error - reject input or truncate it } - Whitelist Allowed Characters: Only allow specific characters that are known to be safe. Reject anything else.
void validateInput(char *input) { for (int i = 0; input[i] != ' '; ++i) { if (!isalnum(input[i])) { // Handle error - reject input return; } } } - Sanitise Input: Remove or escape potentially dangerous characters.
4. Secure Coding Practices
- Principle of Least Privilege: Run your program with the minimum necessary permissions.
- Regular Security Audits: Have your code reviewed by security experts.
- Use a Compiler with Security Features: Modern compilers often have flags to help detect potential vulnerabilities (e.g., stack protection, address space layout randomization).
- Avoid Dynamic Code Loading: If possible, avoid loading code from external sources at runtime as this increases the risk of injection attacks.
5. Command Injection Specifics
If you must execute system commands, be extremely careful.
- Avoid
system(): This function is very vulnerable. - Use
execvp()or similar functions with carefully constructed arguments: Split the command and arguments into an array of strings. Avoid concatenating user input directly into the command string.char *args[] = {"/bin/ls", "-l", input, NULL}; execvp(args[0], args);
6. Example: Preventing Format String Bugs
Incorrect (vulnerable):
printf(userInput);
Correct (safe):
printf("%s", userInput);