REST身份验证方案的安全性

235

背景:

我正在设计一个REST web服务的认证方案。这并不是“真正”需要安全保障的(它更像是一个个人项目),但我希望尽可能地使它安全,作为一种锻炼/学习经验。我不想使用SSL,因为我不想麻烦,而且主要是为了避免设置它的费用。

以下Stack Overflow问题对我开发起到了很大帮助:

我考虑使用Amazon S3的身份验证的简化版本(我喜欢OAuth,但它对我的需求来说似乎太复杂了)。我将在请求中添加由服务器提供的随机生成的nonce,以防止重放攻击。

接下来是问题:

Amazon S3和OAuth都依赖于签署请求URL以及几个选定的标头。对于POST或PUT请求,它们都不会对请求正文进行签名。这不会容易受到中间人攻击的影响吗?中间人可以保留URL和标头,并替换请求体为攻击者想要的任何数据。

看起来我可以通过在被签名的字符串中包括请求体的哈希值来防范这种攻击。这是安全的吗?


6
Amazon S3可以在头部字符串中包含Content-MD5来防止您描述的中间人攻击。 - laz
8
MD5是一种非常弱的哈希函数,使用已被多年不鼓励:http://en.wikipedia.org/wiki/MD5。现在使用SHA2。 MD5就像是一只面临身份危机的猪涂了口红。 - Henrik
6
Startcom 提供免费的 SSL 证书,这些证书在主流浏览器中不会出现证书警告。 - Plato
5
@SeanKAnderson(抱怨:我发现人们谈论99.99999%时,会觉得荒谬,因为情报机构正在自动化进行大量攻击,这种方式处理真正的问题非常奇怪。--“嗯,不会有问题;我的祖母也无法入侵它”。) - Henrik
2
@Plato 我建议现在使用LetsEncrypt免费SSL证书。 - shuttle87
显示剩余4条评论
6个回答

173

之前的答案只提到了SSL在数据传输上下文中,没有涉及认证。

您实际上是在询问如何安全地验证REST API客户端。除非您使用TLS客户端身份验证,否则仅使用SSL 无法成为REST API的可行认证机制。仅使用客户端身份验证的SSL仅对服务器进行身份验证,对于大多数REST API来说是无关紧要的,因为您实际上需要对客户端进行身份验证。

如果您不使用TLS客户端身份验证,则需要使用类似基于摘要的身份验证方案(例如Amazon Web Service的自定义方案)或OAuth 1.0a甚至HTTP基本身份验证(但仅限于SSL)。

这些方案可以验证请求是否由预期的人发送。 TLS(SSL)(不使用客户端身份验证)确保通过线路发送的数据未被篡改。它们是独立但互补的课题。

对于那些感兴趣的人,我已经扩展了一个关于HTTP认证方案及其工作原理的Stack Overflow问题。


16
没错。就你的 API 来说,SSL 只验证了它所处理的请求没有在传输过程中被篡改。API 仍然不知道是谁在访问它,也不知道他们是否有权访问。 - Tim Gautier
1
好的回答。我还建议查看这些优秀的资源.. https://www.owasp.org/index.php/Web_Service_Security_Cheat_Sheet 和 https://www.owasp.org/index.php/REST_Security_Cheat_Sheet (DRAFT) - dodgy_coder
2
只是一个小细节,但使用SSL还有额外的好处,可以防止窃听和中间人攻击。 - dodgy_coder
@Les Hazlewood,我在一个问题中提出了这个问题;谢谢 https://dev59.com/n2Yq5IYBdhLWcg3w-VZl - Spring
@Les Hazlewood 如果您也能看一下这个链接就好了 https://dev59.com/b2zXa4cB1Zd3GeqPUYNW - Spring
显示剩余4条评论

60

REST代表使用web标准,而在web上“安全”传输的标准是SSL。其他任何方式都会有些奇怪,并需要客户端进行额外的部署工作,以便有加密库可用。

