Hardening Bash: injection, IFS, PATH & temp races
Command/argument injection, IFS attacks, PATH hygiene, safe temp files and privilege drop.
Bash sits at the exact spot attackers love: it stitches together untrusted input, external commands, files, and environment. Hardening a script means assuming every input is hostile — arguments, environment variables, filenames, the contents of files you read — and removing the ways that input can become a command, hijack a path, or win a race. These are the bugs that turn a helper script into a local privilege escalation.
Command and argument injection
The root cause is letting data cross into the code position of a command. eval on any string built from input is the worst offender; so is an unquoted variable that undergoes word splitting and globbing, and so is passing user text where a flag is expected (argument injection — a value starting with - becomes an option). Quote every expansion, end option parsing with --, and never eval untrusted data.
# WORD SPLITTING/GLOB: filename with spaces or * wrecks thisrm -rf $dir/* # BADrm -rf -- "$dir"/* # quote; -- ends options# ARGUMENT INJECTION: $user = "--checkpoint-action=exec=sh" etc.grep "$pattern" file # if $pattern begins with -, it is read as a flaggrep -e "$pattern" -- file # -e forces it to be the pattern; -- ends options# EVAL: never build code from dataeval "cp $src $dst" # BAD — $src='a; rm -rf /' is game overcp -- "$src" "$dst" # just run the command with quoted args
IFS, PATH, and environment hygiene
Two environment variables silently change how your script behaves. IFS controls word splitting; if an attacker can set it (or you leave it at the default when reading data), fields split in surprising places. PATH controls which binary "curl" or "python" actually runs; a writable or attacker-controlled directory early in PATH means you execute their program. For anything privileged, set a known IFS, set an absolute PATH, and consider calling critical tools by full path.
#!/usr/bin/env bashset -Eeuo pipefailIFS=$'\n\t' # predictable splittingexport PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'export LC_ALL=C # stable sort/format behaviour# for the most sensitive calls, do not trust PATH at all/usr/bin/install -m 0600 -- "$src" "$dst"
Temp-file races and safe privilege drop
A predictable temp path like /tmp/mytool.$$ is a symlink attack: an attacker pre-creates it pointing at a file you will then write as root. mktemp creates a file or directory with a random name and safe permissions atomically — always use it. And if your script starts as root only to do one privileged step, drop back to an unprivileged user for the rest so a later bug is not running with full power.
# atomic, unpredictable, and cleaned uptmp="$(mktemp -d /tmp/tool.XXXXXX)"trap 'rm -rf "$tmp"' EXIT# write-then-rename = readers never see a half-written fileprintf '%s' "$payload" > "$tmp/out"mv -f "$tmp/out" /etc/app/config # rename is atomic on one filesystem# do the root step, then drop privileges for everything afterchown root:root /etc/app/configexec setpriv --reuid=app --regid=app --clear-groups "$0" --worker "$@"