用户系统设计准则

在开发和维护公司的一套系统两年后,对用户系统总结出了一点经验之谈

  • 不要存明文密码

    简单的md5,简单的sha1/sha256,固定的盐值,多层嵌套哈希,都不会提供额外的安全性
    参见:伯乐在线 - 蒋生武 加盐密码哈希:如何正确使用

  • HTTPS不是绝对安全

    如果你有额外的安全性需求,不要认为HTTPS是救世主

    1. 首次访问的HSTS剥离
    2. 证书泄露:网站的HTTPS证书泄露,导致中间人可以窃听流量
    3. 证书机构漏洞/错误颁发证书
    4. 本地病毒、流氓软件安装的证书
    5. 路由器去广告插件安装的证书
  • 不能仅凭登录态授权

    涉及到账户本身的操作,不能仅凭登录态来授权验证,例如“修改密码” “绑定/解绑手机”之类的操作,需要验证登陆密码或者二次验证,因为一旦用户流量被劫持,session或者token被盗取,黑客无法盗走账户,用户还可以通过更改密码取回自己的账号

  • 用户唯一性

    不要试图通过手机号,身份证号,或者其他号码之类的来归类一个“人”,因为一个人可以多个不同的这些东西

  • 不要创建多套用户系统

    1. 如果为“普通用户” “内部用户” “管理员” 分别建立用户系统的话,实际上很有可能他们都会做同一类操作。
      例如我们希望统计网站的短信通知发送情况(需要显示用户名和根据用户名查询),
      又或者我们想要建立一个推荐系统,
      再或者我们建立了一个多合一的新客户端,
      那么这时候你的代码中就需要处理3种ID和3种不同的数据结构,这真是一场噩梦

    2. 一点异议
      我们假设家中有一堆人民币和一堆黄金
      A: 我们希望将它们放在不同的地方以提供不同的安全级别,即使这样会增加维护难度
      B:我们可以为整个屋子提供一样的高安全级别,而不能奢求保险柜和木头柜子单独的安全性
      A:单独的保险柜是为了以防万一,万一在屋子不安全的情况下,保险柜还能提供一层额外的屏障
      B:事实不是这样,分两个数据表,使用两套代码处理两个用户系统,并不能提供额外的屏障,他们不是木头柜子和保险箱的区别,而是一个木头柜子和另一个木头柜子。
      A:那它们也是两个锁的型号不同的柜子,可以为破解伪造穷举钥匙增加时间难度
      B:这点细微的区别,并不值得为此增加系统复杂性,越干净的地方越容易保持干净,程序员更愿意去维护它,更方便看出可能的漏洞
      而如果是多套系统,一团乱码,不会有程序员愿意去趟浑水,因为复杂的系统中,轻微的变更也容易导致bug,因为你没法在脑子里掌握整个流程

    3. 按照是否同一实体来抉择的话,前台用户和后台管理员肯定不是同一实体,但是肯定又是同一实体
      例如,它们能操作的大多数功能都不重合,但是很有可能有些操作是重合的,例如后台管理员可以和前台版主一样能给帖子评分

      最后,我认为是否分开不是关键,两者都能实现所有的功能,并且在理论上都是行得通的,正确的。关键在于你是否真的知道你自己为什么这样做,以及你能搞定你选的这个方案

  • 按类型存放数据而不是按维度存放

    例如用户的提现银行卡信息,因为一人只有一张卡,我们就将银行卡信息的几个字段新增在用户表
    这样做的坏处显而易见

    1. 打开用户表查看时很混乱,无关字段太多
    2. 想要查看一类数据时无处可查
    3. 后来者很难维护,他必须从一堆字段中找出哪几个字段属于哪个实体
    4. 当两个实体都有同一个字段时会丢失数据,例如创建时间,那你就无法区分这个创建时间是用户的创建时间还是银行卡的创建时间了
  • 数据库字段加密

    1. 如果我们为了数据能全等查询而不使用动态盐值
      那么加密所带来的安全性微乎其微
      参见1:AES加密的安全问题
      参见2:程序员面试闪充–iOS密码学
      参见3:破解之美:利用ECB加密缺陷突破cookie加密

    2. 如果使用动态盐值,那么我们将会无法进行全等查询,或者我们可以记录部分明文用作查询,
      例如 130**1234,这是牺牲业务来满足技术,而且这个技术还没有提供完整的安全性(泄露了一部分)

    3. 使用动态盐值并且不提供查询
      那么你赢了,DBA现在完全不能窃取你的机密数据。
      而且程序员也恨死你了,因为甚至没办法判断一个手机号是否注册了多个账号

    4. 动态盐值和哈希值
      通过哈希值来查询,取出符合哈希值的记录行来解密后对比。
      需要注意你不能使用随机盐值制造哈希值,否则无法用于查询,
      可以添加一些固定盐值来抵御基于彩虹表的破解,例如数据的一部分加上应用程序特性的固定盐值
      (例如hmac(‘13800000000’ . md5(‘13800000000’) . $app_key))

    5. 加密密钥必须不能被数据库软件访问,
      你必须将应用程序服务器和数据库服务器保持在单独的硬件上才有意义。

    6. 如果在不同环境使用不同的密钥,那么将会出现一些问题,例如你将线上的数据导入本地来调试,那么将不能得到正确的结果,因为密钥不一致

    7. 如果我们认为数据库服务器和应用程序服务器分开能提供不同的安全级别,那么我们往深处想,我们将加密数据和数据库服务器分开呢?
      因为它们是两台一样的服务器,我们没有理由认为其中一台沦陷而另一台没有沦陷

    8. 别高兴太早,令人沮丧的是,代码运行环境仍然可以泄露你的机密数据,例如运行环境被入侵,或者后端代码中的漏洞。
      既然你认为DB和DBA靠不住,凭什么认为更复杂的代码和运行环境和程序员能靠得住呢?

参阅:数据库加密字段的模糊搜索_如何搜索安全加密的数据库字段

鄂ICP备14007840号-1