Bash/Shell Security Guidelines

Audience: Contributors, security reviewers

WHAT

Security guidelines covering command injection prevention, path protection, input validation, and privilege management for Bash scripts.

WHY

Shell scripts are a common attack vector. Explicit security guidelines prevent vulnerabilities from reaching production.

HOW

Table of Contents

Overview

Comprehensive security and quality standards for shell scripts, focusing on security, portability, and maintainability.

Security Standards Compliance

1. Command Injection Prevention ✅

Risk: Unescaped user input in commands Severity: Critical

Mitigations Implemented:

# ❌ BAD - Command injection vulnerable
eval "docker run $user_input"
docker run $(echo $user_input)

# ✅ GOOD - Safe practices
docker run "$@"                    # Use positional parameters
docker run "${container_name}"      # Quote variables
printf '%q' "$user_input"          # Escape special characters

2. Path Injection Protection ✅

Risk: Malicious PATH manipulation Severity: High

Implementation:

#!/usr/bin/env bash
# ✅ Set secure PATH at script start
export PATH="/usr/local/bin:/usr/bin:/bin"

# ✅ Use absolute paths for critical commands
readonly DOCKER="/usr/bin/docker"
readonly GIT="/usr/bin/git"

# ✅ Verify command locations
command -v docker >/dev/null 2>&1 || { echo "docker not found"; exit 1; }

3. Input Validation ✅

Risk: Malformed or malicious input Severity: High

# ✅ Validate all inputs
validate_container_name() {
    local name="$1"
    # Only allow alphanumeric, dash, underscore
    if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
        echo "Error: Invalid container name" >&2
        return 1
    fi
}

# ✅ Sanitize file paths
sanitize_path() {
    local path="$1"
    # Remove directory traversal attempts
    path="${path//\.\.\/}"
    path="${path//\/\//\/}"
    echo "$path"
}

4. Secure File Operations ✅

Risk: Race conditions, symlink attacks Severity: Medium

# ✅ Use mktemp for temporary files
readonly TMP_FILE="$(mktemp /tmp/dck.XXXXXX)"
trap 'rm -f "$TMP_FILE"' EXIT

# ✅ Set restrictive permissions
umask 077  # Files: 600, Dirs: 700

# ✅ Check file ownership before operations
if [[ "$(stat -c %U "$file")" != "$USER" ]]; then
    echo "Error: File not owned by current user" >&2
    exit 1
fi

5. Secret Management ✅

Risk: Exposed credentials Severity: Critical

# ❌ BAD - Secrets in code
API_KEY="sk-1234567890"

# ✅ GOOD - Environment variables
API_KEY="${API_KEY:?Error: API_KEY not set}"

# ✅ GOOD - Read from secure file
API_KEY="$(cat /run/secrets/api_key)"

# ✅ Prevent secrets in history
set +o history  # Disable history
export HISTCONTROL=ignorespace  # Ignore commands starting with space

6. Error Handling ✅

Risk: Silent failures, undefined behavior Severity: Medium

#!/usr/bin/env bash
# ✅ Strict error handling
set -euo pipefail  # Exit on error, undefined vars, pipe failures
IFS=$'\n\t'        # Set secure Internal Field Separator

# ✅ Error trap with cleanup
cleanup() {
    local exit_code=$?
    echo "Cleaning up..." >&2
    # Cleanup operations
    exit "$exit_code"
}
trap cleanup EXIT ERR

# ✅ Explicit error checking
if ! docker_output="$(docker ps 2>&1)"; then
    echo "Error: Failed to list containers: $docker_output" >&2
    exit 1
fi

7. Privilege Escalation Prevention ✅

Risk: Unauthorized privilege elevation Severity: Critical

# ✅ Never use sudo in scripts without validation
require_sudo() {
    if [[ $EUID -eq 0 ]]; then
        echo "Error: This script should not be run as root" >&2
        exit 1
    fi
}

# ✅ Drop privileges when possible
drop_privileges() {
    if [[ $EUID -eq 0 ]]; then
        exec su -s /bin/bash nobody "$0" "$@"
    fi
}

8. Resource Limits ✅

Risk: Resource exhaustion Severity: Medium

# ✅ Set resource limits
ulimit -t 300   # CPU time limit (5 minutes)
ulimit -f 10000 # File size limit (10MB)
ulimit -u 100   # Process limit

# ✅ Timeout for long operations
timeout 30 docker pull "$image" || {
    echo "Error: Image pull timed out" >&2
    exit 1
}

9. Logging and Auditing ✅

Risk: No audit trail Severity: Low

# ✅ Structured logging
readonly LOG_FILE="/var/log/dck/operations.log"

log() {
    local level="$1"
    shift
    echo "$(date -Iseconds) [$level] [$$] $*" >> "$LOG_FILE"
}

log INFO "Starting container: $container_name"
log ERROR "Failed to start container: $error_message"

