Back to blog
Best practices

Cron Job Security: Hardening Scheduled Tasks Against Common Attacks

Cron jobs run with elevated privileges and access sensitive data. Learn how to secure them against privilege escalation, injection, and credential exposure.

CronGuard Team··6 min read

The Security Blind Spot

Cron jobs often run as root, handle database credentials, and process sensitive data. Yet they rarely receive the same security scrutiny as web applications or APIs. This makes them attractive targets for attackers and a common source of privilege escalation.

1. Never Run as Root (Unless Necessary)

Most cron jobs do not need root privileges. Running as root means a vulnerability in your script gives an attacker full system access.

# Bad: root crontab
0 2 * * * /path/to/backup.sh

# Better: dedicated service user
# In /etc/cron.d/backup
0 2 * * * backup-user /path/to/backup.sh

Create a dedicated user with minimal permissions:

useradd -r -s /usr/sbin/nologin backup-user
chown backup-user:backup-user /path/to/backup.sh
chmod 750 /path/to/backup.sh

2. Secure Credential Storage

Never hardcode credentials in cron scripts or crontab entries. They end up in version control, process listings, and log files.

Bad: Credentials in Script

#!/bin/bash
# NEVER DO THIS
pg_dump "postgresql://admin:s3cret@localhost/mydb" > backup.sql

Better: Environment File

# /etc/cron.d/backup
0 2 * * * backup-user /bin/bash -c 'source /etc/backup/.env && /path/to/backup.sh'

# /etc/backup/.env (chmod 600, owned by backup-user)
export DATABASE_URL="postgresql://admin:s3cret@localhost/mydb"

Best: Secrets Manager

#!/bin/bash
# Fetch credentials at runtime from secrets manager
DATABASE_URL=$(aws secretsmanager get-secret-value \
  --secret-id prod/database-url \
  --query 'SecretString' --output text)

pg_dump "$DATABASE_URL" > backup.sql

3. Restrict File Permissions

# Script files: owner read+execute only
chmod 750 /path/to/cron-scripts/*.sh

# Output/log files: owner read+write only
chmod 640 /var/log/cron-jobs/*.log

# Credential files: owner read only
chmod 600 /etc/backup/.env

# Backup files: owner read only
chmod 600 /backups/*.sql.gz

4. Prevent Command Injection

If your cron job processes filenames, user input, or external data, unquoted variables can lead to command injection.

# VULNERABLE: unquoted variable allows injection
FILE=$(curl -s https://api.example.com/latest-file)
cat $FILE  # If FILE contains "; rm -rf /", game over

# SAFE: quote all variables
FILE=$(curl -s https://api.example.com/latest-file)
cat "$FILE"

# SAFER: validate input before using it
FILE=$(curl -s https://api.example.com/latest-file)
if [[ ! "$FILE" =~ ^[a-zA-Z0-9._-]+$ ]]; then
  echo "Invalid filename" >&2
  exit 1
fi
cat "$FILE"

5. Use Cron Access Control

Limit which users can create cron jobs:

# /etc/cron.allow - only these users can use crontab
root
deploy
backup-user

# /etc/cron.deny - deny specific users (if cron.allow doesn't exist)
nobody
www-data

6. Audit Cron Changes

Monitor changes to crontab files. An attacker who gains access to a server often installs persistence via cron jobs.

# Watch for crontab changes with auditd
auditctl -w /etc/crontab -p wa -k cron_change
auditctl -w /etc/cron.d/ -p wa -k cron_change
auditctl -w /var/spool/cron/ -p wa -k cron_change

7. Network Security for Check-ins

When your cron jobs check in with external monitoring, those HTTP requests can be intercepted or spoofed.

  • Always use HTTPS for monitoring check-ins
  • Use unique, unguessable monitor IDs so attackers cannot fake check-ins
  • Consider IP allowlisting on your monitoring service

8. Limit Network Access

If a cron job only needs to access localhost and a monitoring service, restrict its outbound network access with firewall rules:

# iptables: allow backup-user only to database and monitoring
iptables -A OUTPUT -m owner --uid-owner backup-user -d 127.0.0.1 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner backup-user -d cronguard.app -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner backup-user -j DROP

9. Log Everything

Comprehensive logging provides an audit trail for security investigations:

#!/bin/bash
set -euo pipefail

LOG="/var/log/cron-jobs/backup.log"
exec >> "$LOG" 2>&1

echo "[$(date -Iseconds)] Job started by $(whoami) from $(hostname)"
echo "[$(date -Iseconds)] Working directory: $(pwd)"

# ... actual work ...

echo "[$(date -Iseconds)] Job completed successfully"

Security Checklist

  • Run with least-privilege user (not root)
  • Store credentials in secrets manager or encrypted env files
  • Set strict file permissions (750 scripts, 600 secrets)
  • Quote all variables to prevent injection
  • Validate external input before processing
  • Use /etc/cron.allow to restrict cron access
  • Audit crontab changes with auditd
  • Use HTTPS for all external communications
  • Log all actions with timestamps
  • Restrict network access per user

Conclusion

Cron jobs are part of your attack surface. They run automatically, often with elevated privileges, and handle sensitive data. Applying basic security practices (least privilege, credential management, input validation, and auditing) dramatically reduces the risk of a cron job becoming an attacker's persistence mechanism or data exfiltration tool.

Back to all posts