5.3 授权
在本节中我们将介绍授权(Authorization)控制(访问控制)。
5.3.1 什么是授权
授权指的是给认证为合法的用户分配相应的权限,下面是一些权限的例子。
- 只有认证用户才能使用的功能
销户处理、转账、创建新用户(以管理员身份)等
- 只有认证用户才能查看的信息
非公开的用户私人信息、非公开的他人的个人信息(以管理员身份)、非公开的论坛内容、WebMail 等
- 只有认证用户才能执行的修改操作
用户本人的信息修改(密码、个人简介、页面设置等)、修改他人的个人信息(以管理员身份)、从 WebMail 发送邮件等
授权系统存在漏洞的话,会导致个人信息泄漏、权限被恶意使用等众多安全上的问题。
5.3.2 典型的授权漏洞
在这一节我们介绍比较有代表性的不合理的授权实现案例。
更改资源 ID 后可以查看没有权限查看的信息
很多 URL 里面都包含表示特定资源的 ID(这里我们叫作资源 ID),如果权限管理做的不够充分的话,那么有可能只通过修改 URL 里的这个 ID 就能查看本来没有权限查看或者修改的数据。
我们下面以图 5-23 的例子进行说明。图 5-23 显示的是登录 ID 为 yamada 的用户在登录网站后,查看自己个人信息的页面流程。个人资料表示页面(图右侧)的 URL 里面包括了要查看的资源 ID“id=yamada”这一查询字符串。
图 5-23 用户确认个人资料的页面流程
如果权限控制实现存在漏洞的话,我们可能只需要把这个 URL 里的 id=yamada 换成别人的用户 ID,那么就有可能查看本来无权查看的其他人的个人信息了。比如把 id 换为 id=sato 的话,就能查看 sato 的个人资料了。如图 5-24 所示。
图 5-24 通过修改资源 ID 查看别人的个人信息
这个例子里资源 ID 是放到 URL 里的,所以出现漏洞很容易理解。即使将资源 ID 通过 hidden 参数放到 POST 里,或者放到 Cookie 里,还是不能避免同样问题的。如果开发人员大意地认为 hidden 或者 Cookie 里的值不会被人修改的话,就可能因疏忽而导致发生类似的安全漏洞。
在这个例子里资源 ID 是用户 ID,在其他系统里它可能会是交易 ID、文档 ID、邮件消息 ID 等。不管哪种系统,都可能存在风险,只通过修改资源 ID 就能查看、修改甚至删除等。
只控制菜单的显示或不显示
第二个权限管理的失败例子是只做菜单显示和不显示上的控制。图 5-25 是这样的一个例子。图里显示的是一个作为管理员登录的页面跳转关系。
在这个例子里,如图 5-25 所示,在顶级菜单有指向管理员和普通用户的链接。
图 5-25 管理员登录时的页面流程
下面再来看看面向普通用户的登录流程,如图 5-26 所示。普通用户的顶级页面里只显示了一个指向普通用户功能的链接,但是如果从普通用户页面的 URL 里的 a001.php 推测管理员的网址,并在浏览器里尝试访问 b001.php 的话,就会看到管理员的功能了,也许还能够使用里面的功能。
图 5-26 普通用户通过修改 URL 访问管理员功能
在这个例子中,如果用户想越过自己的权限使用其他功能的话,需要知道其 URL。得到 URL 的具体方法有以下几种。
- 根据规则按顺序尝试 URL 中的字母或者数字(如图 5-26 所示)
- 尝试 admin 或者 root、manage 等管理员功能菜单里频繁使用的词汇
- 在曾经拥有权限的时候记住 URL 地址,在失去权限后利用记住的 URL 访问管理员功能
拿最后的例子来说,即使把管理菜单的 URL 做得很难被推测,也还是存在被恶意使用的可能性的。
使用 hidden 参数或者 Cookie 保存权限信息
授权漏洞的第三种类型就是使用 hidden 参数或者 Cookie 来保存权限信息的情况。比如通过设置一个 userkind=admin 的 Cookie 就能使用户能够使用管理员功能这样的网站。
在这种情况下,该 Cookie 很容易让人推测出管理员的 Cookie 值,即使使用数字作为用户类型,也同样存在被恶意使用的可能性。
授权漏洞总结
在本小节我们对授权中存在的 3 种类型的漏洞进行了说明。这些漏洞的共同问题在于如果对 URL、hidden 参数、Cookie 进行篡改的话,就可以非法使用网站的正常功能了。
要想正确实现授权功能,需要将权限信息保存到会话变量中去,这样攻击者就不能篡改权限信息了。并且在进行页面显示或者处理之前,还需要对用户权限进行检查。
专栏:将私密信息嵌入 URL 进行授权处理
也有不通过认证或会话管理技术来实现授权的方法,那就是在 URL 中嵌入一些私密信息,使只有知道这个 URL 的人才能访问。
将私密信息嵌入到 URL 中,有下面 3 种方法。
- 将 URL 中的文件名设为非常长的难以推测的随机字符串
- 在 URL 中嵌入一个令牌
- 在 URL 中嵌入访问票(Access Ticket)
但不管是哪种实现方式,在 URL 中嵌入私密信息的做法我们都是不推荐的。因为这违反了采用 POST 方法发送私密信息的原则(请参考 3.1 节),而且还存在下面列出的一些非常现实的风险。此外,它还会导致个人信息泄露。
- 通过 Referer 泄露 URL
- 用户自己将 URL 发布到论坛等地方将 URL 公开
- 搜索引擎可能会收录含有私密信息的 URL
因此原则上应该禁止采用在 URL 中嵌入私密信息的方式来进行授权,如果迫不得已不得不使用该方式的话,那么应该将 URL 的可访问时限限制在最短范围内,并且向用户强调公开此 URL 的危险性。
5.3.3 授权管理的需求设计
要想正确实现授权功能,必须先要在需求层次上进行必要的设计。笔者在检查漏洞工作中参加过很多次关于授权的审查,但是还从来没有看到过有谁能拿出书面的授权设计文档,来说明他们认为的“授权应该是怎样的”。更多情况下他们是潜意识里认为授权管理就是应该那么去做的。
在做权限管理的设计时,可以先创建一个权限矩阵表。下面以一个权限管理比较复杂的应用场景 ASP(Application Service Provider)为例,来看一下如何创建权限矩阵表,如图 5-27 所示。例子中的应用以 ASP 的形式,被公司 A、公司 B、公司 C 所使用。用户中除了整个系统的管理员以外,各个公司还有自己内部的企业管理者用来管理自己公司的员工等。
图 5-27 ASP 服务的例子
表 5-5 权限矩阵的例子
| 系统管理员 | 企业管理者 | 普通用户 |
---|---|---|---|
添加公司 | ○ | × | × |
添加、删除企业管理者 | ○ | × | × |
添加、删除公司员工用户 | ○ | ○ | × |
修改自己密码 | ○ | ○ | ○ |
修改别人密码 | ○ | ○(只限本公司用户) | × |
如果我们在设计的时候创建了这样的权限矩阵,就能正确地进行后面的开发和测试。
专栏:什么是角色
表 5-5 里的“系统管理员”“企业管理者”“普通用户”一般我们管他们叫作角色(Role)。角色是指拥有一组权限,并用能表现其职责的词汇来命名的称呼。表 5-5 同时很好地解释了角色定义的问题。
角色和用户是不同的概念,用户以某一角色在系统中进行各种活动。
笔者不推荐在实践中不使用角色,而只使用类似 admin 或者 root 这样一看就是管理员用户的方法,原因有如下两点。
- 如果有多个管理员的话,事后调查会比较困难
- 管理员密码被多人公用,容易发生安全事故
所以,应该以一人一个 ID 的原则为每个用户创建 ID,并根据各人的职责不同分配不同的角色。
5.3.4 如何正确实现授权管理
用户授权出现漏洞的时候,很多原因都是只想在页面显示中进行权限控制,这是不充分的。正确的方法是在任何操作之前都应该进行如下检查。
- 用户是否可以访问该页面(脚本)
- 是否有操作(查看、修改、删除等)该资源的权限
用户信息应该保存在会话变量里,以防止被人篡改。这不光是安全授权的要求,也是保存用户认证信息的原则。
- 根据保存在会话里的用户 ID 检查权限
- 权限信息不能保存在 Cookie 或者 hidden 参数里
5.3.5 总结
在本节中我们介绍了在实现授权管理中容易发生的安全性问题,以及如何正确地去设计授权系统。
我们也介绍了,容易导致授权管理漏洞发生的原因,其一就是由于开发者认为存放在 URL 或者表单的 hidden 参数、Cookie 等内容不会被篡改。正确的实现方法是将这些关键信息都保存在不能被随意篡改的会话里,并在需要指定权限才能进行的操作之前进行权限检查。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论