2011-11-21 121 views
11

我正在为一组网站设计API。这些站点非常相似(有点像StackOverflow,SuperUser和ServerFault),并且他们有一个共享的后端是合理的。因此,我们决定尝试使用一个很好的REST API作为后端,以及一些使用API​​的非常相似但不同的前端。前端应该最好全部是静态的,但如果事实证明这是不可能的,那么这并不是一个硬性要求。如何保护浏览器从CSRF攻击中使用的RESTful API?

我正在设计API,我担心安全问题,特别是CSRF。从我对CSRF攻击的基本理解来看,它们包含两个重要组件:

  1. 能够命名资源和请求正文。

  2. 欺骗用户/浏览器使用环境认证(如会话)向该资源寻求认证的请求。

很多修复CSRF攻击的经典方法都基于会话。由于我的REST API并没有真正执行会话,因此它们都会阻止很多向量,并且几乎可以解决所有这些问题。例如,双提交没有意义,因为没有什么可以提交。

我最初的方法是攻击CSRF攻击的第2部分。如果我对所有请求进行了身份验证(比如使用HTTP Basic Auth),并且浏览器不保存这些证书(例如,某些JS发出请求),那么只有拥有凭证的JS才能发出请求,并且我们完成了。明显的缺点是应用程序需要知道用户的凭据。另一个稍微不太明显的缺点是,如果我想在API端安全地存储凭据,那么验证密码应该花费固定的,不重要的时间量。如果安全地验证密码需要100ms,那么其他每个请求至少需要100ms + eps,并且这会让一些聪明的客户端欺骗手段使其不那么慢。我可能能够缓存这些信息(因为证书总是相同的),如果我非常小心,我可能会设法做到这一点,而不会引入定时漏洞,但这听起来像是一个黄蜂巢。

OAuth 2.0似乎有点过分了,但我想这可能是最好的解决方案,以免我最终实施得不好。我想我现在可以做HTTP基本身份验证的事情,当我们有第三方应用程序开发人员时,转到OAuth。

OAuth有一点阻抗不匹配。基本上,OAuth真的想帮助应用程序访问另一个应用程序的内容。我希望用户在这样一个帐户存在之前注册其中一个前端。

我也考虑过攻击点1,通过使URL随机化 - 即将标记添加到查询字符串。这肯定会起作用,它与表单中传统的随机化令牌非常接近,并且给予HATEOAS它应该甚至是相当RESTful,尽管这提出了两个问题:1)您从哪里开始?在使用HTTP Basic Auth进行登录时,是否有必要的API起始点? 2)如果他们不能事先预测URL,HATEOAS会被定罪吗?

我看过How to prevent CSRF in a RESTful application?,但我不同意这样一个前提,即随机化的URI必然是不可靠的。另外,这个问题并没有真正的答案,也没有提到OAuth。此外,会话双提交解决方案是无效的,正如我上面提到的(静态前端的不同域比API端点)。

我意识到我从根本上试图在这里做的是试图允许来自一个域的跨站点请求,并禁止从另一个域请求,这并不容易。当然必须有一些合理的解决方案?

+0

的问题,就像你说的,是要既防止和允许跨站点请求。问薛定谔。 –

+0

这并不意味着没有办法解决问题。我甚至提供了一个,尽管有点慢。 – lvh

回答

0

答案取决于你需要支持什么。如果我们假设您想要支持一个网页应用程序,其中使用 REST服务,并使用相同的REST服务用于与恰好是RESTFUL的网络应用程序不同的API(您可以决定“会话”是为了你!)。

现在(/我很累)我认为你已经建议在HTTP Basic Auth中使用javascript的方式是一个好的开始。

+0

HTTP基本身份验证容易受到CSRF的影响。 – rook

+0

你能不能详细说明一下?我意识到浏览器缓存HTTP基本身份验证,但我们并没有要求浏览器去做;我们建议让一些JS构建HTTP基本身份验证标头。因此证书永远不会在浏览器的环境auth池中结束,并且系统变得不受CSRF影响。正确? – lvh

3

CSRF令牌按照定义为“每用户状态”,因此不是RESTful。出于安全目的,大多数API都会打破其“每用户状态”要求,并且需要将CSRF令牌作为HTTP标头参数传递。

还有其他方式preventing CSRF。检查引用者并不像CSRF令牌那么强大,但它是RESTful的,并且不太可能被破坏。请记住,缺乏引用者应被视为失败的请求。

XSS可用于绕过基于令牌的CSRF预防和基于引用的CSRF预防。

+0

与我在回复您的评论时的评论相同。您能否详细说明为什么在使用JS添加身份验证标头时,HTTP基本身份验证不会阻止CSRF?另外,OAuth如何不阻止CSRF? OAuth的一次性令牌部分不能保证吗? – lvh

+0

@lvh用户名和密码是与请求一起发送的每用户状态。用作CSRF令牌的加密随机数也是每个用户状态随每个请求一起发送的,但它更安全,因为它应该是与会话相关的动态值。通常,RESTful协议依赖于静态密码nocne或“api密钥”,它是每个用户的状态并且也停止CSRF。在HTTP标头中发送它并不会让它变得安静,但如果浏览器发送的其他请求包含此值,则可能会打开攻击的大门。 – rook

+0

但这就是我的观点。如果它不成为环境浏览器授权,攻击媒介是什么?对于CSRF而言,它是一个随机数还是实际凭证并不重要:浏览器不知道*,因此如何欺骗它以提供它? – lvh

0

我有另一个潜在的解决方案,所以我列出它作为答案。请随意分开:)

auth.platform.com需要身份验证并设置cookie。如果auth.site.comauth.platform.com一个CNAME,将请求auth.site.com(在auth.platform.com分辨率之后结束了)可以设置cookie为site.com?这样我可以双重提交会话cookie。

当然,auth.platform.com将只有几个白名单域设置的Cookie。

编辑:当然,除了这不会在所有的工作,因为你不得不使用HTTPS安全地做认证和HTTPS会看到右边通过你的诡计。

0

设计你的API作为一个真正的RESTful,防止最常见的CSRF载体:使用GET为“安全”的操作

  • 避免饼干防止它们被“偷走”,
  • 防止imgiframes在没有用户交互的情况下触发不安全的操作。

然后,您应该实施CORS以让用户的浏览器阻止来自您不信任的来源的请求。

+1

-1除了关于CORS的评论外,这与CSRF没有任何关系。 CORS是危险的,可以用来读取CSRF令牌或其他敏感信息。 CORS不能用于防止CSRF。 – rook