为什么数据库ID不能作为URL中的标识符
最近公司在进行网站的SEO优化,将所有主要页面的URL统一更改为新的格式,其中重要的一项改变是将所有URL的标识符统一为ID,例如过去我们的一个用户的公共页面URL是这样的
https://www.example.com/user/[:username]
而更新后的格式则变成
https://www.example.com/user/[:user_id]
在看到设计文档(Design Doc)的同时,我本能就对这个这样的URL形式产生了一丝疑虑,但又说不上为什么。
我们的用户信息是存在MySQL数据库的表单中,user_id是一个整数类型的主键(Primary Key),同时也是自增(Auto Increment)字段。
从安全角度来说,使用user_id来作为标识符的URL设计并没有什么问题,但是这样会有别的问题,什么样的问题呢?
暴露商业数据!
暴露的数据1:总体用户数量
有心人如果想要知道公司现在的总体用户数量,那么很简单,只需要注册一个新用户,然后查看自己的公共主页,这样新用户的user_id就会暴露出来,而这个user_id就约等于网站现在的总体用户数量(有一部分被删除的用户除外)
暴露的数据2:用户增速
如果说暴露总量还不是最危险的,用户增速则是更加重大的信息暴露。有心人知道URL中的user_id规律之后,可以通过做很简单的实验
1. 创建一个新用户,查看URL中的用户ID。 https://www.example.com/user/1000000
2. 隔一段时间之后(一周/月),再创建一个新用户,查看URL中的用户ID。https://www.example.com/user/1005000
二者相减(1005000-1000000)就可以轻易得到这段时间内的用户增长量。甚至可以在外部用很小的代价获得非常精确的用户增长曲线。
而这样的数据,对于大部分公司来说,都是非常核心的数据,尤其在公司未上市之前,应该尽量避免此类数据外流。
这样的情况十分普遍,因为在学习Web开发的初级教程中,几乎都会将数据库主键ID用来作为URL标识符,这样从URl解析主键到后台服务使用主键查询数据库,可以非常简洁直观。但是在实际的生产环境中,这样的做法往往并不可取。
那么有什么样的方法可以避免使用数据库ID呢?
1. 使用UUID —— UUID是乱序的字符串,所以无法用来预测数据,我们可以用多种方法来设计UUID。如果你使用了MongoDB等NoSQL的存储,那么恭喜你获得了天然的UUID,每一个ObjectID都是你可以使用的UUID。如果你的数据依然离不开关系数据库,那么可以考虑在数据库中扩增字段来生成UUID并存储,同时提供基于UUID的查询接口。
2. 加密自增ID —— 在系统已经到处使用自增ID作为内部查询的关键词的情况下,往往迁移到UUID会带来很大的工程开销,并且为了支持已有的URL访问,还需要维护两套不同的逻辑。在这种情况下一种相对简便的方法是在服务器端在生成URL的时候对于ID进行加密,这样在客户端看到的便是加密后的ID。当客户端进行请求的时候,服务器端再进行解密。这样的方法看似简单,其实其中也多需要注意的地方,比如加密的秘钥一旦泄露,便失去了意义。并且有在网页端动态生成URL的需求,也有不小的挑战。
- MySQL建立一个数据库中有多张表,每个表中都有id作为主键,为什么第一张表会报错?
- 为什么数据库有时候不能定位阻塞(Blocker)源头的SQL语句
- 真相大白:为什么Entity Framework不能进行跨数据库查询(附解决方法)
- 为什么在web.xml配置url-pattern要使用 *.jsp 而不能使用 /*.jsp
- VC/MFC中当使用了文件对话框后,就不能连接数据库了,为什么?
- 怎么解决重复提交的问题,即网络不好的时候,提交按钮点了一次,但是却发起了多次http url请求,数据库中写入了多条重复记录(当然ID是不重复的,因为毕竟是多条http url记录)
- 获得数据库表的最大id值。然后加1作为下一个id
- 原生js及jquery获取标签的几种方式——为什么id不能重复
- 关于自动编号的 id 作为数据库主键的问题
- 为什么Entity Framework不能进行跨数据库查询
- 为什么PowerDesigner注释不能在生成数据库过程生成
- 为什么I/O引脚不能作为ROM的芯片使能引脚????
- 数据库 id不能自增长。Filed id doesn't hava a default value
- i++为什么不能作为左值?
- 用CMP访问数据库,简单的,但不理解为什么URL不修改仍然可以运行
- 为什么phpmyadmin数据库中只能插入数字,不能插入中英文
- 为什么auto_ptr智能指针不能作为STL标准容器的元素
- 为什么C++成员函数不能作为C-Style回调函数?
- Python 为什么list不能作为字典的key?
- i++为什么不能作为左值?