一旦您确认使用SSL,原则上身份验证并不需要太复杂。您可以再次遵循Web标准并使用HTTP基本认证(用户名和令牌随每个请求一起发送),因为它比精心设计的签名协议要简单得多,在安全连接的上下文中仍然有效。您只需要确保密码永远不会以明文形式传输;如果在明文连接上接收到密码,则甚至可以禁用密码并将其发送给开发人员。您还应确保在接到凭据后不会将其记录在任何地方,就像您不会记录常规密码一样。

HTTP摘要是更安全的方法,因为它防止了传递秘密令牌;相反,它是一个哈希值,服务器可以在另一端验证。如果您已经采取了上述预防措施,则可能对于较不敏感的应用程序来说过于复杂了。毕竟,用户登录时已经以明文形式传输了密码(除非您在浏览器中使用了一些高级JavaScript加密),同样,他们的每个请求中的cookie也是如此。

请注意,在API中,最好让客户端传递令牌 - 随机生成的字符串 - 而不是开发人员登录网站时使用的密码。因此,开发人员应该能够登录到您的网站并生成新令牌,用于API验证。

使用令牌的主要原因是如果令牌被泄漏可以替换,而如果密码被泄漏,则所有者可能会登录到开发人员的帐户并做任何他们想做的事情。令牌的另一个优点是您可以向同一开发人员颁发多个令牌,也许这是因为他们有多个应用程序或者因为他们需要不同访问级别的令牌。

(更新以涵盖仅使用SSL连接的影响。)


3
你可以花大约30美元一年的价格购买GoDaddy的SSL证书。我很震惊地发现Verisign的SSL证书价格如此之高(如果我没记错的话,一年要600美元左右?)。但是,选择GoDaddy也是完全可行的。 - Brian Armstrong
19
除非您正在使用SSL/TLS双向认证,并且用户/客户端使用的证书由服务器信任,否则您尚未将用户身份验证到服务器/应用程序。您需要进行更多操作以将用户身份验证到服务器/应用程序。 - Pauld
5
Ryan说:如今SSL加密所需的处理能力相比于使用Web应用程序框架(例如Django或Rails)生成响应所需的处理能力要小得多。 - Cameron Walsh
4
起始之星的证书是免费的并且被广泛认可。cacert.org 是一个开放的替代品,但认可度较低。 - Dima Tisnek
17
这并没有回答问题,问题是关于认证的。 - Sam Stainsby
显示剩余4条评论

8

或者你可以使用已知的解决方案并使用SSL。自签名证书是免费的,而且这是一个个人项目,对吧?


2
自签名证书是免费的,但据我所知,您仍然需要一个静态IP。 - dF.
8
除了某些商业付费证书的许可要求之外,没有必须拥有静态IP的要求。 - Chris Marisic
如果您在客户端和服务器两端都控制证书存储,则这可能是一个可行的选择,但是证书管理和分发在生产环境中可能比开发环境复杂得多。在承诺之前,请确保了解此替代方案的复杂性。 - aled

5
如果您需要在URL中作为参数传递正文的哈希,并且该URL是通过私钥签名的,那么中间人攻击将只能替换正文,以生成相同的哈希。现在使用MD5哈希值很容易实现,当SHA-1被攻破时,情况更加不妙。
要保护正文免受篡改,您需要对正文进行签名,这样中间人攻击就不太可能破解,因为他们不知道生成签名的私钥。

9
找到与有效内容生成相同的MD5哈希值的字符串可能比预期要容易得多,但是创建一个恶意版本的有效内容并使其散列为相同的值仍然非常困难。这就是为什么MD5不再用于密码哈希,但仍用于验证下载的原因。 - Tim Gautier

3

1

请记住,您的建议会使客户与服务器通信变得困难。他们需要理解您的创新解决方案并相应地加密数据,这种模型对于公共API来说不是很好(除非您是亚马逊\雅虎\谷歌..)。

无论如何,如果您必须加密正文内容,我建议您查看现有的标准和解决方案,例如:

XML加密(W3C标准)

XML安全


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接