使用HTTP基本身份验证代替TLS客户端证书认证

32
下面的答案来自于这个问题: 此处
获奖答案实际上根本没有回答问题,它只是提到了数据传输的SSL,而没有真正涵盖认证的内容。您真正想问的是如何安全地验证REST API客户端。除非您使用TLS客户端身份验证,否则仅使用SSL不是REST API的可行身份验证机制。没有客户端身份验证的SSL仅对服务器进行身份验证,这对大多数REST API来说无关紧要。如果您不使用TLS客户端身份验证,则需要使用类似于基于摘要的身份验证方案(例如Amazon Web Service的自定义方案)或OAuth甚至是HTTP基本认证(但仅限于SSL)。
所以考虑到我将使用没有客户端证书的HTTPS,我的问题是:如果我们不使用客户端SSL认证,服务器实际上并不知道正在与谁交流。我理解在这里,如果我使用身份验证令牌访问以对客户端进行身份验证,则服务器不知道即使该令牌与服务器数据库中的用户ID匹配。首先,
1-这是一个真正的问题吗?特别是使用Https时?
2-最重要的是,假设这是一个重要的安全漏洞;Http基本身份验证如何在这里提供帮助,正如作者所述? Http基本身份验证只会在头部发送编码的用户名密码。因此,当客户端收到一个令牌(发送用户名密码后返回),然后在其余请求中使用此标记将其放入此头部,一切都突然变得很好?服务器仍然不知道请求来自哪里,也许服务器具有与其数据库中匹配的有效令牌用户,但未知真正发送它的人。(尽管我仍然认为这非常难以通过https窃取令牌并由其他人使用!)无论何时我提到这个主题,我都会得到回复:“嗯……你发送了一个令牌,但服务器并不知道是谁发送的,不太安全”。因此,我的理解是浏览器保留了某种身份验证证书,并且服务器知道请求来自正确的位置,然后我才能确定与该令牌配对的用户(从我的数据库中检查)是“真正正确”的。或者,我在这里说的可能不正确。
3个回答

78

这位发布者说如果我们不使用客户端SSL认证,服务器就不知道自己正在与谁通信。

这不是我说的 :) 我说的是:

除非您使用TLS客户端身份验证,否则仅靠SSL不是REST API的可行身份验证机制。

alone 是关键词。另外:

如果您不使用TLS客户端身份验证,则需要使用某种摘要基于身份验证方案(例如Amazon Web Service的自定义方案)或OAuth甚至只在SSL上启用HTTP Basic身份验证。

换句话说,TLS客户端身份验证是身份验证REST API客户端的方法之一。由于最初的SO问题是关于SSL的,因此我提到,如果您只依赖TLS,那么TLS客户端身份验证是唯一的“内置”身份验证形式。因此,如果您使用TLS,而不利用TLS客户端身份验证,则必须使用其他形式的身份验证来对客户端进行身份验证。

有很多方法可以对REST客户端进行身份验证。 TLS客户端身份验证只是其中之一(TLS和通常非常安全的唯一“内置”方式)。但是,TLS是一个网络级协议,并且大多数人认为对于许多最终用户来说过于复杂。因此,大多数REST API提供选择使用更易于使用的应用级协议,如HTTP,因为它对大多数人来说更容易使用(例如只需设置一个HTTP头)。

因此,如果您要走HTTP头路线,则必须使用头值来对REST客户端进行身份验证。

在HTTP身份验证中,您有一个名为Authorization的头和其值(该头名称相当不幸,因为它通常用于身份验证而不是访问控制,也称为授权)。 Authorization头值是服务器用于执行身份验证的内容,通常由三个标记组成:

  1. HTTP身份验证方案名称,后跟
  2. 空格(几乎总是一个空格字符),然后是
  3. 方案特定的文本值。

一种常见的HTTP身份验证方案是Basic方案,它非常... 嗯... 基础 :). 方案特定的文本值仅是以下计算出的值:

String concatenated = username + ":" + raw_password;
String schemeSpecificTextValue = base_64_encode(concatenated.toCharArray());

所以你可能会看到对应的标题是这样的:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

服务器知道如何解析这个值。它说:“嘿,我知道Basic 认证方案,所以我将获取尾部文本值,进行base64 解码,然后就可以获得用户名和密码。然后我可以看看这些值是否与我存储的相匹配。”

