【问题标题】:Spring Security - Secure Remote Password protocol - SRP - Authentication ProviderSpring Security - 安全远程密码协议 - SRP - 身份验证提供程序
【发布时间】:2014-12-15 04:44:04
【问题描述】:

当问这个问题时,我正在寻找有关实施我自己的 AuthenticationProvider 的指导。我的意思是:

到目前为止,我已经了解到 Spring Security 会询问 AuthenticationProvider 对象是否用户已通过身份验证。目前我正在使用 DaoAuthenticationProvider 处理我自己的客户 UserDetailService 返回的用户名和密码。一切都很好! Spring 支持许多不同的 AuthenticationProvider,例如有一个用于 LDAP、Jdbc、DAO(如上所述),我什至能够找到一个用于 Kerberos。但是 SRP 没有身份验证提供程序,因此需要编写一个。

我的问题如下:

当我们使用 DaoAuthenticationProvider 即用户/密码身份验证时,我们有一个 html 表单,其中输入了用户名和密码,然后一个按钮负责提交这两个参数。购买的参数通过某个传输通道传输到服务器,即只需单击一下,我们就可以在同一个 http 请求中发送所有数据,即身份验证所需的所有数据。这可以在 UsernamePasswordAuthenticationFilter 中看到。这里“attemptAuthentication”方法采用“HttpServletRequest request”,其中包括用户名和密码。到现在一切都好。

 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        ...
        }

好吧,在简单的 SRP 中,我们还有一个包含用户名和密码的表单,除了密码!!!必须!!!不能通过网络传输。为了实现该约束,必须在客户端和服务器之间进行“讨论”,即必须交换以下参数。

1) 用户名​​ (I),

2) 一个名为“A”的值

3) 一个名为“B”的值

4) 一个名为“M1”的值

5) 一个名为“M2”的值

好吧,让我们假设有一个名为“SrpAuthenticationProcessingFilter”的过滤器,如果新的身份验证协议更像是来自 RFC 5054 的对话,那么过滤器应该是什么样子。

   Client                                            Server

   Client Hello (I)        -------->
                                               Server Hello
                                               Certificate*
                                        Server Key Exchange (N, g, s, B)
                           <--------      Server Hello Done
   Client Key Exchange (A) -------->
   [Change cipher spec]
   Finished                -------->
                                       [Change cipher spec]
                           <--------               Finished

   Application Data        <------->       Application Data

这里有一个客户需要

  • a) 先发送他的用户名 (I)
  • b) 那么服务器需要响应一个值 B。(在这种情况下 N,g,s 并不是真正需要的)
  • c) 客户端发送它的“值 A”
  • d) 客户端使用来自服务器的值 B 并根据该值计算密钥。
  • e) 服务器也根据值 A 计算密钥。
  • f) 客户端向服务器发送值 M1。
  • g) 服务器获取 M1 值并基于以 M1 值作为参数的公式,他能够验证密钥是否 匹配,如果购买方的计算密钥匹配,则 用户经过身份验证,产品即共享密钥可以 可进一步用于其他处理。

与用户名和密码验证相比,这些是 7 个步骤而不是一个步骤。其中 3 个需要在 SrpAuthenticationProcessingFilter 之前发生。现在我知道有可能将用户名与“值 A”一起发送,从而缩短步骤数,但我想严格遵守 RFC。永远不要走简单的路对吗?

问题实际上是我应该将负责客户端和服务器之间的乒乓(对话)的代码放在哪里,即上面提到的前 3 个步骤 a、b 和 c。它应该将它们放在 SrpEntryPoint 对象中还是其他地方。 if else 那么是在 SpringSecurity 的上下文中吗?

我可以解决这个问题的一种方法是使用 websockets,但我也想让该身份验证独立于任何第 5-7 层协议,例如 websockets、http、spdy 等。这意味着第一个实现应该是通过简单的 http 请求/响应,然后使用任何其他协议。

