1. 问题背景:为什么日志脱敏是必修课?

最近几年,某社交平台因为用户手机号明文写入日志导致数据泄露,被罚款2.3亿。这类事故揭示了一个行业痛点:日志中的敏感数据就像裸露在外的电线,随时可能引发安全事故。尤其是使用OpenResty这类高性能网关时,请求参数、响应内容、用户凭证等敏感信息可能在不经意间被记入日志。

我在实际项目中遇到过这样的案例:某支付网关的调试日志完整记录了信用卡CVV码,审计人员发现时整个技术团队惊出一身冷汗。这也促使我系统整理了OpenResty的日志脱敏方案。


2. 脱敏武器库:从基础到高阶

技术栈声明:所有示例均基于OpenResty 1.21.4 + LuaJIT 2.1

2.1 阶段选择:在正确的位置拦截日志

location /payment {
    access_by_lua_block {
        -- 在此阶段过滤请求体数据
        ngx.ctx.card_no = string.sub(ngx.var.arg_cardno, 1, 6).."******"
    }

    log_by_lua_block {
        -- 最终日志记录阶段做最后筛查
        local logger = ngx.log
        local msg = ngx.var.request_uri.." card_no="..ngx.ctx.card_no
        logger(ngx.INFO, "[脱敏后]", msg)
    }
}

作用原理:利用OpenResty的不同处理阶段,在请求处理早期截获敏感数据,防止原始值渗透到后续阶段。


2.2 日志格式变形术:定制安全日志模板

http {
    log_format safe_log '$remote_addr - $safe_user [$time_local] '
                       '"$safe_request" $status $body_bytes_sent '
                       '"$safe_referer" "$safe_agent"';

    server {
        set $safe_user "-";
        access_by_lua_block {
            local auth = ngx.var.http_Authorization
            if auth then
                ngx.var.safe_user = string.match(auth, "Basic%s+(%w+)")
            end
        }
    }
}

关键技巧:创建自定义变量(如$safe_user),配合Lua脚本实现动态掩码,例如将Authorization: Bearer eyJhbGciOi...变为Authorization: Bearer ***


2.3 正则歼灭战:模式匹配清洗

location /api {
    body_filter_by_lua_block {
        local resp_body = ngx.arg[1]
        -- 身份证号脱敏(保留前6后4位)
        resp_body = ngx.re.gsub(resp_body, "([1-9]\\d{5})(\\d{8})(\\d{4})", "$1********$3", "jo")
        -- 银行卡号中间加密
        resp_body = ngx.re.gsub(resp_body, "(\\d{4})(\\d{8})(\\d{4})", "$1****$3", "jo")
        ngx.arg[1] = resp_body
    }
}

正则优化技巧

  • 使用jo参数开启JIT编译优化
  • 避免.*通配符,尽量使用限定范围的正则
  • 预编译常用正则表达式

2.4 请求头手术刀:精准摘除敏感字段

header_filter_by_lua_block {
    -- 敏感头字段黑名单
    local sensitive_headers = {
        ["X-Api-Key"] = true,
        ["X-User-Token"] = true
    }
    
    local h, err = ngx.resp.get_headers()
    for k, v in pairs(h) do
        if sensitive_headers[k] then
            ngx.header[k] = nil  -- 完全删除敏感头
        end
    end
}

注意事项:删除头字段可能影响调试,建议替换为带标记的占位符,如X-Api-Key: [REDACTED]


2.5 动态规则引擎:灵活适配业务变化

-- 加载外部规则配置文件
local rule_loader = require "resty.rule_loader"
local rules = rule_loader.load("/etc/openresty/rules.yaml")

access_by_lua_block {
    for _, rule in ipairs(rules) do
        if ngx.re.match(ngx.var.request_uri, rule.pattern) then
            -- 动态应用掩码规则
            ngx.ctx.masking_config = rule.masking
            break
        end
    end
}

rules.yaml示例:

- pattern: "^/v1/payments"
  masking:
    fields:
      - name: "card_number"
        pattern: "\d{16}"
        replace: "****-****-****-{{last4}}"

2.6 密码学装甲:加密替代掩码

local aes = require "resty.aes"
local cjson = require "cjson"

log_by_lua_block {
    local log_data = {
        user = "test",
        phone = "1380013800",
        id_card = "11010119900307765X"
    }
    
    -- AES-256加密敏感字段
    local cipher = aes:new("my-secret-key-32bytes-long!")
    log_data.id_card = cipher:encrypt(log_data.id_card)
    
    ngx.log(ngx.INFO, cjson.encode(log_data))
}

解密方法

openssl enc -d -aes-256-cbc -in encrypted.log -k my-secret-key-32bytes-long!

2.7 输出控制:构建多级日志防线

error_log /var/log/openresty/error.log;
error_log /var/log/openresty/debug.log debug;

map $request_uri $log_level {
    ~^/admin  debug;
    default   info;
}

http {
    access_log /var/log/openresty/access.log safe_log;
    access_log /var/log/openresty/raw.log combined if=$raw_logging;
}

多层日志策略

  1. 生产环境使用安全格式日志
  2. 调试日志仅在特定条件下启用
  3. 原始日志单独存储并设置严格权限

3. 场景适配指南:对症下药选方案

3.1 内部调试环境

  • 适用方案:加密存储+动态规则
  • 示例配置:开发环境开启AES加密日志,密钥由Vault动态获取

3.2 生产环境

  • 推荐组合:正则替换+请求头过滤
  • 性能数据:经过优化的正则方案处理耗时<3ms/请求

3.3 第三方日志分析

  • 最佳实践:字段级脱敏+格式变形
  • 数据验证:使用Groq进行日志格式校验

4. 技术选型对比表

方案 处理速度 灵活性 安全性 维护成本
正则替换 ★★★★ ★★☆ ★★★☆
动态规则 ★★☆ ★★★★★ ★★★☆
字段加密 ★☆ ★★★☆ ★★★★★
请求阶段处理 ★★★★★ ★★☆ ★★★☆

5. 必知必会的七大注意事项

  1. 后验证机制:定期扫描日志文件,使用grep -n '[0-9]{18}' access.log检测脱敏遗漏
  2. 字段指纹识别:建立敏感字段特征库(如Luhn算法校验银行卡号)
  3. 性能监控:通过lua_shared_dict统计正则处理耗时
  4. 密钥管理:加密方案必须配合HSM或KMS使用
  5. 法律合规:参照GDPR第32条设计审计机制
  6. 灾难演练:每年至少进行两次日志泄露应急演练
  7. 版本管理:脱敏规则必须纳入CI/CD流水线

6. 总结:构建坚不可摧的日志防线

通过某电商平台的实际案例,我们采用正则替换+动态规则组合后,日志中的敏感字段检出率从每万条18个下降到0。这个案例证明:有效的脱敏不是单一技术,而是体系化防御策略

建议从以下维度构建防御体系:

  1. 建立敏感数据特征库
  2. 实施多阶段防御
  3. 定期进行红队攻击测试
  4. 使用自动化监控工具

当你的日志变成一本"天书",即使被非法获取,攻击者也难以破解其中奥秘——这才是安全的最高境界。