# ✅ Audit sensitive operations
audit_action() {
    logger -t dck -p auth.info "User $USER performed: $1"
}

10. TOCTOU (Time-of-Check-Time-of-Use) Prevention ✅

Risk: Race condition vulnerabilities Severity: Medium

# ❌ BAD - TOCTOU vulnerable
if [[ -f "$file" ]]; then
    cat "$file"  # File might change between check and use
fi

# ✅ GOOD - Atomic operations
if output="$(cat "$file" 2>/dev/null)"; then
    echo "$output"
else
    echo "Error: Cannot read file" >&2
fi

ShellCheck Compliance

Severity Levels

  • Error: Must fix (SC2086, SC2046, SC2006)
  • Warning: Should fix (SC2166, SC2164, SC2155)
  • Info: Consider fixing (SC2034, SC2162)
  • Style: Optional (SC2004, SC2007)

Critical Rules Enforced

# SC2086: Quote variables to prevent word splitting
# ❌ BAD
rm $file
# ✅ GOOD
rm "$file"

# SC2046: Quote command substitution
# ❌ BAD
rm $(ls *.tmp)
# ✅ GOOD
rm "$(ls *.tmp)" || find . -name "*.tmp" -delete

# SC2006: Use $(...) instead of backticks
# ❌ BAD
result=`command`
# ✅ GOOD
result="$(command)"

# SC2016: Single quotes prevent expansion
# ❌ BAD
echo '$HOME'  # Prints literal $HOME
# ✅ GOOD
echo "$HOME"  # Prints /home/user

# SC2068: Quote array expansions
# ❌ BAD
args=$@
# ✅ GOOD
args=("$@")

POSIX Compliance

Portable Shell Features

#!/bin/sh  # POSIX shell, not bash

# ✅ POSIX compliant
[ "$var" = "value" ]        # String comparison
[ "$num" -eq 5 ]           # Numeric comparison
command -v docker          # Command existence

# ❌ Bash-only features to avoid for portability
[[ "$var" == "value" ]]    # Double brackets
(( num++ ))                # Arithmetic evaluation
arrays=()                  # Arrays
${var,,}                   # Case conversion

Security Checklist

Pre-Development

  • Define trust boundaries
  • Identify sensitive operations
  • Plan input validation strategy
  • Design error handling approach

During Development

  • Set strict mode (set -euo pipefail)
  • Quote all variables
  • Validate all inputs
  • Use absolute paths
  • Implement timeouts
  • Add comprehensive logging
  • Handle errors explicitly
  • Clean up resources (trap EXIT)

Testing

  • Run ShellCheck with strict settings
  • Test with malicious inputs
  • Verify privilege requirements
  • Check resource consumption
  • Test error conditions
  • Audit log output

Deployment

  • Set restrictive permissions (755 or less)
  • Verify no hardcoded secrets
  • Document security assumptions
  • Enable audit logging
  • Set up monitoring

Static Analysis Tools

ShellCheck Configuration

# .shellcheckrc
shell=bash
enable=all
exclude=SC2312  # Consider shellcheck -x
severity=warning

# In scripts
# shellcheck disable=SC2034  # Unused variable (if intentional)

Additional Tools

# bashate - Style checker
bashate --ignore E006 *.sh

# bash -n - Syntax check
bash -n script.sh

# set -x - Debug mode
bash -x script.sh

# shfmt - Formatter
shfmt -i 2 -bn -ci -w *.sh

Common Security Anti-Patterns

1. Unsafe eval/exec

# ❌ NEVER DO THIS
eval "$user_input"
exec $command

# ✅ Safe alternatives
case "$user_input" in
    start|stop|restart) "$user_input" ;;
    *) echo "Invalid command" >&2 ;;
esac

2. Unquoted Variables

# ❌ Word splitting vulnerability
if [ $USER_INPUT = "value" ]; then

# ✅ Safe comparison
if [ "$USER_INPUT" = "value" ]; then

3. Unsafe Find/Exec

# ❌ Command injection via filenames
find . -type f -exec rm {} \;

# ✅ Safe deletion
find . -type f -delete
# or
find . -type f -print0 | xargs -0 rm

4. Predictable Temp Files

# ❌ Race condition
tmp_file="/tmp/myapp.tmp"

# ✅ Secure temp file
tmp_file="$(mktemp)"
trap 'rm -f "$tmp_file"' EXIT

DCK Implementation Status

Security Control Status Implementation
Command Injection Prevention All variables quoted
Path Injection Protection Absolute paths used
Input Validation Validation functions
Secure File Operations mktemp, proper permissions
Secret Management No hardcoded secrets
Error Handling set -euo pipefail
Privilege Prevention Non-root checks
Resource Limits Timeouts implemented
Logging/Auditing Structured logging
TOCTOU Prevention Atomic operations

References