Skip to content
AgileLogics
6 min read

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 077 before creating them, or chmod 600 after.

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.