这基本上就是Basic认证。由于这种方案特别包括提交的原始密码进行base64编码,因此如果不使用TLS连接,它被认为不安全除非。TLS (Transport Layer Security) 保证(大多数情况下)窥探者无法拦截报头(例如通过数据包检查)并查看密码信息。这就是为什么您应该始终在HTTP Basic认证中使用TLS。即使在公司内部网络环境中也是如此。

当然还有其他更安全的HTTP身份验证方案。一个例子是任何使用基于摘要的身份验证的方案。

基于摘要的身份验证方案更好,因为它们的方案文本值不包含提交的密码。相反,计算特定数据的基于密码的哈希值(通常是其他标头字段和值),并将结果放入Authorization标头值中。服务器使用存储在本地的密码计算相同的基于密码的哈希值。如果服务器计算出的值与请求的标头值匹配,则可以考虑请求已得到身份验证。

这种技术之所以更安全,是因为仅传输哈希 - 而不是原始密码本身。这意味着即使在明文(非TLS)连接上也可以使用此技术进行请求身份验证(当然,只有在请求数据本身不敏感时才应这样做)。

一些基于摘要的身份验证方案:

与OAuth 1.0a相比,像Amazon和其他类似的方案对REST来说更加安全,因为它们始终对整个请求进行身份验证,包括请求实体负载(即HTTP头之后的所有内容)。OAuth 1.0a仅适用于application/x-www-form-urlencoded内容,而这不适用于使用application/xmlapplication/json负载的REST API(这是大多数REST API这些天)。

有趣的是,OAuth2 不是基于摘要的 - 它使用我认为不太安全的“承载令牌”(在许多场景中它们是可以的,但仍然不如银行、军事和政府通信中使用的摘要方案好)。


谈到“服务器知道如何解析该值”,它会说:“嘿,我知道基本方案,所以我将获取文本值,进行base64解码,然后就可以得到用户名和提交的密码。接着,我可以查看这些值是否与我存储的匹配。” - Spring
目前我的代码正在解析HTTP头部,而不是Web服务器。 - Spring
使用摘要,我以为必须与客户端使用“共享密钥”,这会使事情变得复杂,但我发现可以使用用户密码作为密钥,因此不需要额外的工作。正确吗? - Spring
2
在Spring中,您可以使用用户密码作为摘要认证的密钥,但通常不建议这样做:如果用户更改了密码,则其REST客户端将立即中断!最好仅为UI登录设置密码,然后使用REST API密钥(id + secret)进行REST登录(例如,Stormpath就是这样做的)。希望对您有所帮助! - Les Hazlewood
@LesHazlewood,您能否为我推荐一些关于该主题及类似主题的书籍? - Gilgamesz

10

当我们谈论“验证用户”时,实际上是指“检查用户是否知道其他人不应该知道的信息”。这种“信息”可能是密码、证书、硬件安全令牌,甚至是用户的视网膜模式,但在所有情况下,我们真正检查的是对该信息的访问权限,而不是用户的身份(无论这究竟意味着什么)。

关键是,原则上,所有这些验证器都可能被盗取并用于冒充用户。密码可以被记录,证书可以从存储它的硬盘中复制,硬件令牌可以被盗取(可能还会被反向工程和克隆),甚至用户的视网膜模式也可以被扫描、记录和伪造。在每种情况下,我们能做的最好就是尽可能地使这变得“非常困难”。


6
也许我误解了问题。
你引用的答案告诉我,如果你不使用某种形式的身份验证,无论是客户端证书、HTTP BASICAUTH还是其他什么,服务器都不知道它正在与谁通信。
也许对于你的应用程序来说这没问题,也许不是,只有你自己能回答这个问题。
换句话说,如果你使用了某种形式的身份验证,服务器就知道它正在与哪个“人”通信;它正在与经过身份验证的凭据所属者通信。
在这种情况下,身份验证是通过某些凭据建立身份的过程。
身份验证并不能保证凭据没有被盗。SSL确保(我不敢说它“保证”)凭据在客户端和服务器之间的传输是安全的。
当你使用GMail时,你正在使用SSL,那么Google怎么知道它正在与你通信?

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