代码之家  ›  专栏  ›  技术社区  ›  Erv Walter

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

  •  55
  • Erv Walter  · 技术社区  · 15 年前

    背景:

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

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

    问题:

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

    例如,我们可以…

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

    这似乎是一个已解决的问题。

    7 回复  |  直到 10 年前
        1
  •  29
  •   Erv Walter    15 年前

    基于对这个问题的其他答案的反馈、额外的研究和离线讨论,下面是我们最终要做的……

    很快就指出,这里的交互模型本质上与选中“记住我”复选框时ASP.NET中表单身份验证使用的模型完全相同。它不仅仅是一个Web浏览器发出HTTP请求。我们的“票据”与形成认证集的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
        ...
    }
    
        2
  •  12
  •   Eric Lippert    15 年前

    构建自己的认证系统始终是“最糟糕的做法”。对于专门从事认证系统的专业人员来说,这是最好的选择。

    如果您致力于构建自己的“登录服务到期票据”体系结构,而不是使用现有体系结构,那么至少熟悉驱动类似系统(如kerberos)设计的问题可能是个好主意。这里有一个温和的介绍:

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

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

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

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

        3
  •  11
  •   Alex Reynolds    15 年前

    Amazon.com使用 HMAC SHA-1 message token 验证和授权请求。他们把它用于相当大的商业服务,所以我有责任相信他们的工程决策。谷歌发布 OpenSocial API 有点相似。基于Google和Amazon.com使用类似的公开发布的方法来保护Web请求,我怀疑这可能是很好的方法。

        4
  •  3
  •   Neil Mix    15 年前

    你提供的两个答案中的任何一个都足够了。你可能会发现那些为你做这件事的框架,但事实是,它并不难构建。(我工作过的每个公司都有自己的)选择数据库存储令牌与加密数据“cookies”是一个体系结构决策——您希望在每个页面视图上进行数据库查找,还是希望通过cookie解密来咀嚼CPU?在大多数应用程序中,使用加密的cookie可以在规模上获得性能胜利(如果这是一个问题的话)。否则,这只是一个品味问题。

        5
  •  1
  •   sehugg    15 年前

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

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

    我知道这并不能回答你的具体问题,但我也面临着这个问题(iPhone-Tomcat),我决定尽可能多地使用Web服务器上的认证服务。在大多数情况下,将身份验证信息包含在每个请求中没有明显的惩罚。一个快速的谷歌会出现很多关于WCF和RESTful服务的博客文章(以及有关stackoverflow的一些相关问题)。

    希望这有帮助!

        6
  •  1
  •   irakli    10 年前

    您应该实现:

    1. OAuth2隐式授权-用于第三方应用程序 http://tools.ietf.org/html/rfc6749#section-1.3.2
    2. OAuth2资源所有者密码凭证用于您自己的移动应用程序 http://tools.ietf.org/html/rfc6749#section-1.3.3

    这正是您要查找的OAuth2的工作流。不要重新发明轮子。

        7
  •  0
  •   erickson    15 年前

    这听起来就像是一个具有很长过期时间的会话标识符。在Web应用程序中也可以应用相同的原则。

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

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