如何优雅地处理前端敏感信息并避免常见安全漏洞

司空英杰 安全 阅读 2,305
赞 15 收藏
二维码
手机扫码查看
反馈

项目背景和敏感信息问题的由来

最近做了一个电商后台管理系统,涉及到订单、用户数据的管理。开始觉得不就是个普通的 CRUD 项目嘛,结果上线后发现敏感信息泄露的问题挺严重的。用户的手机号、地址这些隐私数据在日志里都能看到明文,这可把我吓了一跳。

如何优雅地处理前端敏感信息并避免常见安全漏洞

其实项目初期压根没考虑过这些问题,只想着快点把功能做完上线。后来是安全审计团队发现了这个问题,给我发了封长长的邮件,说这是重大安全隐患。这才意识到,敏感信息处理还真是个不容忽视的大问题。

最大的坑:日志里的明文敏感信息

最头疼的就是日志系统了。我们用的是常见的 log4js,本来觉得挺好用的,但没想到它会把所有请求参数都打印出来。比如这样:

// 原始日志输出
logger.info('用户查询订单', {
    userId: 12345,
    phone: '13800138000',
    address: '上海市浦东新区XX路XX号'
});

看起来很正常对吧?但实际运行时,这些敏感信息就全暴露在日志文件里了。更糟糕的是,我们的日志还接入了第三方监控平台,这意味着敏感信息直接传到了外部系统。

开始想了个简单粗暴的办法:在每个接口里手动过滤敏感字段。结果可想而知,不仅代码重复性高,还容易漏掉。有一次因为新增了一个支付接口忘了处理,又差点出事。

解决方案:全局拦截与脱敏处理

折腾了几天,最后决定从源头解决问题,在请求响应的中间件层面统一处理。这里分享下最终的实现方案:

// 敏感字段配置
const sensitiveFields = ['phone', 'address', 'idCard'];

// 脱敏函数
function desensitize(value, type = 'default') {
    if (!value) return value;
    
    switch(type) {
        case 'phone':
            return value.replace(/(d{3})d{4}(d{4})/, '$1****$2');
        case 'idCard':
            return value.replace(/(d{6})d{8}(d{4})/, '$1********$2');
        default:
            return '*'.repeat(value.length);
    }
}

// 中间件处理
app.use((req, res, next) => {
    const originalJson = res.json;
    res.json = function(data) {
        const processData = (obj) => {
            if (typeof obj !== 'object' || obj === null) return obj;

            for (let key in obj) {
                if (sensitiveFields.includes(key)) {
                    const type = key === 'phone' ? 'phone' : 
                                 key === 'idCard' ? 'idCard' : 'default';
                    obj[key] = desensitize(obj[key], type);
                } else if (typeof obj[key] === 'object') {
                    obj[key] = processData(obj[key]);
                }
            }
            return obj;
        };

        data = processData(data);
        originalJson.call(this, data);
    };
    next();
});

这个方案的核心思路是:通过中间件拦截所有响应数据,自动检测并脱敏敏感字段。相比之前的逐个接口处理,这种方式明显更优雅,也更不容易出错。

踩过的几个小坑

虽然整体方案还不错,但在实现过程中还是遇到了不少麻烦:

  • 性能问题:最初版本每次都会深度遍历整个对象,导致响应变慢。后来加了个类型判断,只处理对象和数组,其他类型直接跳过。
  • 嵌套结构处理:有些接口返回的数据特别深,一开始漏掉了递归处理,导致深层级的敏感信息没被脱敏。现在用了递归函数解决了这个问题。
  • 特殊情况:某些字段名字相似但不是敏感信息,比如 shippingAddress 和 billingAddress,容易误伤。最后通过更精确的字段匹配规则解决了。

还有一些未解的小问题

目前这套方案基本能满足需求,但还有些地方不够完美:

首先是配置管理这块,现在的敏感字段列表是写死在代码里的。如果以后需要动态调整,可能得改成从配置文件读取。不过暂时还没这个需求,也就先这样了。

其次是日志记录粒度的问题。有时候排查问题需要查看完整信息,但又不能让这些信息出现在普通日志里。考虑过搞个特殊的 debug 模式,但还没来得及实现。

回顾与反思

回头看这个项目,最大的收获是对安全有了更深的认识。以前总觉得安全是后端的事,前端只要管好页面展示就行。但实际上,前后端的界限越来越模糊,很多安全问题都需要从前端就开始考虑。

另一个感触是,看似简单的功能,真要做到完善还挺复杂的。就像这个脱敏处理,从最初的简单过滤到最后的完整方案,中间经历了好几次迭代。这也提醒我,技术方案一定要留有扩展空间,别一开始就写死了。

以上就是我在项目中处理敏感信息的一些经验,希望对你有帮助。具体的实现细节可能还得根据项目情况调整,欢迎评论区交流你的做法。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论