Security for Shell Scripts
Prerequisites: Quoting, Handling file names safely
Shell scripts often run with high privileges — as root, in CI with deployment credentials, on production servers. A few habits prevent the most common vulnerabilities.
1. Never eval untrusted input
eval runs a string as code. If any part comes from outside — arguments,
files, API responses, environment variables — an attacker can run anything:
user_input='hello; echo INJECTED'
eval "echo $user_input"
echo "---"
echo "$user_input"
hello
INJECTED
---
hello; echo INJECTED
The eval version ran a second command. Avoid eval; almost every use
has a safer alternative (arrays for dynamic arguments, case for choosing
actions, namerefs for dynamic variable names).
2. Quote to prevent injection and splitting
Unquoted variables are split and glob-expanded, so a value like * or
-rf / can turn into unexpected arguments. Quote everything, and use
-- before file-name arguments.
3. Pass data as arguments, not code
When running other interpreters, don't paste values into the code:
name="O'Brien"
# Fragile and unsafe: the quote breaks the awk program
# awk "BEGIN { print \"Hello, $name\" }"
# Safe: pass the value as data
awk -v name="$name" 'BEGIN { print "Hello, " name }'
Hello, O'Brien
The same applies to SQL (use the client's parameter features), ssh
remote commands (quote with printf %q), and find -exec.
4. Temporary files and permissions
- Create temporary files with
mktemp, never predictable names in/tmp. - Set restrictive permissions on files holding secrets:
umask 077before creating them, orchmod 600after.
5. Secrets
- Don't hard-code passwords or tokens in scripts — read them from environment variables or a secrets manager.
- Don't pass secrets as command-line arguments: other users can see them
in
ps. Prefer environment variables or files with tight permissions. - Don't print secrets, and be careful with
set -x, which echoes every command — including ones containing tokens.
6. Downloaded scripts
curl https://example.com/install.sh | bash runs code you haven't read,
as you. Download it first, read it, verify a checksum or signature if one
is published (the Hash Generator computes
SHA-256 hashes), then run it.
7. PATH and root
Scripts run by cron or as root should set a known PATH at the top
(PATH=/usr/local/bin:/usr/bin:/bin), so a malicious program earlier in
PATH can't be picked up. Run with the lowest privileges that work.