Identity Server4 基础应用(四)Hybrid Flow与PKCE

前言

前文介绍了Authorization Code flow的基本内容,可以看出其拥有不错的安全机制。但是仍然存在局限,如果客户端是运行在服务器上的Web应用程序(这类客户端称为机密客户端)当然是个不错授权模式,因为很多涉及安全隐患的步骤(如AccessToken)都是通过后端通道由web服务器和授权服务器直接通信的,而不需要经过用户的浏览器或者其他的地方。虽然如此,但是Authorization Code仍然是通过前端通道传递的,如果code被泄露,就仍然存在安全隐患,典型的有Cut and pasted code attack,就是一种盗用code的攻击来获取用户的权限。因此,官方更加建议使用“Hybrid Flow”或“PKCE”从而增加安全性,但是官方更加推荐使用PKCE。

Hybrid Flow

先介绍一下ID_Token具体是什么,ID_Token就像我们的身份证或者户口本,客户端程序可以通过ID_Token获取用户的ClaimsID_Token由三个部分(头Header,体Body和签名Signature)组成并且是通过JWT形式呈现。ID_Token中包含的主要内容有issIssuing authority)、subA unique identifier for the end-user issued by the issuer)、audexpiatauth_time等。

Hybrid Flow在流程上大致是和Authorization Code flow一样的,唯一的区别是在完成用户的身份认证之后,通过authorization endpoint返回的数据不同。而这一步返回的数据是根据我们发送请求时的response_type参数决定的,这使得流程更加灵活。

Hybrid Flow根据response_type的不同,authorization endpoint返回可以分为三种情况。

  1. response_type = code + id_token ,即包含Authorization Codeidentity Code
  2. response_type = code + token ,即包含Authorization CodeAccess Code
  3. response_type = code + id_token + token,即包含Authorization Codeidentity CodeAccess Token

可以看到,code都是一定会返回的。如果ID_Tokenauthorization endpointtoken endpoint都被返回了,那么

  • iss和sub的值必须是相同的
  • 所有关于身份认证的Claims在两者中应该都包含
  • 但是authorization endpoint返回的Claim数量应该会少一点,这也是出于对隐私的考虑

如果Access Tokenauthorization endpointtoken endpoint都被返回了,那么

  • 那么两次值可能相同也可能不同
  • 这都是取决于这两个终结点的安全特性

那么到底为什么要使用hybrid flow呢?让我们的应用程序在前端通道和后端通道都可以接收到分开的token接下来进行一些简单的实践,有了前面AuthorizationCode模式代码的经验,编写Hybrid Flow也是很简单的。

授权服务器上新增HybridClient

在之前代码的基础上,新增一个授权方式是Hybridclient,这样就ok了。

创建一个Hybrid流程的MVC客户端

创建一个新的ASP.NET Core MVC程序,取名为“MVC_Hybrid”,使用Nuget添加IdentityModelOpenIdConnect的引用。

Startup.cs中进行配置,代码和Authorization Code Flow的基本一致,只是我们在配置ResponseType时需要使用Hybrid定义的三种情况之一,具体代码如下。

随后在Controller中,和前面介绍Authorization Code时一样,我们通过扩展方法在授权过程中获取的几个token,并显示到界面上

 1 public async Task<IActionResult> Index()
 2         {
 3             //我们利用拓展方法获取存下来的Access Token
 4             var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
 5             ViewBag.idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);
 6             var client = new HttpClient();
 7             //携带上AccessToken
 8             client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
 9             //去请求受保护的api1上的资源
10             var response = await client.GetAsync("http://localhost:5001/identity");
11             if (!response.IsSuccessStatusCode)
12             {
13                 ViewBag.Json = response.ReasonPhrase;
14             }
15             var content = await response.Content.ReadAsStringAsync();
16             ViewBag.Json = JArray.Parse(content).ToString();
17             return View();
18         }
Controller中Index
1 @{
2     ViewBag.Title = "Json";
3     Layout = "_Layout";
4 }
5 <p>@ViewBag.idToken</p>
6 <pre>@ViewBag.Json</pre>

以上,准备工作就已经完成了。接下来运行授权服务器和mvc客户端,运行成功后使用内置的用户登录,完成授权后进入获取到数据。

在整个过程中,我们使用Fiddler抓取请求,可以看到在Authorization Endpoint同时返回了codeid_token

使用https://jwt.io/解析Authorization EndPoint返回的Id_TokenToken EndPoint返回的id_Token,可以看到其中包含的用户信息都是一样的。

关于Hybrid Flow的其他两种模式也类似,我们只需要在客户端的修改请求时的ResponseType即可,就不再赘述了。

在使用Hybrid时我们看到授权终结点返回的Id Token中包含at_hashAccess Token的哈希值)和c_hashCode的哈希值),规范中定义了以下的一些检验规则。

  1. 两个id_token中的 iss 和 sub 必须相同。
  2. 如果任何一个 id token 中包含关于终端用户的声明,两个令牌中提供的值必须相同。
  3. 关于验证事件的声明必须都提供。
  4. at_hash 和 c_hash 声明可能会从 token 端点返回的令牌中忽略,即使从 authorize 端点返回的令牌中已经声明。

Proof Key for Code Exchange

在授权过程中,Authorization Code通过前端通道传递有被泄露的风险,在Hybrid FlowId_Token也在前端通道传递的同时也将用户数据暴露了出来。因此这里介绍的Proof Key for Code Exchange(PKCE) 就是用来降低威胁的一种方法。概括下PKCE参与在授权验证中的主要流程。

  1. 客户端在请求code前随机生成一段字符串,称为code_verifier
  2. code_verifier通过加密算法生成加密后的字符串,称为code_challenge
  3. 当客户端在向授权服务器的授权终结点请求code的同时发送code_challenge给授权服务器,同时为了告诉服务器我使用的是什么加密算法,将算法标识放在
  4. code_challenge_method中;
  5. 当客户端使用codeToken终结点请求AccessToken的同时会发送code_verifier
  6. 授权服务器使用相同的加密算法将code_verifier变换后与之前收到的code_challenge比对,比对成功才会发放Access Token

再画一个简图直观的看一下上面的流程。

 其实在第二篇文章中其实我们已经看到了这个方法的影子,在Fiddler查看授权请求时我们看到了code_challengcode_challenge_methodcode_verifier,这是因为我们在配置OpenIdConnect时对PKCE的支持是默认开启的,只不过当时我们没有在授权服务器端打开验证。

那么在授权服务器端我们如何开启使用PKCE验证呢,也很简单,在对client进行配置时,只需添加一句配置。

1 AllowedGrantTypes = GrantTypes.Code,
2 RequirePkce = true,  //开启PCKE

根据官方的说法,还是更加推荐使用PKCE。因为涉及到的加密过程都需要在客户端中实现,相比Hybrid模式实现PKCE的客户端就十分简洁,并且也在前端通道中增加其他的响应元素,最主要的还是因为在实现起来,PKCE更加简单,在ASP.NET Core3开始后已经增加了对PKCE的默认支持,只需简单的一句设置(RequirePkce = true)就能搞定了。

参考资料:

https://identityserver4.readthedocs.io/en/latest/topics/grant_types.html 

https://medium.com/identity-beyond-borders/openid-connect-hybrid-flow-1123bc9461fe 

https://tools.ietf.org/html/rfc7636 

https://tonyxu.io/zh/posts/2018/oauth2-pkce-flow/ 

 

posted @ 2020-03-20 12:38  冬瓜山总教练  阅读(1223)  评论(0编辑  收藏  举报