ASP.NET Cookie是怎么生成的
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
中间件,该中间件处理流程到设置 AuthenticationResponseGrant
/ SignInEntry
为止。但接下来会继续执行 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
中,它会经过:
-
AccountController
-
SignInManager
-
AuthenticationManager
-
设置
AuthenticationResponseGrant
然后进入 CookieAuthentication
的流程,这些代码集中在 Owin
中,它会经过:
-
CookieAuthenticationMiddleware
(读取AuthenticationResponseGrant
) -
ISecureDataFormat
(实现类:SecureDataFormat<T>
) -
IDataSerializer
(实现类:TicketSerializer
) -
IDataProtector
(实现类:MachineKeyDataProtector
) -
ITextEncoder
(实现类:Base64UrlTextEncoder
)
这些过程,结果上文中找到的所有参数的值,我总结出的“祖传破解代码”如下:
运行前请设置好 app.config
/ web.config
中的 machineKey
节点,并安装 NuGet
包: Microsoft.Owin.Security
,运行结果如下(完美破解):
学习方式有很多种,其中看代码是我个人非常喜欢的一种方式,并非所有代码都会一马平川。像这个例子可能还需要有一定 ASP.NET
知识背景。
注意这个“祖传代码”是基于 .NETFramework
,由于其用到了 MachineKey
,因此无法在 .NETCore
中运行。我稍后将继续深入聊聊 MachineKey
这个类,看它底层代码是如何工作的,然后最终得以在 .NETCore
中直接破解 ASP.NETIdentity
中的 Cookie
,敬请期待!
最后,在新的一年里,祝大家阖家欢乐,鼠年大吉!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK