How Cookie-Based Authentication Works in the Takes Framework

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

当您在Facebook登录页面输入电子邮件和密码时,您就可以进入您的账户。然后,无论您在网站上去哪里,您总是会在页面的右上角看到您的照片。Facebook会记住您,不会再次反复要求密码。这要归功于HTTP cookies,被称为基于cookie的身份验证。尽管这种机制经常引起一些安全问题,但它非常受欢迎和简单。下面是Takes如何在几行代码中实现这一点。

首先,让我们看看它是如何工作的。此外,让我们看看我认为它应该如何工作。

第一步:用户输入电子邮件和密码,然后点击“提交”。服务器接收到带有此信息的POST请求:

服务器会将提供的信息与其记录进行匹配,并决定要执行的操作。如果信息无效,服务器会返回相同的登录页面,并要求您重新输入。如果信息有效,服务器会返回类似以下内容的信息:

由于响应状态码为303,浏览器会跳转到Location头中指定的页面,并打开网站的首页。以下是它发送给服务器的内容:

服务器从“Cookie”头部中获取我的电子邮件,并理解这是我!无需再次要求密码。服务器相信cookie中的信息。就是这样。这就是基于cookie的身份验证的全部内容。

对了,那安全性呢?如果服务器相信带有用户电子邮件的“Cookie”头的任何浏览器请求,任何人都可以从其他地方发送我的电子邮件并访问我的账户。

防止这种情况发生的第一步是使用一个只有服务器知道的秘密加密密钥来加密电子邮件。除了服务器本身之外,没有人能够以与服务器需要解密的方式进行加密。响应将如下所示,使用XOR密码的“bamboo”作为秘密密钥的加密示例:

这不是最好的加密机制,对于合适的加密,最好使用一些更强大的机制,比如DES

这听起来都很不错,但是如果有人劫持了服务器和浏览器之间的通信,并获取到一个正确加密的电子邮件cookie,会怎么样呢?在这种情况下,窃贼即使不知道其内容也能使用同样的cookie进行身份验证。服务器会相信这些信息并允许该人进入我的账户。这种类型的攻击被称为中间人攻击(MITM)。为了防止这种情况发生,我们应该使用HTTPS,并告知浏览器该cookie是敏感的,不应该在没有SSL加密的情况下返回给服务器。这通过Set-Cookie头部的一个额外标志来实现:

还有一种与基于cookie的身份验证相关的攻击类型,它基于浏览器将与网页关联的所有cookie暴露给其中执行的JavaScript的能力。攻击者可以将一些恶意的JavaScript代码注入到页面中(别问我如何…只有在整个HTML渲染错误时才会发生),该代码将获得对cookie的访问权限。然后,该代码将把cookie发送到其他地方以便攻击者收集。这种类型的攻击称为跨站脚本攻击(XSS)。为了防止这种情况,Set-Cookie头部还有另一个标志称为HttpOnly

这个标志的存在会告诉浏览器,这个特定的cookie只能通过HTTP请求传回服务器。JavaScript无法访问它。

这是Takes框架中设计的基于cookie的身份验证机制。整个框架由takes组成,它们接收请求并生成响应(本文将详细解释该框架)。当请求到达时,我们应该在Cookie头中找到身份验证cookie,并将其转换为用户凭据。当响应发送出去时,我们应该在其中添加Set-Cookie头,其中包含加密的用户凭据。就是这样,只有这两个步骤。

假设我们有一个账户页面,应该显示当前用户的余额:

request进来后,我们应该检索用户的身份,该身份被编码在一个认证cookie中。为了使这个机制可重用,我们有一个TkAuth装饰器,它包装了一个现有的take,解码传入的cookie,并在请求中添加一个新的TkAuth头,其中包含用户的身份信息。

再次,当TkAuth接收到一个包含身份验证cookie的请求时,它会要求pass解码该cookie,并返回一个有效的IdentityIdentity.ANONYMOUS

然后,当响应返回到浏览器时,TkAuth会要求pass将身份编码回一个字符串,并将Set-Cookie添加到响应中。

PsCookie使用一个Codec的实例来执行这些反向和正向编码操作。

当我们的TkAccount take想要从请求中检索当前经过身份验证的用户身份时,它可以使用RqAuth,这是一个Request的实用修饰器。

RqAuth 装饰器使用由 PsCookie 添加的标题来验证用户并创建一个 Identity 对象。

这个机制确实非常可扩展和“可组合”。假设我们想在集成测试期间跳过身份验证。以下是方法:

PsChain 实现 Pass 并尝试通过逐个询问封装的 Pass 来对用户进行身份验证。链中的第一个是 PsFake。它在构造函数中使用一个布尔参数来决定是返回虚假身份还是什么都不返回。只需一个布尔触发器,我们就可以关闭应用程序中的整个身份验证机制。

假设您希望通过 Facebook OAuth 对用户进行身份验证。以下是如何进行的:

当用户在您的网站上点击登录链接时,浏览器会转到 facebook.com,在那里验证他或她的身份。然后,Facebook返回一个 302 重定向响应,并设置一个 Location 头,其值为我们在登录链接中提供的URL。链接必须包含类似于这样的内容:?PsByFlag=PsFacebook。这将告诉 PsByFlag 此请求用于验证用户身份。

以下是我们如何实现注销机制:

现在,我们可以在网站的任何链接上添加?PsByFlag=PsLogout,它将注销当前用户。

您可以通过查看Rultor中的TkAppAuth类,来了解所有这些是如何在实际应用中运作的。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-27 at 04:45

sixnines availability badge   GitHub stars