生成加密安全的身份验证令牌

Posted

技术标签:

【中文标题】生成加密安全的身份验证令牌【英文标题】:Generating cryptographically secure authentication tokens 【发布时间】:2010-10-24 20:12:27 【问题描述】:

背景:

这确实是一个普遍的最佳实践问题,但有关特定情况的一些背景知识可能会有所帮助:

我们正在为 iPhone 开发一个“连接”应用程序。它将通过 REST 服务与后端应用程序通信。为了不必每次启动应用程序时都提示用户输入用户名和密码,我们将公开一个“登录”服务,该服务在初始启动时验证用户名和密码并返回可用于未来 Web 的身份验证令牌对真实数据的服务请求。令牌可能有一个过期时间,之后我们会要求他们使用他们的用户名/密码重新进行身份验证。

问题:

生成此类用于身份验证的令牌的最佳做法是什么?

例如,我们可以...

散列(SHA-256 等)随机字符串,并将其与到期日期一起存储在给定用户的数据库中。对后续请求中的令牌进行简单查找以确保其匹配。 使用密钥加密用户 ID 和一些附加信息(时间戳等)。在后续请求中解密令牌以确保它是由我们颁发的。

感觉这一定是一个已解决的问题。

【问题讨论】:

【参考方案1】:

根据对这个问题的其他答案的反馈、额外的研究和线下讨论,我们最终做了以下事情......

很快就有人指出,这里的交互模型与选中“记住我”复选框时 ASP.NET 中的表单身份验证使用的模型基本完全相同。它不是发出 HTTP 请求的 Web 浏览器。我们的“票证”等同于 Forms Authentication 设置的 cookie。默认情况下,表单身份验证本质上使用“使用密钥加密某些数据”的方法。

在我们的登录 Web 服务中,我们使用此代码创建票证:

string[] userData = new string[4];

// fill the userData array with the information we need for subsequent requests
userData[0] = ...; // data we need
userData[1] = ...; // other data, etc

// create a Forms Auth ticket with the username and the user data. 
FormsAuthenticationTicket formsTicket = new FormsAuthenticationTicket(
    1,
    username,
    DateTime.Now,
    DateTime.Now.AddMinutes(DefaultTimeout),
    true,
    string.Join(UserDataDelimiter, userData)
    );

// encrypt the ticket
string encryptedTicket = FormsAuthentication.Encrypt(formsTicket);

然后,我们为 WCF 服务添加了一个操作行为属性,该属性添加了一个 IParameterInspector,用于检查请求的 HTTP 标头中的有效票证。开发者将这个操作行为属性放在需要认证的操作上。以下是该代码解析票证的方式:

// get the Forms Auth ticket object back from the encrypted Ticket
FormsAuthenticationTicket formsTicket = FormsAuthentication.Decrypt(encryptedTicket);

// split the user data back apart
string[] userData = formsTicket.UserData.Split(new string[]  UserDataDelimiter , StringSplitOptions.None);

// verify that the username in the ticket matches the username that was sent with the request
if (formsTicket.Name == expectedUsername)

    // ticket is valid
    ...

【讨论】:

您如何找到这种方法?我可能是错的,但是,我认为它有一点缺陷。客户端请求被授权唯一需要的是票证可以在服务器上解密。如果票证在传输过程中被截获(例如数据包嗅探),甚至从移动设备中提取,攻击者可能会重新使用它来发送恶意请求。如果您使用 SSL,那么第一次攻击会得到缓解,但是,第二次攻击仍然可行。 我们对这种方法还是很满意的。我们确实使用 SSL,所以嗅探问题不是问题。从移动设备中提取的票证要求移动设备受到攻击和/或物理上落入攻击者手中。到那时,攻击者已经“赢了”,游戏结束了。无论技术细节如何,如果有“记住我的密码”功能,情况总是如此。在我们的案例中,由于票证只能使用 8 小时,因此攻击者使用被盗票证的时间窗口有限。 "使用服务器 Machine.config 的 配置元素对票证进行加密和 签名" - 我一定略过参与文档!干杯 如果我错了,请纠正我,但基本上任何基于令牌的安全方案都容易受到中间人的攻击和重放请求或捕获令牌并将其用于其他请求。令牌的优点是它的生命周期通常是有限的,并且不直接引用用户。但它仍然可以被捕获和滥用...... @RickStrahl,当然,我们的令牌仅用于 TLS / HTTPS 连接,因此除非攻击者破坏了实际的移动设备,否则无法拦截(在这种情况下,所有希望都可能失去只需记录击键以获取用户密码)。【参考方案2】:

