Mục lục
- Mục Tiêu Bài Học
- Các Lệnh Nguy Hiểm Trong Redis
- rename-command — Legacy Approach
- Vô Hiệu Hóa Lệnh Bằng rename-command
- Đổi Tên Lệnh Thay Vì Disable
- Giới Hạn Của rename-command
- ACL Approach — Redis 6+
- Per-User Command Restrictions Bằng ACL
- MODULE — Rủi Ro Và Cách Hạn Chế
- enable-module-command — Redis 7+
- Pre-loaded Modules Pattern
- EVAL, Lua Sandbox Và CVE-2022-0543
- FUNCTION — Thay Thế An Toàn Hơn EVAL
- DEBUG Subcommands
- MIGRATE Và SHUTDOWN
- Cluster Commands Đặc Biệt Nguy Hiểm
- Xác Minh Lệnh Đã Bị Disable
- Kế Hoạch Khôi Phục Admin Access
- Anti-patterns & Best Practices
- Tổng Kết & Quiz
Mục Tiêu Bài Học
- Nhận biết được các lệnh Redis nào nguy hiểm và tại sao — xóa data, thay đổi config runtime, crash process, exfiltrate data, hoặc thực thi code tùy ý.
- Sử dụng được
rename-commandtrongredis.confđể vô hiệu hóa hoặc đổi tên lệnh. - Hiểu giới hạn của
rename-commandvà lý do ACL (Redis 6+) là cách tiếp cận được ưu tiên hơn. - Cấu hình ACL per-user để restrict hoặc disable lệnh nguy hiểm theo từng role.
- Kiểm soát runtime module loading qua
enable-module-command no(Redis 7+) và pattern pre-load module tại startup. - Giải thích được rủi ro của
EVAL(Lua sandbox escape — CVE-2022-0543) và cách hạn chế. - Phân biệt FUNCTION (Redis 7+) với EVAL về model bảo mật.
Các Lệnh Nguy Hiểm Trong Redis
Redis thuộc loại server in-memory không có model permission phức tạp theo kiểu SQL. Một client đã authenticate mặc định có quyền chạy mọi lệnh, kể cả những lệnh có thể gây hậu quả không thể đảo ngược hoặc mở rộng attack surface đáng kể.
Bảng phân loại theo mức độ tác động:
| Lệnh | Tác động | Mức độ |
|---|---|---|
FLUSHALL |
Xóa toàn bộ data trên mọi database. Không thể undo nếu không có backup. | Nghiêm trọng |
FLUSHDB |
Xóa toàn bộ data trong database hiện tại. | Nghiêm trọng |
CONFIG SET |
Thay đổi cấu hình runtime: dir, dbfilename, requirepass. Attackers dùng để ghi SSH authorized_keys (cryptojacking pattern). |
Nghiêm trọng |
CONFIG REWRITE |
Ghi cấu hình hiện tại xuống disk, có thể overwrite redis.conf. | Cao |
DEBUG SEGFAULT |
Crash Redis process ngay lập tức — DoS vector. | Cao |
DEBUG SLEEP N |
Block Redis trong N giây — DoS, khiến client timeout hàng loạt. | Cao |
MODULE LOAD |
Load file .so tùy ý vào Redis process — RCE nếu attacker kiểm soát filesystem. |
Nghiêm trọng |
EVAL |
Thực thi Lua script tùy ý — Lua sandbox có thể bị bypass (xem CVE-2022-0543). | Cao |
KEYS * |
Scan toàn bộ keyspace — block Redis O(N). Dùng trên production với hàng triệu key gây treo. | Trung bình |
MONITOR |
Stream toàn bộ lệnh đang chạy về client — leak data, password, PII trong realtime. | Cao |
SHUTDOWN |
Dừng Redis process — DoS. | Cao |
MIGRATE |
Copy key sang Redis server khác — data exfiltration vector. | Cao |
CLUSTER RESET |
Reset trạng thái cluster node — phá vỡ cluster topology. | Nghiêm trọng |
Lưu ý: lệnh CONFIG có nhiều subcommand. CONFIG GET dùng để đọc cấu hình — ít nguy hiểm hơn CONFIG SET nhưng vẫn leak thông tin cấu hình cho attacker. Trong thực tế, người ta thường restrict toàn bộ CONFIG với app user và chỉ cho phép admin.
rename-command — Legacy Approach
rename-command là directive trong redis.conf, tồn tại từ các phiên bản Redis rất cũ. Nó cho phép đổi tên một lệnh sang tên khác, hoặc vô hiệu hóa hoàn toàn bằng cách đổi thành chuỗi rỗng.
Cú pháp:
rename-command <tên_gốc> <tên_mới>
rename-command <tên_gốc> "" # Vô hiệu hóa
Directive này được xử lý khi Redis khởi động — không thể thay đổi runtime. Đây là giới hạn quan trọng nhất so với ACL.
Phạm vi tác động: toàn bộ server. Không phân biệt user — mọi client sau khi authenticate đều bị ảnh hưởng như nhau. Đây cũng là điểm yếu lớn: không có cách nào cho phép admin dùng lệnh gốc trong khi app user không được, trừ khi dùng ACL kết hợp.
Vô Hiệu Hóa Lệnh Bằng rename-command
Các lệnh không bao giờ cần dùng trên production application có thể disable hoàn toàn:
# redis.conf
# Xóa data — không có lý do hợp lệ nào để app gọi lệnh này trong production
rename-command FLUSHALL ""
rename-command FLUSHDB ""
# Block Redis, O(N) scan toàn bộ keyspace — dùng SCAN thay thế
rename-command KEYS ""
# Stream mọi command về client — data exfiltration, chỉ dùng dev debug
rename-command MONITOR ""
# Crash và sleep Redis — chỉ tồn tại cho internal testing
rename-command DEBUG ""
# Data exfil
rename-command MIGRATE ""
Khi lệnh bị disable, Redis xử lý nó như một lệnh không tồn tại:
redis-cli FLUSHALL
# (error) ERR unknown command 'FLUSHALL', with args beginning with:
Error message này không tiết lộ rằng lệnh đã bị disable — chỉ thông báo "unknown command", tương tự gõ sai tên lệnh. Đây là hành vi có chủ ý để tránh leak thông tin.
Đổi Tên Lệnh Thay Vì Disable
Một số lệnh vẫn cần thiết cho ops scripts hoặc admin tasks nhưng không nên để app user gọi tự do. Thay vì disable hoàn toàn, đổi tên thành chuỗi ngẫu nhiên không đoán được:
# redis.conf
# CONFIG cần cho admin (tune maxmemory, reload cert) nhưng nguy hiểm với app user
rename-command CONFIG "CONFIG_Xr7mK9zT2qL"
# DEBUG cần cho on-call engineer nhưng không bao giờ dùng trong app code
rename-command DEBUG "DEBUG_Wn4pJ8vB6sY"
# SHUTDOWN cần cho graceful shutdown script
rename-command SHUTDOWN "SHUTDOWN_Hm2kN5xD9cR"
Ops scripts sau đó dùng tên mới:
# Script admin — dùng tên đã rename
redis-cli CONFIG_Xr7mK9zT2qL GET maxmemory
# Attacker không biết tên mới — lệnh gốc không hoạt động
redis-cli CONFIG GET maxmemory
# (error) ERR unknown command 'CONFIG'
Yêu cầu đối với chuỗi rename:
- Đủ dài và ngẫu nhiên để không đoán được. Chuỗi ngắn như
"x"hay"admin"vô nghĩa. - Khác nhau cho mỗi lệnh — không dùng chung một prefix.
- Không commit chuỗi này vào git. Lưu trong secret manager (Vault, AWS Secrets Manager) và inject vào config qua pipeline.
Đây là điểm yếu cơ bản của rename approach: bất kỳ ai đọc được file redis.conf đều biết tên mới. Nếu config bị leak (git commit, log, backup), rename không còn bảo vệ được gì.
Giới Hạn Của rename-command
1. Server-wide, không phân theo user
Không có cách nào để admin user dùng CONFIG thật trong khi app user bị chặn — trừ phi dùng ACL kết hợp. Nếu rename CONFIG thành CONFIG_secret, admin cũng phải dùng tên đó.
2. Yêu cầu restart
rename-command chỉ đọc khi khởi động. Muốn thay đổi phải edit config + restart Redis. Không có cơ chế hot-reload.
3. Cluster: phải đồng nhất toàn bộ node
Trong Redis Cluster, mỗi node có file config riêng. Nếu một node rename FLUSHALL thành "" còn node khác không, client connect đến node đó vẫn gọi được lệnh. Phải đảm bảo cùng cấu hình trên tất cả node — dễ sai khi scale out hoặc thêm node mới.
4. Replication và Sentinel
Replication stream dùng nội bộ một số lệnh (như REPLCONF, PSYNC). Rename nhầm những lệnh này có thể break replication. Sentinel cũng vậy — Sentinel dùng CONFIG để kiểm tra và cập nhật replica list; nếu rename CONFIG, cần config thêm cho Sentinel:
# sentinel.conf
# Nếu Redis đã rename CONFIG, Sentinel phải biết tên mới
sentinel rename-command mymaster CONFIG "CONFIG_Xr7mK9zT2qL"
5. Không audit trail
Rename không ghi log khi ai đó cố gọi lệnh bị rename. ACL có thể kết hợp với log để track failed command attempts.
ACL Approach — Redis 6+
Redis 6.0 bổ sung ACL (Access Control List) — hệ thống per-user permission cho phép định nghĩa từng user được gọi lệnh gì, trên key pattern nào, và subscribe channel nào. Đây là cách tiếp cận được khuyến nghị thay cho rename-command trong môi trường Redis 6+.
Ưu điểm so với rename-command:
- Phân quyền per-user: admin có thể dùng
FLUSHALL, app user không thể — cùng lúc trên cùng server. - Có thể thay đổi runtime qua
ACL SETUSERhoặcACL LOAD— không cần restart. - Granular hơn: restrict đến subcommand level (ví dụ: cho phép
CONFIG GETnhưng không choCONFIG SET). - Kết hợp với key pattern: user chỉ được đọc key khớp
app:*. - Kết hợp với channel pattern: user chỉ được pub/sub trên channel
notifications:*.
ACL được định nghĩa trong redis.conf hoặc file aclfile riêng:
# redis.conf
# Tắt default user (anonymous access)
# Mặc định "default" user có quyền full, không có password — cực kỳ nguy hiểm
# Bài 113 (authentication) đã đề cập; nhắc lại vì liên quan trực tiếp đến command restriction
user default off
# Admin user — full access, chỉ những người biết password mới dùng được
user admin on >strongAdminPass ~* &* +@all
# App user — restricted
user app on >appPassword ~app:* &* +@all -FLUSHALL -FLUSHDB -CONFIG -KEYS -MONITOR -DEBUG -MIGRATE -MODULE -SHUTDOWN
Cú pháp ACL:
on/off: bật/tắt user.>password: đặt password.~pattern: giới hạn key pattern (~*= mọi key).&pattern: giới hạn channel pattern (pub/sub,&*= mọi channel).+@all: cho phép mọi lệnh.-FLUSHALL: xóa quyền gọi lệnh cụ thể.-@dangerous: xóa quyền gọi toàn bộ lệnh trong category "dangerous".
Per-User Command Restrictions Bằng ACL
Ví dụ cấu hình thực tế cho 3 role: admin, app, readonly:
# redis.conf hoặc aclfile.acl
# 1. Default user — disable hoàn toàn
user default off
# 2. Admin — full access
# Chỉ dùng trong ops scripts, không hardcode vào app code
user admin on >Admin#Secure!Pass2026 ~* &* +@all
# 3. App user — quyền cần thiết để chạy application
# Cho phép tất cả trừ các lệnh nguy hiểm
user app on >App#Secure!Pass2026 ~app:* &app:* +@all -@dangerous -CONFIG -MODULE -SHUTDOWN -DEBUG -MIGRATE -MONITOR -KEYS -FLUSHALL -FLUSHDB
# 4. Read-only user — chỉ đọc, không ghi
user readonly on >RO#Secure!Pass2026 ~* &* +@read
Kiểm tra category "dangerous" bao gồm những lệnh nào:
redis-cli ACL CAT dangerous
# Output (tùy Redis version):
# 1) "FLUSHDB"
# 2) "FLUSHALL"
# 3) "PSYNC"
# 4) "DEBUG"
# 5) "OBJECT"
# 6) "RESTORE"
# 7) "KEYS"
# 8) "PEXPIREAT"
# 9) "SORT"
# 10) "MONITOR"
# 11) "SHUTDOWN"
# 12) "CONFIG"
# ... (khoảng 30+ lệnh)
Lưu ý: SORT có trong category dangerous vì có thể trigger O(N log N) với N rất lớn. OBJECT chủ yếu là debugging tool. Không phải mọi lệnh trong category đều cần restrict — cần xem xét context ứng dụng.
Granular subcommand restriction
Từ Redis 7.0, ACL hỗ trợ restrict đến subcommand:
# Cho phép CONFIG GET nhưng không CONFIG SET, CONFIG REWRITE
ACL SETUSER app +CONFIG|GET -CONFIG|SET -CONFIG|REWRITE
Cú pháp +COMMAND|subcommand và -COMMAND|subcommand cho phép kiểm soát chi tiết hơn nhiều so với rename-command.
Verify ACL đang hoạt động
# Xem cấu hình ACL của user hiện tại
redis-cli -u redis://app:App#Secure!Pass2026@localhost:6379 ACL WHOAMI
# Output: app
# Thử gọi lệnh bị restrict
redis-cli -u redis://app:App#Secure!Pass2026@localhost:6379 FLUSHALL
# (error) NOPERM this user has no permissions to run the 'flushall' command
# Lỗi NOPERM khác với "unknown command" của rename-command
# NOPERM nói rõ hơn: user không có quyền
MODULE — Rủi Ro Và Cách Hạn Chế
Redis Module API cho phép load shared library (.so trên Linux, .dylib trên macOS) trực tiếp vào Redis process. Module chạy trong cùng memory space với Redis, có quyền truy cập đầy đủ vào data và có thể gọi system call tùy ý.
Lệnh MODULE LOAD /path/to/module.so ở đây trở thành RCE vector: nếu attacker kiểm soát một client có quyền chạy lệnh này, họ có thể upload file .so lên server (qua CONFIG SET dir + SET key <binary> + BGSAVE — pattern đã được documented trong nhiều CVE) rồi load nó như một module.
Attack pattern điển hình:
# Bước 1: thay đổi dir về thư mục writable
redis-cli CONFIG SET dir /tmp
# Bước 2: ghi module .so vào Redis key
redis-cli SET malicious_module "<binary content of malicious.so>"
# Bước 3: save xuống disk
redis-cli CONFIG SET dbfilename malicious.so
redis-cli BGSAVE
# Bước 4: load module — RCE
redis-cli MODULE LOAD /tmp/malicious.so
Cách này yêu cầu nhiều bước và quyền CONFIG SET, nhưng nếu app user có cả hai quyền này thì chain hoàn toàn khả thi. Disable MODULE LOAD (và CONFIG SET) ngắt chain ở nhiều điểm:
# redis.conf — rename-command approach
rename-command MODULE ""
# ACL approach (ưu tiên hơn)
# user app ... -MODULE
enable-module-command — Redis 7+
Redis 7.0 thêm directive enable-module-command trong redis.conf để kiểm soát ở tầng config (không phụ thuộc ACL):
# redis.conf (Redis 7+)
# no — disable hoàn toàn lệnh MODULE LOAD (và MODULE UNLOAD)
# Module chỉ load được qua config directive "loadmodule" tại startup
# local — chỉ allow load module từ path local (không network path, không RPC)
# yes — allow mọi MODULE LOAD (default; không an toàn)
enable-module-command no
Với enable-module-command no:
redis-cli MODULE LOAD /tmp/malicious.so
# (error) ERR Module loading is disabled
Directive này hoạt động song song với ACL. Ngay cả user admin cũng không load module runtime nếu enable-module-command no — đây là server-level hard disable, không phải per-user. Cân nhắc điều này khi ops cần load module mới: phải restart Redis.
Pre-loaded Modules Pattern
Nếu ứng dụng cần module (RedisBloom, RedisTimeSeries, RedisJSON...), load chúng tại startup qua config thay vì runtime:
# redis.conf
# Load module tại startup — whitelist rõ ràng, path cố định
loadmodule /usr/lib/redis/modules/redisbloom.so
loadmodule /usr/lib/redis/modules/redistimeseries.so
loadmodule /usr/lib/redis/modules/rejson.so
# Sau đó disable runtime loading
enable-module-command no
Pattern này đảm bảo:
- Chỉ các module được liệt kê tường minh trong config mới được load.
- Module path là absolute, cố định — không phụ thuộc vào giá trị runtime.
- Không có cơ chế nào để load module mới mà không edit config + restart — dễ audit.
Trong Kubernetes, config thường được mount qua ConfigMap. Thay đổi loadmodule phải đi qua CI/CD pipeline và tạo pod restart — hoàn toàn có audit trail.
EVAL, Lua Sandbox Và CVE-2022-0543
EVAL script numkeys key [key ...] arg [arg ...] cho phép client gửi Lua script và Redis thực thi trong một sandboxed Lua interpreter. Redis cung cấp một số hàm hạn chế: gọi được lệnh Redis (redis.call), không gọi được file I/O hay network.
CVE-2022-0543 — Lua sandbox escape
Debian và Ubuntu package Lua 5.1 có dynamic module loading (package library). Khi Redis build trên Debian/Ubuntu dùng system Lua, sandbox không loại bỏ được package library, cho phép load arbitrary shared library từ Lua:
-- Lua exploit (CVE-2022-0543, Debian-based systems)
local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io")
local io = io_l()
local f = io.popen("id", "r")
local res = f:read("*a")
f:close()
return res
-- Output: "uid=999(redis) gid=999(redis) groups=999(redis)"
CVSS score: 10.0 (Critical). Ảnh hưởng Redis <= 6.2.6 và 7.0 RC1 trên Debian/Ubuntu. Patch: cập nhật lên Redis 6.2.7+ hoặc Redis 7.0.0 stable trở lên. Các distro-specific patch cũng đã release.
Giảm thiểu rủi ro EVAL
- Cập nhật Redis: đây là biện pháp ưu tiên nhất với CVE-2022-0543.
- Disable EVAL nếu không cần: nếu ứng dụng không sử dụng Lua scripting, disable là biện pháp đơn giản nhất.
- ACL restrict scripting category:
-@scriptingloại bỏ quyền gọi EVAL, EVALSHA, SCRIPT LOAD.
# ACL restrict scripting
user app on >password ~* &* +@all -@scripting
# Hoặc rename-command
rename-command EVAL ""
rename-command EVALSHA ""
rename-command SCRIPT ""
Nếu ứng dụng dùng Lua scripts cho atomic operations (phổ biến để implement distributed lock, leaky bucket rate limiter...), cân nhắc chuyển sang FUNCTION (Redis 7+) — xem phần tiếp theo.
FUNCTION — Thay Thế An Toàn Hơn EVAL
Redis 7.0 ra mắt Functions (RFC PR #758) như một replacement cho EVAL. Thay vì client gửi Lua code mỗi lần gọi, function được đăng ký vào server một lần và được gọi bằng tên.
# Đăng ký function (chỉ cần một lần — thường trong deployment script)
redis-cli FUNCTION LOAD "#!lua name=mylib\nredis.register_function('my_hset', function(keys, args) redis.call('HSET', keys[1], args[1], args[2]) return 1 end)"
# Gọi function
redis-cli FCALL my_hset 1 user:42 name Alice
# (integer) 1
# Liệt kê functions đã đăng ký
redis-cli FUNCTION LIST
Lợi thế về bảo mật so với EVAL:
- Code function được lưu trên server, không phải client gửi script tùy ý mỗi lần gọi. Có thể audit các function đã đăng ký.
- ACL có thể restrict lệnh
FUNCTION LOAD(chỉ cho admin) trong khi vẫn cho phépFCALL(cho app user). - Functions được persist cùng dataset (
SAVE/BGSAVE) — không mất khi restart.
# ACL: app user chỉ được FCALL, không được load function mới
user app on >password ~* &* +@all -FUNCTION|LOAD -FUNCTION|DELETE -@scripting
# Cho phép gọi function đã đăng ký:
# +FCALL +FCALL_RO
Functions vẫn còn tương đối mới (Redis 7+). Nếu đang chạy Redis 6.x, EVAL + restrict chặt ACL là cách tiếp cận thực tế hơn.
DEBUG Subcommands
DEBUG là lệnh internal dành cho testing và debugging Redis core. Các subcommand nguy hiểm nhất:
DEBUG SEGFAULT: raise SIGSEGV ngay lập tức, crash Redis process. DoS hoàn toàn, không có graceful shutdown.DEBUG SLEEP <seconds>: block event loop trong N giây. Mọi command từ mọi client đều bị queue và không xử lý. Với giá trị lớn (SLEEP 3600), Redis trở nên unusable.DEBUG RELOAD: save dataset xuống disk rồi reload — tương tự BGSAVE nhưng blocking và có thể mất data nếu serialization có bug.DEBUG JMAP,DEBUG QUICKLIST-PACKED-THRESHOLD: các subcommand khác thay đổi internal state.
Production không bao giờ cần lệnh DEBUG. Disable hoàn toàn:
# rename-command
rename-command DEBUG ""
# ACL
# user app ... -DEBUG
Nếu cần DEBUG cho on-call debugging (ví dụ DEBUG OBJECT key để xem encoding), rename thành chuỗi ngẫu nhiên và chỉ dùng trong admin session. Document cẩn thận trong runbook — chuỗi rename cần lưu trữ an toàn.
MIGRATE Và SHUTDOWN
MIGRATE — Data Exfiltration Vector
MIGRATE host port key|"" destination-db timeout [COPY] [REPLACE] [AUTH password] [KEYS key [key ...]] copy key từ Redis hiện tại sang Redis khác. Redis nguồn connect TCP đến host:port được chỉ định trong lệnh — hoàn toàn do client kiểm soát.
Nếu attacker có quyền gọi MIGRATE:
- Chỉ định
hostvàportlà server của attacker. - Redis connect ra ngoài và copy key đến đó — data exfiltration không để lại log trên ứng dụng.
- Attacker không cần network access vào Redis (nếu firewall inbound), nhưng Redis lại tự connect outbound.
App user không cần MIGRATE trong hoạt động thường ngày. Disable hoàn toàn cho app user:
rename-command MIGRATE ""
# Hoặc ACL: user app ... -MIGRATE
SHUTDOWN
SHUTDOWN [NOSAVE|SAVE] [NOW] [FORCE] [ABORT] dừng Redis process. Đây là DoS vector rõ ràng. App user không cần quyền này; graceful shutdown trong ops nên thực hiện qua systemd/supervisord thay vì redis-cli SHUTDOWN.
rename-command SHUTDOWN ""
# Hoặc ACL: user app ... -SHUTDOWN
Nếu vẫn cần SHUTDOWN từ redis-cli trong một số ops workflow, rename thay vì disable và lưu tên mới trong secret manager.
Cluster Commands Đặc Biệt Nguy Hiểm
Trong môi trường Redis Cluster, một số subcommand của CLUSTER có thể phá vỡ topology:
CLUSTER RESET [HARD|SOFT]: hard reset xóa toàn bộ cluster state, disconnects từ mọi node khác, xóa slot assignment. Gọi nhầm trên một node đang chạy gây hậu quả nghiêm trọng.CLUSTER SETSLOT <slot> IMPORTING|MIGRATING|NODE|STABLE: thay đổi slot ownership — nếu set sai sẽ làm key inaccessible hoặc trigger failover không mong muốn.CLUSTER FAILOVER [FORCE|TAKEOVER]: trigger failover thủ công — nếu bị gọi sai thời điểm gây downtime.
Restrict các cluster admin commands về admin user:
# ACL — app user không cần cluster admin operations
user app on >password ~app:* &* +@all -CLUSTER|RESET -CLUSTER|FAILOVER -CLUSTER|SETSLOT
App user thường vẫn cần một số subcommand của CLUSTER để discover slot mapping (CLUSTER KEYSLOT, CLUSTER SLOTS, CLUSTER NODES). Dùng granular subcommand restriction thay vì disable toàn bộ CLUSTER.
Xác Minh Lệnh Đã Bị Disable
Sau khi cấu hình rename-command hoặc ACL, cần test để xác nhận restriction hoạt động đúng:
# Test rename-command (disable) — "unknown command" khi bị disable hoàn toàn
redis-cli FLUSHALL
# (error) ERR unknown command 'FLUSHALL', with args beginning with:
redis-cli KEYS "*"
# (error) ERR unknown command 'KEYS', with args beginning with:
redis-cli DEBUG SLEEP 0
# (error) ERR unknown command 'DEBUG', with args beginning with:
# Test ACL restriction — "NOPERM" khi user không có quyền
redis-cli -u redis://app:apppassword@localhost:6379 FLUSHALL
# (error) NOPERM this user has no permissions to run the 'flushall' command
redis-cli -u redis://app:apppassword@localhost:6379 CONFIG SET maxmemory 100mb
# (error) NOPERM this user has no permissions to run the 'config|set' command
# Admin user vẫn có quyền đầy đủ
redis-cli -u redis://admin:adminpassword@localhost:6379 DBSIZE
# (integer) 12345
Nên tạo automated test suite (bash script hoặc Python) chạy sau mỗi lần deploy config Redis, verify từng lệnh trong danh sách "phải bị block" trả về đúng error, và từng lệnh trong danh sách "admin được phép" thực hiện được.
#!/bin/bash
# verify-redis-restrictions.sh
APP_URL="redis://app:apppassword@localhost:6379"
ADMIN_URL="redis://admin:adminpassword@localhost:6379"
check_blocked() {
local cmd="$1"
local result
result=$(redis-cli -u "$APP_URL" $cmd 2>&1)
if echo "$result" | grep -qE "NOPERM|unknown command"; then
echo "[PASS] $cmd is blocked for app user"
else
echo "[FAIL] $cmd is NOT blocked! Got: $result"
exit 1
fi
}
check_allowed() {
local cmd="$1"
local result
result=$(redis-cli -u "$ADMIN_URL" $cmd 2>&1)
if echo "$result" | grep -qE "NOPERM|unknown command"; then
echo "[FAIL] $cmd is blocked for admin user!"
exit 1
else
echo "[PASS] $cmd is allowed for admin user"
fi
}
check_blocked "FLUSHALL"
check_blocked "FLUSHDB"
check_blocked "KEYS *"
check_blocked "MONITOR"
check_blocked "DEBUG SLEEP 0"
check_blocked "MIGRATE localhost 9999 nonexistent 0 100"
check_blocked "SHUTDOWN ABORT"
check_allowed "DBSIZE"
check_allowed "INFO server"
echo "All checks passed."
Kế Hoạch Khôi Phục Admin Access
Restriction quá chặt có thể khóa chính mình ra khỏi Redis khi cần xử lý sự cố. Một số tình huống hay gặp:
Tình huống 1: CONFIG bị disable, cần thay đổi config
Nếu CONFIG bị disable hoàn toàn qua rename-command CONFIG "":
- Không thể dùng
CONFIG SEThayCONFIG GETtừ redis-cli. - Giải pháp: edit file
redis.conftrực tiếp + restart Redis. Đây là quy trình dự phòng bắt buộc. - Với rename (không disable): dùng tên đã rename. Tên này phải được lưu trong runbook / secret manager, không chỉ trong đầu của một người.
Tình huống 2: SHUTDOWN bị disable, cần stop Redis gracefully
- Dùng signal:
kill -SIGTERM <redis_pid>— Redis xử lý SIGTERM bằng cách flush data và shutdown gracefully. - systemd:
systemctl stop redis. - Docker:
docker stop <container>gửi SIGTERM.
Tình huống 3: Default user bị disable, quên password admin
- Stop Redis process (qua systemd/signal).
- Thêm tạm thời
user default on nopass ~* &* +@allvào config (hoặc comment outaclfile). - Restart Redis.
- Reset password admin, test lại.
- Xóa dòng tạm, restart lần nữa.
Quan trọng: document procedure này trong runbook và test định kỳ (ít nhất mỗi quý). Đừng để lần đầu tiên thực hiện là lúc sự cố 2 giờ sáng.
Anti-patterns & Best Practices
Anti-patterns
- Rename lệnh sang chuỗi ngắn hoặc đoán được:
rename-command CONFIG "cfg"hayrename-command DEBUG "dbg"không có bảo vệ thực sự. Attacker brute-force hoặc đoán được dễ dàng. - Commit chuỗi rename vào git: bất kỳ ai có access repo đều biết tên lệnh thật. Git history cũng lưu lại nếu xóa sau.
- Chỉ disable cho app user nhưng bỏ qua default user: nếu default user vẫn có quyền full và không có password (cấu hình mặc định của Redis), ACL restriction cho app user không có tác dụng với client không authenticate.
- Không đồng bộ cấu hình trên tất cả cluster node: node không được cấu hình vẫn cho phép lệnh, và client kết nối đến đó bypass restriction.
- Disable CONFIG rồi không có recovery plan: admin operations như thay đổi
maxmemory, reload cert TLS đều cần CONFIG. Không có recovery procedure là rủi ro operational. - Dùng rename-command để replace ACL: rename không phân biệt được user, không có audit trail, không thể change runtime. Đây là workaround cho Redis < 6, không phải best practice.
- Để
enable-module-command yes(mặc định) trên production Redis 7+: nên explicit setnohoặclocalkhi không cần runtime module load. - Không cập nhật Redis sau khi có CVE: CVE-2022-0543 là ví dụ điển hình — không cập nhật đồng nghĩa để EVAL là RCE vector trên Debian-based systems.
Best Practices
- Dùng ACL thay vì rename-command cho Redis 6+. ACL granular hơn, runtime-configurable, per-user.
- Disable
defaultuser hoặc đặt password. Default user là điểm yếu đầu tiên cần vá. - Disable FLUSHALL, FLUSHDB, MONITOR, KEYS, MIGRATE cho app user.
- Restrict CONFIG cho app user. Admin có thể dùng
CONFIG GET/SETnhưng app code không cần. - Disable MODULE runtime loading (
enable-module-command notrên Redis 7+). Load module tại startup qualoadmodule. - Restrict hoặc disable EVAL nếu ứng dụng không dùng Lua scripting. Cập nhật Redis để patch CVE-2022-0543.
- Dùng FUNCTION (Redis 7+) thay EVAL nếu cần scripting — model bảo mật tốt hơn.
- Rename (không disable) CONFIG và DEBUG với chuỗi ngẫu nhiên đủ dài nếu cần cho admin scripts. Lưu chuỗi trong secret manager, không trong git.
- Test restriction sau mỗi config change — automated script.
- Document và test recovery procedure ít nhất mỗi quý.
- Kết hợp restriction lệnh với các lớp hardening khác: AUTH/ACL (bài 113-114), TLS (bài 115), network isolation (bài 117).
Config snippet khuyến nghị (Redis 7+)
# redis.conf — production hardening
# Tắt default user
user default off
# Admin user — full access
user admin on >${REDIS_ADMIN_PASSWORD} ~* &* +@all
# App user — restricted
user app on >${REDIS_APP_PASSWORD} ~app:* &app:* +@all -@dangerous -CONFIG -MODULE -SHUTDOWN -DEBUG -MIGRATE -MONITOR -KEYS -FLUSHALL -FLUSHDB
# Module restriction — chỉ load tại startup
enable-module-command no
loadmodule /usr/lib/redis/modules/redisbloom.so
# (Các lệnh khác như EVAL cân nhắc restrict nếu không dùng)
# user app ... -@scripting
Tổng Kết & Quiz
Tổng kết
- Redis có nhiều lệnh có thể xóa data (FLUSHALL, FLUSHDB), thay đổi config runtime (CONFIG SET — cryptojacking pattern), crash process (DEBUG SEGFAULT), exfiltrate data (MIGRATE), hoặc thực thi code tùy ý (MODULE LOAD, EVAL sandbox escape).
rename-commandtrong redis.conf là legacy approach: disable hoặc obfuscate tên lệnh, nhưng server-wide, không per-user, cần restart, khó đồng bộ cluster.- ACL (Redis 6+) là cách tiếp cận ưu tiên: per-user, granular đến subcommand level, có thể thay đổi runtime, kết hợp key pattern và channel pattern.
enable-module-command no(Redis 7+) disable runtime MODULE LOAD ở tầng config — cứng hơn ACL restriction. Kết hợp vớiloadmoduleđể pre-load module whitelist tại startup.- CVE-2022-0543: Lua sandbox escape trong EVAL trên Debian/Ubuntu, CVSS 10.0. Patch: cập nhật Redis 6.2.7+/7.0.0+ hoặc disable EVAL qua ACL
-@scripting. - FUNCTION (Redis 7+) có model bảo mật tốt hơn EVAL: code được đăng ký trước, admin restrict FUNCTION LOAD, app dùng FCALL — không gửi arbitrary script mỗi request.
- Cần test restriction automated và document recovery procedure. Restriction quá chặt mà không có recovery plan là rủi ro operational.
Quiz 5 câu
- Tại sao
CONFIG SET dir /root/.sshkết hợp với một số lệnh khác có thể trở thành attack vector nghiêm trọng? Liệt kê chuỗi lệnh đầy đủ và tại sao disable CONFIG cho app user là quan trọng. - So sánh
rename-command FLUSHALL ""vàACL SETUSER app ... -FLUSHALL: mỗi cách có lợi thế và giới hạn gì? Trong Redis 7, bạn nên chọn cách nào? - CVE-2022-0543 ảnh hưởng đến EVAL như thế nào? Tại sao chỉ xảy ra trên Debian/Ubuntu mà không phải mọi distro? Có thể mitigate mà không disable EVAL không?
- Trong Redis Cluster với 6 node, bạn muốn disable FLUSHALL. Cần làm gì và lỗi gì có thể xảy ra nếu làm không đồng bộ?
- Phân biệt EVAL và FUNCTION (Redis 7+) từ góc độ bảo mật. Tại sao FUNCTION cho phép mô hình phân quyền chi tiết hơn?
Đáp án gợi ý
CONFIG SET dir /root/.ssh→CONFIG SET dbfilename authorized_keys→SET payload "<attacker SSH public key>"→BGSAVE. Kết quả: Redis ghi file/root/.ssh/authorized_keyschứa public key của attacker, attacker SSH vào server với quyền root. Chain này yêu cầu Redis chạy với quyền root (anti-pattern nhưng hay gặp) và có quyềnCONFIG SET. Disable CONFIG cho app user ngắt chain ngay tại bước đầu.rename-command: server-wide, áp dụng cho mọi user, không restart không thay đổi được, admin cũng bị ảnh hưởng. ACL: per-user, admin vẫn có quyền, thay đổi runtime, granular. Redis 7 nên dùng ACL;rename-commandchỉ cần thiết khi muốn hard-disable toàn bộ server (kể cả admin) mà không cần restart capability.- CVE-2022-0543: Debian/Ubuntu build Lua 5.1 như shared library và giữ lại
packagemodule. Redis sandbox không loại bỏ đượcpackage.loadlib, cho phép Lua code load arbitrary.so→ RCE. CentOS/Alpine build static Lua không cópackage→ không ảnh hưởng. Mitigation không disable EVAL: patch lên Redis 6.2.7+/7.0+; hoặc restrict ACL-@scriptingchỉ cho app user không tin tưởng. - Phải cấu hình rename-command (hoặc ACL) đồng nhất trên tất cả 6 node. Nếu thiếu một node: client connect đến node đó vẫn gọi được FLUSHALL — restriction vô nghĩa. Redis Cluster không propagate rename-command giữa các node tự động — đây là lý do ACL + aclfile với tooling đồng bộ (Ansible, Chef, K8s ConfigMap) quan trọng.
- EVAL: client gửi arbitrary Lua code mỗi lần gọi — không thể audit hay restrict code cụ thể, chỉ có thể allow/deny toàn bộ EVAL. FUNCTION: code được FUNCTION LOAD vào server trước (chỉ admin làm được nếu ACL restrict FUNCTION|LOAD), app chỉ FCALL tên đã đăng ký — không gửi arbitrary code. Admin có thể audit danh sách functions, restrict app user không load function mới trong khi vẫn cho FCALL, và functions persist cùng dataset.
Bài tiếp theo
Bài 117 đi vào Network Isolation — VPC, firewall rules, security group và bind address để giới hạn network-level access đến Redis, tầng bảo vệ song song với ACL và TLS.
