OS command injection and HTTP header injection
OS command injection occurs when an application builds a shell command including user-controlled data without validating or escaping it. The shell interprets metacharacters (;, &, |, $(), `) as command separators. An attacker can thus chain their own commands to those intended by the application.
Command injection example and fix
// Vulnérable : concaténation directe dans une commande shell
const { exec } = require('child_process');
exec(`ping -c 1 ${req.query.host}`, callback);
// Payload : req.query.host = "8.8.8.8; cat /etc/passwd"
// Résultat : ping + lecture de /etc/passwd
// Corrigé : passage des arguments séparément, sans shell
const { execFile } = require('child_process');
execFile('ping', ['-c', '1', req.query.host], callback);
// execFile n'invoque pas de shell : les métacaractères sont ignorés
Common injection mechanisms
At-risk functions are those that pass a string to the shell: exec(), system(), shell_exec(), popen() (PHP/Python), os.system(), subprocess.call(shell=True) (Python), Runtime.exec(String) (Java). These functions invoke an intermediate shell (/bin/sh) that interprets metacharacters.
Python: subprocess without shell
import subprocess
# Vulnérable
subprocess.call(f"convert {filename} output.jpg", shell=True)
# Corrigé : liste d'arguments, shell=False (défaut)
subprocess.call(['convert', filename, 'output.jpg'])
# Ou avec validation stricte de l'entrée :
import re
if not re.match(r'^[a-zA-Z0-9._-]+$', filename):
raise ValueError('Invalid filename')
HTTP header injection
HTTP header injection occurs when a user-controlled value is inserted into an HTTP response header without validation. HTTP headers are delimited by carriage returns (\r\n). An attacker who can insert these characters can add arbitrary headers or even split the HTTP response to inject a complete response (HTTP response splitting).
Vulnerable redirect header
# Vulnérable : la valeur de redirect est insérée dans l'en-tête Location
redirect_url = request.args.get('redirect')
response.headers['Location'] = redirect_url
# Payload : redirect = "http://legit.com\r\nSet-Cookie: session=attacker"
# Résultat : injection d'un cookie Set-Cookie dans la réponse
# Corrigé : validation stricte de l'URL
from urllib.parse import urlparse
parsed = urlparse(redirect_url)
if parsed.netloc not in ALLOWED_DOMAINS:
redirect_url = '/'
General rule
The prevention rule is universal: never build a command or header by string concatenation including user input. For system commands, always pass arguments separately (list, not a string). For HTTP headers, validate and encode values. Most modern frameworks handle header encoding automatically when using their dedicated APIs.
Further reading
A question after reading?
Does your code invoke system commands with user input? Send a message.
Or book a 30-minute scoping call directly
Book a call →