How do you write a Bash script that is safe against command injection, path traversal, and race conditions?
Quick Answer
Quote all variables, validate user input against allowlists, use mktemp for temp files, avoid eval, use -- to end option parsing, and prefer built-in string operations over external commands.
Detailed Answer
Command Injection Prevention
- Always double-quote variables: "$var" not $var - Never use eval with user input - Never use backticks; use $() for command substitution - Use -- to end option parsing: grep -- "$pattern" file - Validate input: [[ "$input" =~ ^[a-zA-Z0-9_-]+$ ]]
Path Traversal Prevention
- Use realpath or readlink -f to canonicalize paths - Validate that resolved path is within expected directory - Never construct paths from untrusted input without validation
Race Condition Prevention
- Use mktemp for temporary files (atomic creation with unique name) - Use flock for file-based locking - Check-then-act is always unsafe; use atomic operations - Use noclobber (set -C) to prevent overwriting existing files
General Safety
- set -euo pipefail at the top - Trap EXIT for cleanup - Use readonly for constants - Prefer [[ ]] over [ ] for conditionals (no word splitting)
Code Example
#!/usr/bin/env bash
set -euo pipefail
readonly ALLOWED_DIR="/var/app/data"
readonly VALID_NAME_RE='^[a-zA-Z0-9_-]+$'
# Validate input (never trust user input)
validate_filename() {
local name=$1
if [[ ! "$name" =~ $VALID_NAME_RE ]]; then
echo "Invalid filename: $name" >&2
return 1
fi
# Resolve full path and check it's within allowed directory
local full_path
full_path=$(realpath -m "${ALLOWED_DIR}/${name}")
if [[ "$full_path" != "${ALLOWED_DIR}/"* ]]; then
echo "Path traversal detected: $name" >&2
return 1
fi
echo "$full_path"
}
# Safe temp file with cleanup
TMPFILE=$(mktemp /tmp/safe-script.XXXXXX)
trap 'rm -f "$TMPFILE"' EXIT
# File locking to prevent race conditions
(
flock -n 200 || { echo "Another instance running" >&2; exit 1; }
# Critical section here
echo "Processing..."
) 200>/var/lock/my-script.lock
# Never do this:
# eval "echo $user_input" # Command injection!
# cat /data/$user_input # Path traversal!
# if [ -f $file ]; then rm $file # Word splitting + race condition!Interview Tip
Security-conscious scripting is rare and impressive in interviews. Mention the three attack vectors (injection, traversal, race conditions) and the specific mitigations. The realpath check for path traversal is a strong signal.