一、为什么敏感信息不能打印到日志?
- 日志扩散范围不可控
程序日志会被存储、同步、备份,还可能被运维、测试、第三方监控平台查看。敏感信息一旦写入日志,就脱离了加密保护,任何人能看日志,就能拿到明文数据。
- 合规要求严格禁止
无论是《网络安全法》《个人信息保护法》,还是 GDPR、PCI DSS(支付卡规范),都明确要求禁止明文存储 / 传输用户敏感隐私信息,违规最高可处年收入 5% 的罚款。
- 攻击面无限扩大
若服务器被入侵,攻击者第一个会翻日志;哪怕是内部人员误操作,也能轻松导出包含密码、密钥的日志文件,造成不可逆的泄露。
二、常见的错误代码示例(千万别这么写!)
1. 直接打印用户登录参数(最典型)
// Java 错误示例
public UserInfo login(String username, String password) {
// 错误:明文打印密码到日志
log.info("用户登录请求,用户名:{},密码:{}", username, password);
// 业务逻辑...
}
# Python 错误示例
def user_login(username, password):
# 错误:明文打印敏感信息
print(f"登录信息:用户名={username},密码={password}")
# 业务逻辑...
2. 打印完整身份证、手机号
// Node.js 错误示例
function saveUserInfo(user) {
// 错误:直接输出完整身份证、手机号
console.log("保存用户信息:", user);
// user = { name: "张三", idCard: "110101199003074567", phone: "13800138000" }
}
3. 打印密钥、Token 等配置信息
// Go 错误示例
func initDB() {
dsn := "user:password@tcp(127.0.0.1:3306)/test"
// 错误:打印数据库明文连接串
fmt.Println("数据库连接信息:", dsn)
}
三、正确做法:敏感信息脱敏(核心解决方案)
通用脱敏规则(行业标准):
- 手机号:隐藏中间 4 位 →
138****8000 - 身份证:隐藏中间 8-10 位 →
110***********4567 - 密码、密钥:直接替换为
*** - 邮箱:隐藏用户名中间部分 →
z****g@163.com
通用脱敏工具方法(全语言通用思路)
1. Java 实现(SLF4J 日志通用)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogDesensitizeUtil {
private static final Logger log = LoggerFactory.getLogger(LogDesensitizeUtil.class);
// 手机号脱敏
public static String desensitizePhone(String phone) {
if (phone == null || phone.length() != 11) return phone;
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
// 密码/密钥直接隐藏
public static String hideSensitive(String sensitive) {
return "***";
}
// 正确示例:登录日志脱敏打印
public static void loginSuccess(String username, String password) {
log.info("用户登录成功,用户名:{},密码:{}",
username,
hideSensitive(password)); // 密码不打印明文
}
}
2. Python 实现
# 脱敏工具函数
def desensitize_phone(phone):
if len(phone) != 11:
return phone
return phone[:3] + "****" + phone[-4:]
def hide_sensitive(info):
return "***"
# 正确示例
def user_login(username, password):
# 密码脱敏打印
print(f"登录请求:用户名={username},密码={hide_sensitive(password)}")
3. 通用对象脱敏(打印实体类不泄露敏感信息)
toString()(或序列化方法),自动脱敏敏感字段:// Java 实体类脱敏
public class User {
private String username;
private String password; // 敏感字段
private String phone; // 敏感字段
// 重写toString,敏感字段脱敏
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + hideSensitive(password) + '\'' + // 脱敏
", phone='" + desensitizePhone(phone) + '\'' + // 脱敏
'}';
}
}
四、开发必守的日志安全规范
- 三不打印原则
不打印密码、密钥、Token;不打印完整身份证、银行卡号;不打印明文用户隐私信息。
- 本地调试与上线区分
本地调试可临时打印敏感信息,上线前必须删除或脱敏,不要用
debug日志侥幸规避。 - 使用成熟的脱敏框架
Java 可使用
logback-desensitize、mybatis-plus脱敏插件;Python 使用logging过滤器,避免重复造轮子。 - 日志权限管控
生产环境日志仅授权限人员查看,定期清理过期日志,避免日志长期留存。
五、总结
敏感信息打印日志,是典型的「低级别、高危害」漏洞 —— 不需要复杂攻击,只要一行不规范的代码,就能导致大规模数据泄露。
总结
- 敏感信息明文打日志是高危漏洞,违反合规要求且极易造成泄露;
- 核心解决方案是脱敏,密码直接隐藏,手机号、身份证做部分隐藏;
- 全语言通用脱敏思路:重写对象打印方法、封装脱敏工具函数、区分调试与生产日志;
- 养成日志安全习惯,是每个开发者的必备素养。