Nginx通过map指令在log_format中动态掩码手机号等敏感信息,从日志源头脱敏,满足最小必要原则;支持对$args、$http_referer、$http_user_agent中的手机号正则匹配并替换为*格式,同时可轻量级处理IP和用户名。

直接在 Nginx 的 log_format 中结合 map 指令做动态掩码,是实现访客敏感信息(如手机号)日志脱敏最稳妥、无需额外模块的方式。它不修改请求流,不增加代理层,从日志源头就控制输出内容,既满足《个人信息保护法》对“最小必要”采集的要求,又保留关键调试字段。
识别并提取 URL 中的手机号参数
手机号常出现在查询参数中,例如 ?mobile=13812345678、&phone=139xxxx8888 或 tel=137xxxxxxxx。Nginx 本身不解析参数结构,但可通过正则从 $args 提取并掩码:
- 用
map定义新变量$sanitized_args,匹配含手机号特征的键值对 - 正则需覆盖常见键名(mobile/phone/tel/cell)和典型号码格式(11位数字、带+86前缀等)
- 匹配成功时,将整个键值对替换为掩码形式,如
mobile=138****5678
示例配置:
map $args $sanitized_args {
default $args;
~*(mobile|phone|tel|cell)=([+86]*1[3-9]\d{9})&? "$1=***$2";
~*(mobile|phone|tel|cell)=([+86]*1[3-9]\d{9})$ "$1=***$2";
}注意:正则末尾用 &? 和 $ 分别处理参数在中间或结尾的情况,避免误截断其他参数。
对 Referer 或 User-Agent 中嵌入的手机号做掩码
部分 H5 页面或测试 SDK 会把手机号拼进 $http_referer 或 $http_user_agent,例如:
Referer: https://example.com/login?uid=13812345678User-Agent: MyApp/2.1 (iPhone; iOS 17; 13812345678)
此时不能只靠 $args,需单独对这两个变量做映射:
map $http_referer $sanitized_referer {
default $http_referer;
~*([?&]uid|user_id|mobile|phone)=([+86]*1[3-9]\d{9}) "$1=***$2";
}
<p>map $http_user_agent $sanitized_ua {
default $http_user_agent;
~<em>(()([^)]</em>1[3-9]\d{9}[^)]*)) "$1***$2";
}</p>第二条正则利用括号捕获上下文,仅替换括号内出现的手机号,避免误伤 UA 中正常版本号或设备型号。
构建安全日志格式并启用
定义脱敏后的新日志格式,显式引用所有已处理变量,禁用原始高危字段:
log_format secure '$remote_addr - $remote_user [$time_local] ' '"$request_method $uri?$sanitized_args $http_version" ' '$status $body_bytes_sent ' '"$sanitized_referer" "$sanitized_ua"';
- 不再使用
$args或$http_referer原始值,全部替换为对应掩码变量 - 保留
$uri(不含参数路径)、$status、$body_bytes_sent等无敏感风险字段 - 若业务完全不需要
$remote_user,可直接删去,避免 Basic Auth 用户名泄露
最后在 server 或 location 块中启用:
access_log /var/log/nginx/access_secure.log secure;
补充:IP 地址与用户名的轻量级脱敏
虽然问题聚焦手机号,但实际日志中 $remote_addr 和 $remote_user 同样属于个人信息:
- IP 可用
map截断后两段:~^(\d+\.\d+)\. $1.0.0,保留地域粒度 - 用户名若来自认证头,建议统一设为
"-"或哈希后截取前6位(md5($remote_user) ~^(.{6}) $1) - 避免用
substr类函数——Nginx 原生不支持,必须依赖map + 正则捕获实现
这些处理可与手机号掩码并存,共用同一套 map 逻辑体系,维护成本低且生效即时。