构建自己的身份验证系统始终是“最糟糕的做法”。这种事情最好留给专门研究身份验证系统的专业人士。

如果您一心想要构建自己的“来自登录服务的过期票据”架构,而不是重复使用现有架构,那么至少熟悉推动类似系统设计的问题可能是个好主意,如 Kerberos。这里有一个温和的介绍:

http://web.mit.edu/kerberos/dialogue.html

看看过去 20 年来在 Kerberos(和类似系统)中发现了哪些安全漏洞并确保不会复制它们也是一个好主意。 Kerberos 是由安全专家构建并经过数十年的仔细审查,仍然存在严重的算法缺陷,例如:

http://web.mit.edu/kerberos/www/advisories/MITKRB5-SA-2003-004-krb4.txt

从他们的错误中吸取教训比从自己的错误中学习要好得多。

【讨论】:

如果有一个预先存在的身份验证系统可以在 iPhone 应用程序和 REST WCF Web 服务之间工作,请发布一个链接,我很乐意使用它而不是发明我们自己的...... ,我一定会看看你提供的 kerberos 链接。 请指导最佳实践,因为 RESTful API 登录正是我现在正在研究的内容 =) OP 显然是在寻求最佳实践,而不是试图建立自己的。 【参考方案3】:

Amazon.com 使用HMAC SHA-1 message token 对请求进行身份验证和授权。他们将其用于相当大的商业服务,因此我有责任相信他们的工程决策。 Google 发布的OpenSocial API 有点相似。根据 Google 和 Amazon.com 使用类似且公开发布的方法来保护 Web 请求,我怀疑这些可能是不错的方法。

【讨论】:

【参考方案4】:

您提供的两个答案中的任何一个都足够了。您可能会找到可以为您执行此操作的框架,但事实是构建起来并不难。 (我工作过的每家公司都推出了自己的产品。)数据库存储令牌与加密数据“cookies”的选择是一个架构决策——你想在每个页面视图上进行数据库查找,还是你更愿意用cookie解密来咀嚼CPU?在大多数应用程序中,使用加密的 cookie 可以大规模提高性能(如果这是一个问题)。否则只是口味问题。

【讨论】:

【参考方案5】:

由于您使用的是 WCF,因此如果使用 CFNetwork,您有多种选择——例如 NTLM 或摘要式身份验证:

http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/Concepts/Concepts.html#//apple_ref/doc/uid/TP30001132-CH4-SW7

我知道这不能回答您的具体问题,但我也遇到过这个问题(iPhone - Tomcat),并决定尽可能使用 Web 服务器上的身份验证服务。在大多数情况下,将身份验证信息包含在每个请求中不会造成重大损失。快速 Google 会找到很多关于 WCF 和 RESTful 服务的博文(以及 *** 上的一些相关问题)。

希望这会有所帮助!

【讨论】:

【参考方案6】:

你应该实现:

    OAuth2 隐式授权 - 用于第三方应用程序https://www.rfc-editor.org/rfc/rfc6749#section-1.3.2 OAuth2 资源所有者密码凭证 — 用于您自己的移动应用程序 https://www.rfc-editor.org/rfc/rfc6749#section-1.3.3

这正是您正在寻找的来自 OAuth2 的工作流程。不要重新发明***。

【讨论】:

【参考方案7】:

这听起来就像一个过期时间很长的会话标识符。在 Web 应用程序中使用的相同原则也适用于此。

会话标识符不是编码信息,而是从一个非常大的空间(128 位)中随机选择的。服务器保留将会话标识符与用户和其他所需信息(例如到期时间)相关联的记录。客户端通过安全通道为每个请求提供会话标识符。

安全性依赖于会话标识符的不可预测性。使用加密 RNG 从非常大的空间生成它们。

【讨论】:

以上是关于生成加密安全的身份验证令牌的主要内容,如果未能解决你的问题,请参考以下文章

基于令牌的身份验证的安全性

使用其他提供商身份验证服务(如 Soundcloud)时令牌身份验证的安全性?

Azure CDN URL使用令牌身份验证和Blob存储SAS重写规则

Laravel 通过生成的令牌进行身份验证,无需护照和 jwt

使用访问令牌和刷新令牌保持身份验证

带有烧瓶安全扩展的基于令牌的身份验证