← News
/PLAYBOOK · 2026-08-05

Redis Security Hardening in Practice: Unauthorized Access and RCE Defense Config

Redis defaults allow unauthenticated access; attackers get RCE by writing crontab or SSH keys. Full hardening checklist: bind address, strong auth, dangerous-command disable, and an automated check script.

Redis hardening is easily skipped in middleware defense: Redis defaults to no auth and listens on 0.0.0.0:6379. Once public, attackers get remote command execution (RCE) by writing crontab, SSH public keys, or loading a malicious module via replication. Full config path from detection to hardening follows.

Scope

Prerequisites

Mechanism

Redis unauthorized-access chains fall into four types:

Only prerequisite: 6379 reachable with no auth. Hardening core is three layers: network isolation + strong auth + disable dangerous commands.

Procedure

1. Detect unauthorized access

`# Local detection (simulate attacker)
redis-cli -h 127.0.0.1 -p 6379 ping
# Returns PONG with no AUTH error → unauthorized access risk exists
# Remote detection (use the target public IP; authorized testing only)
nc -zv <target-ip> 6379
redis-cli -h <target-ip> -p 6379 info server | head -5`

2. Bind address: no public listen

`# Edit /etc/redis/redis.conf (or /etc/redis.conf)
# Listen only on loopback and internal addresses; do not leave 0.0.0.0
bind 127.0.0.1 192.168.1.10
# Explicitly tighten the default path that protected-mode would still allow
protected-mode yes
# When listening only on a local Unix socket, TCP can be fully disabled
port 6379`

bind must be a concrete internal IP; do not comment out the bind line. On Docker, use `-p 127.0.0.1:6379:6379`, not bare `-p 6379:6379`.

3. Enable strong auth

`# Generate a high-strength random password (32+ characters)
openssl rand -hex 32   # e.g. output c9a7... (save for later)
# Write to config
requirepass "paste-the-generated-password-here"
# Verify after restart
redis-cli -a 'your-password' ping   # Returns PONG
redis-cli ping                # should return (error) NOAUTH`

Note: the `requirepass` password is readable via `CONFIG GET`; disable `CONFIG` as well (step 4).

4. Disable dangerous commands

Rename or disable high-risk commands (disable recommended):

`# Append at the end of redis.conf
rename-command CONFIG ""
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command EVAL ""
rename-command EVALSHA ""
rename-command SLAVEOF ""
rename-command REPLICAOF ""
rename-command DEBUG ""
rename-command KEYS ""`

If the app actually needs `EVAL` (Lua) and similar, rename to a random string instead:

`rename-command EVAL "ev4l_9f2k1q"`

5. Persistence and replication security

`# Tighten persistence file permissions (default directory for the 6379-port RDB file)
chmod 700 /var/lib/redis
chmod 600 /var/lib/redis/dump.rdb
# Enable authentication for master-replica replication; set on the replica
replicaof 192.168.1.10 6379
masterauth "your-password"
# Disable dangerous module loading (if unused)
# Comment out loadmodule-related lines`

6. Firewall and automated checks

`# iptables: allow 6379 from the internal network only
iptables -A INPUT -p tcp --dport 6379 -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROP
# Daily check script (crontab every 10 minutes for unauthorized status)
cat > /usr/local/bin/redis_check.sh <<'EOF'
#!/bin/bash
timeout 2 redis-cli -h 127.0.0.1 -p 6379 ping | grep -q PONG   && echo "$(date) [WARN] Redis unauthorized access possible!" >> /var/log/redis_check.log
EOF
chmod +x /usr/local/bin/redis_check.sh
echo "*/10 * * * * root /usr/local/bin/redis_check.sh" >> /etc/crontab`

Verification

`# 1. Auth in effect
redis-cli ping                    # NOAUTH
redis-cli -a your-password ping        # PONG
# 2. Dangerous commands disabled
redis-cli -a your-password CONFIG GET dir   # (error) ERR unknown command
# 3. Port exposure
ss -tlnp | grep 6379              # should show only 127.0.0.1 / internal IP
# 4. External scan (authorized environment)
nmap -p 6379 --script redis-info <target-ip>`

FAQ

FAQ1: After disabling CONFIG, the app cannot set expiry policy?

Runtime key commands (`EXPIRE`, `TTL`, etc.) are unaffected; only server-level `CONFIG` is gone. Change config by editing the file and restarting; no runtime `CONFIG SET`.

FAQ2: Why still configure ACL on 6.0+?

After 6.0 ACL, `requirepass` is only the default-user password. Create a dedicated account per app with the minimal command set:

`# Create an app account limited to the specified keyspace
ACL SETUSER appuser on >app_pass ~app:* +get +set +del +expire +ttl +exists +ping
# Verify
redis-cli -u redis://appuser:app_pass@127.0.0.1:6379/0 ping`

FAQ3: Does rename-command break cluster or Sentinel communication?

Yes. Cluster nodes use `CLUSTER`; Sentinel uses `SLAVEOF/CONFIG` and other internals. Disabling them breaks failover. For cluster/Sentinel, stick to bind + requirepass + ACL; contain dangerous commands with network-layer ACL (cloud security group allowlist internal only).

Summary

Apply four layers: network isolation → strong auth → command reduction → runtime monitoring. Bind to internal IPs, 32-char random password, disable CONFIG/EVAL/SLAVEOF and other dangerous commands, plus a check script. That cuts the unauthorized-access and replication-RCE chains. On newer versions, also enable ACL for least privilege.