前端项目中敏感信息的安全处理与实战避坑指南

端木文娟 移动 阅读 2,894
赞 18 收藏
二维码
手机扫码查看
反馈

为啥要折腾敏感信息的前端处理?

最近在做几个移动端项目,用户登录、支付、个人资料这些功能都涉及敏感信息。说实话,一开始我也没太在意,觉得“不就是把手机号、身份证号显示出来嘛”,直到有一次测试说“你这页面后退还能看到上一个用户的手机号”,我才意识到:前端对敏感信息的处理,真不是加个星号那么简单。

前端项目中敏感信息的安全处理与实战避坑指南

于是我把手头用过的几种方案拉出来遛一遛,看看谁更靠谱、谁更省心。这篇文章就聊聊我在实际项目中踩过的坑和最终的选择。

方案一:纯前端脱敏(最简单但也最危险)

很多人第一反应是:在 JS 里把数据处理一下,比如手机号变成 138****1234。代码写起来确实快:

function maskPhone(phone) {
  if (!phone) return '';
  return phone.replace(/(d{3})d{4}(d{4})/, '$1****$2');
}

看起来没问题,但问题大了去了。我之前在一个 SPA 项目里就这么干,结果用户切换账号时,因为 Vue 的 keep-alive 缓存了组件,旧数据没清干净,新用户看到了前一个用户的部分信息。调试半天才发现,脱敏逻辑只在 mounted 里跑了一次,数据变了但视图没更新。

而且,这种方案完全依赖前端逻辑。如果有人直接调接口拿原始数据(比如通过 DevTools 拦截响应),或者 SSR 渲染时没做处理,敏感信息照样裸奔。所以现在我基本不用这种纯前端脱敏——除非是临时 demo,或者数据本身就不算真敏感。

方案二:后端脱敏 + 前端展示(推荐!)

我现在的主力方案是让后端返回脱敏后的数据。比如用户详情接口直接返回 "phone": "138****1234",前端只负责展示。这样从源头上避免了敏感信息泄露的风险。

举个例子,假设我们调用用户信息接口:

// 假设后端已经脱敏
fetch('https://jztheme.com/api/user/profile')
  .then(res => res.json())
  .then(data => {
    // data.phone 已经是 "138****1234"
    document.getElementById('phone').innerText = data.phone;
  });

这个方案的好处太多了:

  • 不管前端怎么缓存、怎么跳转,看到的都是脱敏数据
  • SSR、CSR、甚至小程序,都能统一处理
  • 安全责任明确:后端控制数据,前端只管展示

当然,也有小麻烦。比如有些页面需要“点击显示完整号码”(像支付宝那样),这时候就得额外开一个带权限校验的接口,专门返回完整信息。但我觉得这反而是好事——强制你做权限控制,而不是图省事把原始数据全扔给前端。

我踩过的一个坑是:后端脱敏规则不统一。有的接口返回 138****1234,有的返回 138******1234,前端还得再处理一遍。后来我们团队定了个规范:所有手机号脱敏格式统一为前三后四,中间四个星号。从此世界清净了。

方案三:前端动态脱敏(灵活但复杂)

有些场景下,后端不能/不愿改,只能靠前端动态脱敏。比如老系统改造,或者第三方 API 返回原始数据。这时候我会用“渲染时脱敏”的方式:

// Vue 组件示例
export default {
  computed: {
    maskedPhone() {
      return this.user.phone?.replace(/(d{3})d{4}(d{4})/, '$1****$2') || '';
    }
  }
}

或者 React 里:

const MaskedPhone = ({ phone }) => {
  const masked = phone?.replace(/(d{3})d{4}(d{4})/, '$1****$2') || '';
  return <span>{masked}</span>;
};

这种方式比纯前端脱敏强在哪?它确保每次渲染都重新计算,避免了缓存导致的数据残留。但依然有风险:如果某个地方忘了用这个组件,直接写了 {user.phone},那敏感信息就漏了。

所以我一般会配合 ESLint 规则,禁止直接访问原始字段。比如用自定义 rule 检查是否用了 user.phone 而不是 maskedPhone。不过说实话,这套流程搭起来挺费劲,小项目根本没必要。

谁更灵活?谁更省事?

如果让我选,90% 的情况我会选后端脱敏。虽然初期要推动后端配合,但长期来看维护成本最低,安全边界最清晰。前端不用操心“哪里漏了脱敏”,测试也更容易——只要检查接口返回就行。

纯前端脱敏?除非是内部工具或者数据真的不敏感(比如测试账号),否则我不会再碰。动态脱敏可以作为过渡方案,但一定要有配套的代码规范和检查机制,不然迟早出事。

还有一个细节:脱敏规则要和业务对齐。比如身份证号,有些场景只需要隐藏出生年月(110***********1234),有些要全隐藏(110**************)。后端统一处理的话,改规则只要动一个地方;前端处理就得全局搜索替换,容易遗漏。

我的选型逻辑

总结一下我的决策流程:

  1. 先问:这个数据真的需要在前端展示吗?能不能只展示图标或状态(比如“已绑定手机”)?能不展示就不展示。
  2. 如果必须展示,优先推动后端脱敏。哪怕多花两天时间沟通,也值得。
  3. 实在不行,才用前端动态脱敏,并且必须封装成通用组件,禁止直接使用原始字段。
  4. 绝对不用“一次性脱敏”(比如只在数据加载时处理一次)。

另外,别忘了 HTTPS!就算脱敏了,如果传输过程被窃听,攻击者还是能拿到原始请求。不过这是另一个话题了……

最后提醒:脱敏不是万能的

即使做了脱敏,也要注意其他泄露点。比如:

  • console.log 打印了原始数据(开发时常见,上线前记得删)
  • 错误日志上报了用户信息
  • 页面 meta 标签或 title 包含敏感内容

我就见过一个项目,手机号脱敏做得很好,但分享链接的 og:image 里包含了用户头像和昵称,结果被爬虫抓走了。所以说,安全是个系统工程,脱敏只是其中一环。

以上是我对敏感信息前端处理的对比总结,核心就一句话:能交给后端的,别自己扛。有不同看法欢迎评论区交流,比如你们是怎么处理“点击显示完整信息”这种需求的?

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

暂无评论