因此,目前实施 SRP 的正确结构是:

  • SRPAuthenticationEntryPoint 实现 org.springframework.security.web.AuthenticationEntryPoint - 这基本上说明了如果对受保护资源的请求进入但用户尚未经过身份验证应该做什么。在这里,我们可以在资源尚未经过身份验证的情况下进行重定向。也许这也是负责步骤 a、b、c 的地方,不确定这是否是正确的地方。请求指导和信息!

  • SrpAuthenticationProcessingFilter 扩展了 GenericFilterBean。 SrpAuthenticationProcessingFilter 的存在是为了进行部分验证,例如检查 srp 参数是否接收到正确的并对应于服务器设置的 srp 参数。在这里重要的是要提到 SrpAuthenticationProcessingFilter 没有进行任何用户名验证,即需要在调用 SrpAuthenticationProcessingFilter 之前的一个步骤中发生,可能是 SrpEntryPoint 或我不知道如何调用它的其他步骤。 SrpAuthenticationProcessingFilter 有一个方法“doFilter”,其中正在创建第二个结构,即 SrpAuthenticationToken。

  • SrpAuthenticationToken 扩展 org.springframework.security.authentication.AbstractAuthenticationToken。在我的理解中,该令牌与 DAO 对象类似,该令牌映射成功身份验证所需的所有字段。当部分验证的参数被填充到 SrpAuthenticationToken 中时,SrpAuthenticationToken 被传递给 org.springframework.security.authentication.AuthenticationManager 接口的authenticat方法,即类似的东西

    myAuthentication = authenticationManager.authenticate(SrpAuthenticationToken);

取决于在 Spring Security 配置中配置了哪个身份验证提供程序,然后在我们的例子中调用 SrpAuthentication 提供程序,即:

@Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth
          .authenticationProvider(sRPAuthenticationProvider);
}
  • SRPAuthenticationProvider - 实现 org.springframework.security.authentication.AuthenticationProvider。在这里,步骤 d、e、f 和 g 正在被验证和比较。如果发生错误,则抛出新的 BadCredentialsException("Invalid username/password/Verifiere") 异常。

  • SrpSessionValidator - 这个只负责验证 Srp 会话的特定参数,并且会从 SrpAuthenticationProcessingFilter 以及在 SrpAuthenticationProcessingFilter 之前的一步中调用,以验证用户名是否存在于数据库中。

我只知道如何实现 Srp 身份验证,因此我想要一些 cmets,如果这完全有意义并且 SRPAuthenticationEntryPoint 是步骤 a、b 和 c 的正确位置。我觉得这不是正确的地方。

非常感谢任何反馈。

问候,

铁托

Addition1(2014 年 11 月 21 日)-> 作为对“在哪里放置负责客户端和服务器之间的 ping pong(对话)的代码,即前 3 个步骤 a、b 和 c”的问题的回答答案很可能是一个标准(我称之为协商)过滤器,它将承担这项工作。

现在我想重新表述这个问题,即在身份验证完成之前以及在收到 M1 和 M2 消息之前,即步骤 1、2 和 3。我应该把那个对象放在哪里? IE。它应该是对象应该存在的地方,例如 60 秒,然后在没有收到 M1 和 M2 消息的情况下自动删除。我的意思是“SecurityContextHolder”对象之前的某个对象。我只是不知道与弹簧安全相关的对象/上下文的名称是什么,我也不知道这种结构是否存在?

【问题讨论】:

  • 我知道这已经很长时间了,但是您的实施的当前状态是什么?你能完成吗?你有我可以查看的存储库吗?

标签: authentication spring-security authorization srp-protocol


【解决方案1】:

https://bitbucket.org/simon_massey/thinbus-srp-spring-demo 有一个 Spring 安全 SRP 实施

自述文件页面显示:

集成的一个显着特点是没有更改 除了 to 之外的标准 spring 安全认证路径 配置自定义SrpAuthenticationProvider

它还说:

Spring Security 不会将用户 HTTPSession 暴露给 身份验证提供者。所以演示代码使用带有超时的 Guava 缓存 持有 SRP6JavascriptServerSession 的短暂时间 登录交换。从 Thinbus 1.2.1 开始,会话是可序列化的,因此 大型无国籍网站的替代方法是持有 数据库中的 SRP6JavascriptServerSession 对象而不是 内存缓存。

【讨论】:

    【解决方案2】:

    我的方法是使用 AJAX 运行协议的第一部分,直到在客户端创建 AM1,然后将它们作为登录凭据发布到服务器并使用 Spring Security 进行检查。

    要了解它是如何工作的,有一个 junit-js 测试,它使用 ThinbusTestSRP6JavascriptClientSessionSHA256.js 上运行 javascript 客户端对象和 java 服务器对象之间的相互身份验证(请注意,maven 构建使用 JDK 运行此单元测试javascript 运行时):

        // note the following exchange is written in javascript calling client js and invoking server java which is run by JUnit-JS using the JDK javascript runtime so it shows both sides of the full authentication in one unit test method
    
        // normal login flow step1a client: browser starts with username and password given by user at the browser
        client.step1(username,password);
    
        // normal login flow step1b server: server starts with username from browser plus salt and verifier saved to database on user registration. 
        var B = server.step1(username, salt, v);
    
        // normal login flow step2a client: server sends users salt from user registration and the server ephemeral number
        var credentials = client.step2(salt, B);
    
        // normal login flow step2b server: client sends its client ephemeral number and proof of a shared session key derived from both ephermal numbers and the password
        var M2 = server.step2(credentials.A, credentials.M1);
    
        // normal login flow step3 client: client verifies that the server shows proof of the shared session key which demonstrates that it knows actual verifier
        client.step3(M2);
    

    显然,javascript 客户端仅以 usernamepassword 开头。服务器使用username解析salt并生成随机B。客户端从服务器获得saltB,并生成其随机的AM1,这是密码证明。以M1 为参数的服务器step2 是检查用户密码证明的服务器,如果证明不正确,它将抛出异常。然后服务器发送M2,这是服务器证明它有用户验证器v,这样做是为了防止假服务器欺骗真实服务器。

    有一个浏览器演示通过 AJAX 使用 Thinbusthinbus-srp-js-demo 上对 Java 服务器执行 SRP。您可以使用 AJAX 方法(例如 Spring MVC AJAX)重新使用 JAX RS 来执行在客户端创建 A+M1 的第一步,然后使用登录表单发布这些到 Spring Security 并拥有Spring Security 通过在服务器对象上运行 step2 来验证 A+M1,如 junit-js 测试所示。然后您的AuthenticationManager 可以从由用户名键入的并发映射中解析由 AJAX 创建的服务器对象。

    一个小提示是,如果您使用 HTTPS 连接到服务器,我会考虑检查服务器证明 M2 是可选的。如果您不使用 HTTPS,那么服务器欺骗意味着他们可以为用户提供一个发送密码并忽略错误 M2 的页面;因此 M2 证明不提供网页上下文中的安全性。使用电话间隙之类的东西将 html/js 打包到本机应用程序中的移动应用程序将受益于 M2 检查;可以在用户登录后添加到页面中,由可信代码检查。

    【讨论】:

    • 西蒙,谢谢你的帖子。 Thinbus 是一个很好的测试示例,可以让客户端的生活更轻松,但是它存在一个问题。随机性的来源,即小“a”和小“b”。即那些一次性的临时密钥。这些并不像 Thinbus 中所要求的那样随机。这就是我决定使用另一个 java 脚本 srp 实现的原因。尽管如此,我同意你对最后一段的建议。
    • 因此,这些检查在任何实现中都必须是强制性的,仅此而已。我的问题更多的是关于 SRP 的 spring 实现(尚不存在),即关于客户端和服务器之间的“乒乓(对话),即前 3 个步骤 a、b 和 c”,但与服务器端有关不在客户端。不过我相信我们将有机会在其他时间与您讨论这个问题:)。
    • 我想如果你再读一遍 SRP 论文你会发现 M2 和 M1 是不同的:所以服务器会说一些客户端可以验证的东西。如果服务器说了同样的话,客户端说它不会向客户端证明任何事情。假设 s、A、B 共享,服务器可以证明它知道 V,客户端可以证明它知道密码。
    • 你是完全正确的,我犯了一个错误。我需要在这里写的是,需要验证密钥“K”是否与购买方的密钥相同。我删除上面的消息是为了避免混淆其他用户,即正确的版本是 K(alice)必须等于 K(bob),即 Ka == Kb。该验证通过 M1 和 M2 消息交换完成。谢谢你的收获。
    • 尽管如此,我仍然坚信验证服务器证明的需要,即 M2 在任何实现中都必须是强制性的。
    猜你喜欢
    • 2011-02-25
    • 2012-02-18
    • 2010-10-12
    • 2012-01-15
    • 1970-01-01
    • 2015-04-10
    • 2019-07-16
    • 2012-03-07
    • 2011-02-09
    相关资源
    最近更新 更多