19

ASP.NET Cookie是怎么生成的

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA%3D%3D&%3Bmid=2654077430&%3Bidx=3&%3Bsn=b172c41f3afcf0d56c7371ccd15ca0a5
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

可能有人知道 Cookie 的生成由  machineKey 有关,  machineKey 用于决定  Cookie 生成的算法和密钥,并如果使用多台服务器做负载均衡时,必须指定一致的  machineKey 用于解密,那么这个过程到底是怎样的呢?

如果需要在 .NETCore 中使用  ASP.NETCookie ,本文将提到的内容也将是一些必经之路。

抽丝剥茧,一步一步分析

首先用户通过 AccountController->Login 进行登录:

它调用了 SignInManager 的  PasswordSignInAsync 方法,该方法代码如下(有删减):

想浏览原始代码,可参见官方的 Github 链接:https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Owin/SignInManager.cs#L235-L276

可见它先需要验证密码,密码验证正确后,它调用了 SignInOrTwoFactor 方法,该方法代码如下:

该代码只是判断了是否需要做双重验证,在需要双重验证的情况下,它调用了 AuthenticationManager 的  SignIn 方法;否则调用  SignInAsync 方法。  SignInAsync 的源代码如下:

可见,最终所有的代码都是调用了 AuthenticationManager.SignIn 方法,所以该方法是创建  Cookie 的关键。

AuthenticationManager 的实现定义在  Microsoft.Owin 中,因此无法在  ASP.NETIdentity 中找到其源代码,因此我们打开  Microsoft.Owin 的源代码继续跟踪(有删减):

AuthenticationManager 的  Github 链接如下:https://github.com/aspnet/AspNetKatana/blob/c33569969e79afd9fb4ec2d6bdff877e376821b2/src/Microsoft.Owin/Security/AuthenticationManager.cs

可见它用到了 AuthenticationResponseGrant ,继续跟踪可以看到它实际是一个属性:

发现它其实是设置了 SignInEntry ,继续追踪:

其中, _context 的类型为  IOwinContext ,  OwinConstants.Security.SignIn 的常量值为  "security.SignIn"

跟踪完毕……

啥?跟踪这么久,居然跟丢啦!?

当然没有!但接下来就需要一定的技巧了。

原来, ASP.NET 是一种中间件(  Middleware )模型,在这个例子中,它会先处理  MVC 中间件,该中间件处理流程到设置  AuthenticationResponseGrantSignInEntry 为止。但接下来会继续执行  CookieAuthentication 中间件,该中间件的核心代码在  aspnet/AspNetKatana 仓库中可以看到,关键类是  CookieAuthenticationHandler ,核心代码如下:

这个原始函数有超过 200 行代码,这里我省略了较多,但保留了关键、核心部分,想查阅原始代码可以移步  Github 链接:https://github.com/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin.Security.Cookies/CookieAuthenticationHandler.cs#L130-L313

这里挑几点最重要的讲。

MVC 建立关系

建立关系的核心代码就是第一行,它从上文中提到的位置取回了 AuthenticationResponseGrant ,该  Grant 保存了  Claims 、  AuthenticationTicket 等  Cookie 重要组成部分:

继续查阅 LookupSignIn 源代码,可看到,它就是从上文中的  AuthenticationManager 中取回了  AuthenticationResponseGrant (有删减):

如此一来,柳暗花明又一村,所有的线索就立即又明朗了。

Cookie的生成

AuthenticationTicket 变成  Cookie 字节串,最关键的一步在这里:

在接下来的代码中,只提到使用 CookieManager 将该  Cookie 字节串添加到  Http 响应中,翻阅  CookieManager 可以看到如下代码:

有兴趣的朋友可以访问 Github 看原始版本的代码:https://github.com/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin/Infrastructure/ChunkingCookieManager.cs#L125-L215

可见这个实现比较……简单,就是往 Response.Headers 中加了个头,重点只要看  TicketDataFormat.Protect 方法即可。

逐渐明朗

该方法源代码如下:

可见它依赖于 _serializer 、  _protector 、  _encoder 三个类,其中,  _serializer 的关键代码如下:

其本质是进行了一次二进制序列化,并紧接着进行了 gzip 压缩,确保  Cookie 大小不要失去控制(因为  .NET 的二进制序列化结果较大,并且微软喜欢搞  xml ,更大:joy:)。

然后来看一下 _encoder 源代码:

可见就是进行了一次简单的 base64-url 编码,注意该编码把  = 号删掉了,所以在  base64-url 解码时,需要补  = 号。

这两个都比较简单,稍复杂的是 _protector ,它的类型是  IDataProtector

IDataProtector

它在 CookieAuthenticationMiddleware 中进行了初始化,创建代码和参数如下:

注意它传了三个参数,第一个参数是 CookieAuthenticationMiddleware 的  FullName ,也就是  "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware" ,第二个参数如果没定义,默认值是  CookieAuthenticationDefaults.AuthenticationType ,该值为定义为  "Cookies"

但是,在默认创建的 ASP.NET MVC 模板项目中,该值被重新定义为  ASP.NETIdentity 的默认值,即  "ApplicationCookie" ,需要注意。

然后来看看 CreateDataProtector 的源码:

可见它先从 IAppBuilder 的  "security.DataProtectionProvider" 属性中取一个  IDataProtectionProvider ,否则使用  DpapiDataProtectionProvider

我们翻阅代码,在 OwinAppContext 中可以看到,该值被指定为  MachineKeyDataProtectionProvider

文中的 Constants.SecurityDataProtectionProvider ,刚好就被定义为  "security.DataProtectionProvider"

我们翻阅 MachineKeyDataProtector 的源代码,刚好看到它依赖于  MachineKey

最终到了我们的老朋友 MachineKey

逆推过程,破解Cookie

首先总结一下这个过程,对一个请求在 Mvc 中的流程来说,这些代码集中在  ASP.NETIdentity 中,它会经过:

  1. AccountController

  2. SignInManager

  3. AuthenticationManager

  4. 设置  AuthenticationResponseGrant

然后进入 CookieAuthentication 的流程,这些代码集中在  Owin 中,它会经过:

  1. CookieAuthenticationMiddleware (读取  AuthenticationResponseGrant

  2. ISecureDataFormat (实现类:  SecureDataFormat<T>

  3. IDataSerializer (实现类:  TicketSerializer

  4. IDataProtector (实现类:  MachineKeyDataProtector

  5. ITextEncoder (实现类:  Base64UrlTextEncoder

这些过程,结果上文中找到的所有参数的值,我总结出的“祖传破解代码”如下:

运行前请设置好 app.configweb.config 中的  machineKey 节点,并安装  NuGet 包:  Microsoft.Owin.Security ,运行结果如下(完美破解): 

zEb6zuu.jpg!web

学习方式有很多种,其中看代码是我个人非常喜欢的一种方式,并非所有代码都会一马平川。像这个例子可能还需要有一定 ASP.NET 知识背景。

注意这个“祖传代码”是基于 .NETFramework ,由于其用到了  MachineKey ,因此无法在  .NETCore 中运行。我稍后将继续深入聊聊  MachineKey 这个类,看它底层代码是如何工作的,然后最终得以在  .NETCore 中直接破解  ASP.NETIdentity 中的  Cookie ,敬请期待!

RrMRbub.jpg!web最后,在新的一年里,祝大家阖家欢乐,鼠年大吉!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK