Database Security

Redis Security: Authentication, TLS, and Network Isolation

A deep-dive into securing Redis deployments: ACL-based authentication, TLS transport encryption, network binding, dangerous command renaming, and cluster authentication.

August 1, 20257 min readShipSafer Team

Redis ships with no authentication and listens on all interfaces by default. That combination makes it one of the most frequently compromised databases on the internet — hundreds of thousands of exposed Redis instances serve as pivot points into production environments every year. This guide covers every layer of the Redis security model: authentication, transport encryption, network isolation, dangerous command control, and cluster authentication.

The Default Redis Security Problem

A vanilla Redis installation binds to 0.0.0.0:6379 and accepts any command from any connecting client with no password check. Redis's design philosophy has always prioritized speed inside a trusted network; the assumption is that Redis never reaches an untrusted surface. That assumption fails constantly.

Run a fresh Redis server and you can immediately:

redis-cli FLUSHALL          # destroy all data
redis-cli CONFIG SET dir /etc/cron.d/   # write arbitrary files via RDB
redis-cli CONFIG SET dbfilename root    # combined with BGSAVE = cron backdoor

The classic Redis compromise chain — pivot through CONFIG SET + BGSAVE to write SSH authorized_keys or cron jobs — has been well-documented since 2015. It still works against any Redis instance that skips authentication and network binding.

Authentication: requirepass vs ACL (Redis 6+)

The Old Way: requirepass

Before Redis 6, authentication was a single global password set in redis.conf:

requirepass your_strong_password_here

Clients authenticate with:

redis-cli -a your_strong_password_here PING

Or in application code:

const client = redis.createClient({
  url: 'redis://:your_strong_password_here@localhost:6379'
});

requirepass has a critical limitation: every client — your application, your cache warming job, your analytics pipeline — gets the same credentials with full access to every command and every key. There's no way to grant read-only access to one service and read-write to another.

The Right Way: Access Control Lists (Redis 6+)

Redis 6 introduced a proper ACL system. Each user gets an independent username, password hash, enabled/disabled state, allowed commands, and allowed key patterns.

In redis.conf:

# Disable the default user (no password, full access)
user default off

# Read-only service account — can only GET/MGET/HGET keys matching cache:*
user cache_reader on >cache_reader_password ~cache:* +get +mget +hget +hgetall +lrange +smembers

# Application service account — full access to app:* keys, no dangerous commands
user app_service on >app_service_password ~app:* +@all -@dangerous -flushall -flushdb -config -debug

# Admin account — full access, only connect from localhost
user redis_admin on >admin_password ~* &* +@all

Apply without restart:

redis-cli ACL LOAD

Inspect the current ACL:

redis-cli ACL LIST
redis-cli ACL WHOAMI
redis-cli ACL CAT           # list command categories
redis-cli ACL CAT dangerous # list dangerous commands

The @dangerous category covers commands like FLUSHALL, FLUSHDB, KEYS, CONFIG, DEBUG, SAVE, BGSAVE, BGREWRITEAOF, SHUTDOWN, and SLAVEOF. Blocking this category from application accounts is the single most impactful ACL change you can make.

Store ACLs in a separate file for cleaner management:

# redis.conf
aclfile /etc/redis/users.acl

TLS Transport Encryption

Redis 6 added native TLS support. Without it, all data — including your authentication credentials — travels in plaintext.

Generating Certificates

For production, use certificates signed by your internal CA or a public CA. For testing:

# Generate CA key and certificate
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
  -subj "/CN=Redis CA"

# Generate server key and CSR
openssl genrsa -out redis.key 4096
openssl req -new -key redis.key -out redis.csr \
  -subj "/CN=redis.internal"

# Sign the server certificate
openssl x509 -req -days 365 -in redis.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out redis.crt

Redis TLS Configuration

# redis.conf

# Disable plaintext port, enable TLS port
port 0
tls-port 6380

# Certificate paths
tls-cert-file /etc/redis/tls/redis.crt
tls-key-file /etc/redis/tls/redis.key
tls-ca-cert-file /etc/redis/tls/ca.crt

# Require client certificates (mutual TLS)
tls-auth-clients yes

# Enforce TLS 1.2 minimum
tls-protocols "TLSv1.2 TLSv1.3"

# Strong cipher suites only
tls-ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
tls-ciphersuites "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"

# Prefer server cipher order
tls-prefer-server-ciphers yes

Connect with TLS from the CLI:

redis-cli -p 6380 \
  --tls \
  --cert /etc/redis/tls/client.crt \
  --key /etc/redis/tls/client.key \
  --cacert /etc/redis/tls/ca.crt \
  PING

Node.js with ioredis:

import Redis from 'ioredis';
import fs from 'fs';

const redis = new Redis({
  host: 'redis.internal',
  port: 6380,
  tls: {
    cert: fs.readFileSync('/etc/redis/tls/client.crt'),
    key: fs.readFileSync('/etc/redis/tls/client.key'),
    ca: fs.readFileSync('/etc/redis/tls/ca.crt'),
    rejectUnauthorized: true,
  },
  username: 'app_service',
  password: 'app_service_password',
});

Network Binding: Never 0.0.0.0

Redis must not bind to all interfaces unless every interface is trusted. Change redis.conf:

# Only listen on loopback and a specific private interface
bind 127.0.0.1 10.0.1.5

# Explicitly enable protected mode (default on, but be explicit)
protected-mode yes

protected-mode yes means Redis will refuse connections from remote IPs unless a password is set. It's a last-resort safety net — don't rely on it; use explicit binding.

For containerized deployments, use Docker network isolation instead of exposing the Redis port to the host:

# docker-compose.yml
services:
  redis:
    image: redis:7-alpine
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    networks:
      - internal_network
    # No 'ports:' section — Redis is not reachable from the host

  app:
    image: myapp:latest
    environment:
      REDIS_URL: redis://app_service:password@redis:6379
    networks:
      - internal_network

networks:
  internal_network:
    driver: bridge
    internal: true  # No external routing

Dangerous Command Renaming

Even with ACLs, you may want to rename or disable dangerous commands at the protocol level so they cannot be called even if an ACL is misconfigured. Use rename-command in redis.conf:

# Completely disable FLUSHALL and FLUSHDB
rename-command FLUSHALL ""
rename-command FLUSHDB ""

# Rename DEBUG to a random string — internal tooling still works if it knows the name
rename-command DEBUG "DEBUG_d7f3a91b2c"

# Disable CONFIG from the network (use redis-cli locally instead)
rename-command CONFIG ""

# Disable SHUTDOWN to prevent remote shutdown
rename-command SHUTDOWN ""

# Rename KEYS to something unusual (KEYS is O(N) and kills production)
rename-command KEYS "KEYS_UNSAFE_d91f"

Note: rename-command and ACL are complementary. ACL blocks commands per-user at authentication time; rename-command removes commands from the protocol entirely, regardless of user. Use both.

Cluster Authentication

Redis Cluster adds peer-to-peer communication between nodes. Without cluster authentication, any process on the network can join or interact with cluster nodes.

Cluster Password (Pre-Redis 7)

# All nodes must share the same requirepass and masterauth
requirepass cluster_shared_password
masterauth cluster_shared_password

TLS for Cluster (Redis 6+)

# redis.conf (each node)
tls-replication yes
tls-cluster yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000

With tls-cluster yes, all intra-cluster gossip is encrypted. Nodes authenticate each other via the shared TLS certificate authority.

Redis Cluster with Kubernetes

When running Redis Cluster on Kubernetes, use network policies to restrict which pods can reach Redis node ports:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: redis-cluster-access
spec:
  podSelector:
    matchLabels:
      app: redis
  ingress:
    - from:
        - podSelector:
            matchLabels:
              redis-client: "true"
      ports:
        - port: 6379
        - port: 16379   # Cluster bus port

Additional Hardening

Disable persistence if not needed: RDB and AOF files can contain sensitive data. If Redis is a pure cache, disable them:

save ""
appendonly no

Set a memory limit: Prevent Redis from consuming all system memory, which can cause OOM kills of other processes:

maxmemory 2gb
maxmemory-policy allkeys-lru

Enable slow log: Detect anomalous command patterns:

slowlog-log-slower-than 10000  # microseconds
slowlog-max-len 128

Audit with ACL LOG:

redis-cli ACL LOG          # show recent ACL violations
redis-cli ACL LOG RESET    # clear the log

Security Checklist

Before deploying Redis to production, verify each of these:

  • bind is set to specific IPs, not 0.0.0.0
  • protected-mode yes
  • Default user is disabled (user default off)
  • Application accounts use ACL with minimal command sets
  • @dangerous category blocked for all non-admin accounts
  • requirepass or ACL password is at least 32 random characters
  • TLS is enabled with certificates from a trusted CA
  • FLUSHALL, FLUSHDB, CONFIG, DEBUG, SHUTDOWN are renamed or disabled
  • Redis port is not exposed to the public internet
  • Redis process runs as a dedicated non-root user
  • maxmemory is set to prevent runaway memory consumption
  • ACL LOG is enabled and monitored

The most common Redis breach starts with a forgotten exposed port and no password. Fix the network first, then layer ACLs and TLS on top. Defense in depth means that even if one control fails — a misconfigured firewall rule, a stolen credential — the attacker still can't reach production data.

redis
database security
authentication
tls
acl
network security

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.