Security
Chapter 44: Security Considerations in Bash
Section titled “Chapter 44: Security Considerations in Bash”Overview
Section titled “Overview”This chapter covers security best practices for bash scripting. Security is critical for production scripts that handle sensitive data, system resources, and automation in DevOps environments. A single vulnerability can compromise your entire infrastructure.
Security Principles
Section titled “Security Principles”Key Security Concepts
Section titled “Key Security Concepts”┌────────────────────────────────────────────────────────────────┐│ Security Principles │├────────────────────────────────────────────────────────────────┤│ ││ 1. Input Validation ││ - Validate all user input ││ - Sanitize data ││ - Use allowlists not blocklists ││ ││ 2. Principle of Least Privilege ││ - Use minimum required permissions ││ - Run with minimal privileges ││ - Avoid running as root when possible ││ ││ 3. Secure Coding Practices ││ - Quote all variables ││ - Use -- to separate options ││ - Avoid command injection ││ ││ 4. Secret Management ││ - Never hardcode secrets ││ - Use environment variables or vaults ││ - Clear secrets from memory ││ ││ 5. Logging and Monitoring ││ - Log security events ││ - Monitor for anomalies ││ - Alert on suspicious activity ││ │└────────────────────────────────────────────────────────────────┘Input Validation
Section titled “Input Validation”Validate All Input
Section titled “Validate All Input”#!/usr/bin/env bash# Input validation examples
# Validate numeric inputif [[ ! "$1" =~ ^[0-9]+$ ]]; then echo "Error: Invalid number" exit 1fi
# Validate file path - alphanumeric and basic chars onlyif [[ ! "$1" =~ ^[a-zA-Z0-9/_-]+$ ]]; then echo "Error: Invalid characters in path" exit 1fi
# Validate email formatif [[ ! "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then echo "Error: Invalid email" exit 1fi
# Validate IP addressif [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then echo "Error: Invalid IP address" exit 1fiAllowlist vs Blocklist
Section titled “Allowlist vs Blocklist”#!/usr/bin/env bash
# ❌ Bad - blocklist (can be bypassed)if [[ "$input" == *".."* ]]; then echo "Invalid" exit 1fi
# ✓ Good - allowlistif [[ ! "$input" =~ ^[a-zA-Z0-9_-]+$ ]]; then echo "Invalid" exit 1fiSecure Practices
Section titled “Secure Practices”Quote Variables
Section titled “Quote Variables”#!/usr/bin/env bash
# Always quote variables
# ❌ Dangerous - unquoted variablerm -rf $directory # Could expand to multiple args# If directory="/tmp /var", becomes: rm -rf /tmp /var
# ✓ Safe - quoted variablerm -rf "$directory"
# Use -- to separate options from arguments# ❌ Dangerousrm -rf "$filename" # If filename starts with -
# ✓ Saferm -- "$filename"Path Traversal Prevention
Section titled “Path Traversal Prevention”#!/usr/bin/env bash
# Get canonical pathreal_path=$(realpath "$input_path")
# Verify it's within allowed directoryallowed_dir="/var/www/html"if [[ "$real_path" != "$allowed_dir"* ]]; then echo "Error: Path outside allowed directory" exit 1fi
# Additional checkallowed_dir=$(realpath "/var/www/html")input_dir=$(dirname "$(realpath "$input_path")")if [[ "$input_dir" != "$allowed_dir" ]]; then echo "Error: Path traversal detected" exit 1fiSecret Management
Section titled “Secret Management”Never Hardcode Secrets
Section titled “Never Hardcode Secrets”#!/usr/bin/env bash
# ❌ Never do thisPASSWORD="secret123"API_KEY="sk-abc123"DB_PASSWORD="dbpass"
# ✓ Use environment variables: "${DATABASE_PASSWORD:?Set DATABASE_PASSWORD environment variable}": "${API_KEY:?Set API_KEY environment variable}"
# This will exit with error if not set
# ✓ Use secret management tools
# HashiCorp Vault# vault read secret/database
# AWS Secrets Manager# aws secretsmanager get-secret-value --secret-id my-secret
# Kubernetes Secrets# kubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d
# .env files (add to .gitignore)if [[ -f .env ]]; then set -a # Auto-export source .env set +afiPermission Best Practices
Section titled “Permission Best Practices”Set Proper Permissions
Section titled “Set Proper Permissions”#!/usr/bin/env bash
# Script permissionschmod 700 script.sh # Owner only (rwx------)chmod 755 /usr/local/bin # Public execute (rwxr-xr-x)
# Sensitive fileschmod 600 secrets.txt # Owner only (rw-------)chmod 400 key.pem # Read only (r--------)
# Use umask for new filesumask 077 # Most restrictive: files created will be 600
# Check effective permissionsstat -c "%a %n" filename
# Verify before running privileged commandsif [[ $EUID -ne 0 ]]; then echo "This script must be run as root" exit 1fiCommand Injection Prevention
Section titled “Command Injection Prevention”Escape Special Characters
Section titled “Escape Special Characters”#!/usr/bin/env bash
# ❌ Dangerous - command injection vulnerabilitycmd="ls -la $user_input"eval "$cmd"
# If user_input is "; rm -rf /", you're in trouble!
# ✓ Safe - validate inputif [[ "$user_input" =~ ^[a-zA-Z0-9_-]+$ ]]; then cmd="ls -la $user_input" eval "$cmd"else echo "Invalid input" exit 1fi
# ✓ Better - use arraysargs=(-la "$user_input")ls "${args[@]}"
# ✓ Best - avoid eval entirelyls -la -- "$user_input"Sanitize Filenames
Section titled “Sanitize Filenames”#!/usr/bin/env bash
# Remove dangerous characterssanitize_filename() { local filename="$1" # Remove null bytes, newlines, path separators echo "$filename" | tr -d '\0\n/'}
# Use in scriptfilename=$(sanitize_filename "$filename")Privilege Escalation
Section titled “Privilege Escalation”Run with Minimal Privileges
Section titled “Run with Minimal Privileges”#!/usr/bin/env bash
# Check if running as rootif [[ $EUID -ne 0 ]]; then echo "This script must be run as root" exit 1fi
# Use sudo for specific operations# Instead of: sudo ./script.sh# Use: sudo some_command
# Drop privileges when possible# Using su-exec, chroot, or similarif [[ $EUID -eq 0 ]]; then # Drop to unprivileged user su -s /bin/bash nobody -c 'run_command'fi
# Check sudo without passwordif sudo -n true 2>/dev/null; then can_sudo=truefiSecure Script Template
Section titled “Secure Script Template”#!/usr/bin/env bash## Secure script template for production use#
set -euo pipefail
# Security optionsreadonly SCRIPT_NAME="$(basename "$0")"readonly SCRIPT_VERSION="1.0.0"
# Logginglog() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"}
log_security() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] SECURITY: $*" | tee -a /var/log/security.log >&2}
# Input validationvalidate_input() { local input="$1" local pattern="$2"
if [[ ! "$input" =~ $pattern ]]; then log_security "Invalid input: $input" return 1 fi return 0}
# Sanitize pathsanitize_path() { local path="$1" # Remove dangerous sequences echo "$path" | sed 's/\.\.//g; s/;//g; s/&&//g'}
# Temporary file handlingsetup_temp() { local tmpfile tmpfile=$(mktemp) || exit 1 echo "$tmpfile"}
cleanup() { local exit_status=$? # Clean up temp files [[ -n "${tmpfile:-}" ]] && rm -f "$tmpfile" exit $exit_status}trap cleanup EXIT
# Main functionmain() { log "Starting $SCRIPT_NAME v$SCRIPT_VERSION"
# Validate inputs validate_input "$variable" "^[a-zA-Z0-9_]+$" || exit 1
# Rest of script}
main "$@"File Descriptor Security
Section titled “File Descriptor Security”Close Unused File Descriptors
Section titled “Close Unused File Descriptors”#!/usr/bin/env bash
# Close unnecessary file descriptorsexec 3>/dev/null # Close fd 3exec 4</dev/null # Close fd 4
# Redirect sensitive outputexec 2>/var/log/error.log
# Restore stderr laterexec 3>&2exec 2>/dev/null# ... operations ...exec 2>&3exec 3>&-Summary
Section titled “Summary”In this chapter, you learned:
- ✅ Security principles and concepts
- ✅ Input validation techniques
- ✅ Secure coding practices
- ✅ Secret management approaches
- ✅ Permission best practices
- ✅ Command injection prevention
- ✅ Privilege escalation handling
- ✅ Secure script templates
- ✅ File descriptor security
Next Steps
Section titled “Next Steps”Continue to the next chapter to learn about Interview Questions and prepare for your DevOps/SRE interviews.
Previous Chapter: Performance Optimization Next Chapter: Interview Questions