国产精品chinese,色综合天天综合精品网国产在线,成午夜免费视频在线观看,清纯女学生被强行糟蹋小说

    <td id="ojr13"><tr id="ojr13"><label id="ojr13"></label></tr></td>
        • <source id="ojr13"></source>
            <td id="ojr13"><ins id="ojr13"><label id="ojr13"></label></ins></td>

            Article / 文章中心

            淺議ASP.NET Cookie的生成原理

            發(fā)布時(shí)間:2022-02-10 點(diǎn)擊數(shù):840
            簡介:  前言   可能有人知道 Cookie的生成由 machineKey有關(guān), machineKey用于決定 Cookie生成的算法和密鑰,并如果使用多臺服務(wù)器做負(fù)載均衡時(shí),必須指定一致的 machineKey用于解密,那么這個(gè)過程到底是怎樣的呢?   如果需要在 .NETCore中使用 ASP.NETCookie,本文將提到的內(nèi)容也將是一些必經(jīng)之路。   抽絲剝繭,一步一步分析   首先用戶通過 AccountController->Login進(jìn)行登錄:   //   // POST: /Account/Login   public async Task Login(LoginV

            前言

            可能有人知道 Cookie的生成由 machineKey有關(guān), machineKey用于決定 Cookie生成的算法和密鑰,并如果使用多臺服務(wù)器做負(fù)載均衡時(shí),必須指定一致的 machineKey用于解密,那么這個(gè)過程到底是怎樣的呢?

            如果需要在 .NETCore中使用 ASP.NETCookie,本文將提到的內(nèi)容也將是一些必經(jīng)之路。

            抽絲剝繭,一步一步分析

            首先用戶通過 AccountController->Login進(jìn)行登錄:

            //

            // POST: /Account/Login

            public async Task Login(LoginViewModel model, string returnUrl)

            {

            if (!ModelState.IsValid)

            {

            return View(model);

            }

            var result=await SignInManager.PasswordSignInAsync(model, model.Password, model.RememberMe, shouldLockout: false);

            switch (result)

            {

            case SignInStatus.Success:

            return RedirectToLocal(returnUrl);

            // ......省略其它代碼

            }

            }

            它調(diào)用了 SignInManager的 PasswordSignInAsync方法,該方法代碼如下(有刪減):

            public virtual async Task PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)

            {

            // ...省略其它代碼

            if (await UserManager.CheckPasswordAsync(user, password).WithCurrentCulture())

            {

            if (!await IsTwoFactorEnabled(user))

            {

            await UserManager.ResetAccessFailedCountAsync(user.Id).WithCurrentCulture();

            }

            return await SignInOrTwoFactor(user, isPersistent).WithCurrentCulture();

            }

            // ...省略其它代碼

            return SignInStatus.Failure;

            }

            想瀏覽原始代碼,可參見官方的 Github鏈接:

            github/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Owin/SignInManager.cs#L235-L276

            可見它先需要驗(yàn)證密碼,密碼驗(yàn)證正確后,它調(diào)用了 SignInOrTwoFactor方法,該方法代碼如下:

            private async Task SignInOrTwoFactor(TUser user, bool isPersistent)

            {

            var id=Convert.ToString(user.Id);

            if (await IsTwoFactorEnabled(user) && !await AuthenticationManager.TwoFactorBrowserRememberedAsync(id).WithCurrentCulture())

            {

            var identity=new ClaimsIdentity(DefaultAuthenticationTypes.TwoFactorCookie);

            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));

            AuthenticationManager.SignIn(identity);

            return SignInStatus.RequiresVerification;

            }

            await SignInAsync(user, isPersistent, false).WithCurrentCulture();

            return SignInStatus.Success;

            }

            該代碼只是判斷了是否需要做雙重驗(yàn)證,在需要雙重驗(yàn)證的情況下,它調(diào)用了 AuthenticationManager的 SignIn方法;否則調(diào)用 SignInAsync方法。 SignInAsync的源代碼如下:

            public virtual async Task SignInAsync(TUser user, bool isPersistent, bool rememberBrowser)

            {

            var userIdentity=await CreateUserIdentityAsync(user).WithCurrentCulture();

            // Clear any partial cookies from external or two factor partial sign ins

            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.TwoFactorCookie);

            if (rememberBrowser)

            {

            var rememberBrowserIdentity=AuthenticationManager.CreateTwoFactorRememberBrowserIdentity(ConvertIdToString(user.Id));

            AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent=isPersistent }, userIdentity, rememberBrowserIdentity);

            }

            else

            {

            AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent=isPersistent }, userIdentity);

            }

            }

            可見,最終所有的代碼都是調(diào)用了

            AuthenticationManager.SignIn方法,所以該方法是創(chuàng)建 Cookie的關(guān)鍵。

            AuthenticationManager的實(shí)現(xiàn)定義在 Microsoft.Owin中,因此無法在 ASP.NETIdentity中找到其源代碼,因此我們打開 Microsoft.Owin的源代碼繼續(xù)跟蹤(有刪減):

            public void SignIn(AuthenticationProperties properties, params ClaimsIdentity[] identities)

            {

            AuthenticationResponseRevoke priorRevoke=AuthenticationResponseRevoke;

            if (priorRevoke !=null)

            {

            // ...省略不相關(guān)代碼

            AuthenticationResponseRevoke=new AuthenticationResponseRevoke(filteredSignOuts);

            }

            AuthenticationResponseGrant priorGrant=AuthenticationResponseGrant;

            if (priorGrant==null)

            {

            AuthenticationResponseGrant=new AuthenticationResponseGrant(new ClaimsPrincipal(identities), properties);

            }

            else

            {

            // ...省略不相關(guān)代碼

            AuthenticationResponseGrant=new AuthenticationResponseGrant(new ClaimsPrincipal(mergedIdentities), priorGrantperties);

            }

            }

            AuthenticationManager的 Github鏈接如下:

            github/aspnet/AspNetKatana/blob/c33569969e79afd9fb4ec2d6bdff877e376821b2/src/Microsoft.Owin/Security/AuthenticationManager.cs

            可見它用到了

            AuthenticationResponseGrant,繼續(xù)跟蹤可以看到它實(shí)際是一個(gè)屬性:

            public AuthenticationResponseGrant AuthenticationResponseGrant

            {

            // 省略get

            set

            {

            if (value==null)

            {

            SignInEntry=null;

            }

            else

            {

            SignInEntry=Tuple.Create((IPrincipal)value.Principal, valueperties.Dictionary);

            }

            }

            }

            發(fā)現(xiàn)它其實(shí)是設(shè)置了 SignInEntry,繼續(xù)追蹤:

            public Tuple> SignInEntry

            {

            get { return _context.Get>>(OwinConstants.Security.SignIn); }

            set { _context.Set(OwinConstants.Security.SignIn, value); }

            }

            其中, _context的類型為 IOwinContext,

            OwinConstants.Security.SignIn的常量值為 "security.SignIn"。

            跟蹤完畢……

            啥?跟蹤這么久,居然跟丟啦???

            當(dāng)然沒有!但接下來就需要一定的技巧了。

            原來, ASP.NET是一種中間件( Middleware)模型,在這個(gè)例子中,它會先處理 MVC中間件,該中間件處理流程到設(shè)置

            AuthenticationResponseGrant/ SignInEntry為止。但接下來會繼續(xù)執(zhí)行 CookieAuthentication中間件,該中間件的核心代碼在 aspnet/AspNetKatana倉庫中可以看到,關(guān)鍵類是

            CookieAuthenticationHandler,核心代碼如下:

            protected override async Task ApplyResponseGrantAsync()

            {

            AuthenticationResponseGrant signin=Helper.LookupSignIn(Options.AuthenticationType);

            // ... 省略部分代碼

            if (shouldSignin)

            {

            var signInContext=new CookieResponseSignInContext(

            Context,

            Options,

            Options.AuthenticationType,

            signin.Identity,

            signinperties,

            cookieOptions);

            // ... 省略部分代碼

            model=new AuthenticationTicket(signInContext.Identity, signInContextperties);

            // ... 省略部分代碼

            string cookieValue=Options.TicketDataFormattect(model);

            Options.CookieManager.AppendResponseCookie(

            Context,

            Options.CookieName,

            cookieValue,

            signInContext.CookieOptions);

            }

            // ... 又省略部分代碼

            }

            這個(gè)原始函數(shù)有超過 200行代碼,這里我省略了較多,但保留了關(guān)鍵、核心部分,想查閱原始代碼可以移步 Github鏈接:

            github/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin.Security.Cookies/CookieAuthenticationHandler.cs#L130-L313

            這里挑幾點(diǎn)最重要的講。

            與 MVC建立關(guān)系

            建立關(guān)系的核心代碼就是第一行,它從上文中提到的統(tǒng)招證書位置取回了

            AuthenticationResponseGrant,該 Grant保存了 Claims、 AuthenticationTicket等 Cookie重要組成部分:

            AuthenticationResponseGrant signin=Helper.LookupSignIn(Options.AuthenticationType);

            繼續(xù)查閱 LookupSignIn源代碼,可看到,它就是從上文中的 AuthenticationManager中取回了

            AuthenticationResponseGrant(有刪減):

            public AuthenticationResponseGrant LookupSignIn(string authenticationType)

            {

            // ...

            AuthenticationResponseGrant grant=_context.Authentication.AuthenticationResponseGrant;

            // ...

            foreach (var claimsIdentity in grant.Principal.Identities)

            {

            if (string.Equals(authenticationType, claimsIdentity.AuthenticationType, StringComparison.Ordinal))

            {

            return new AuthenticationResponseGrant(claimsIdentity, grantperties new AuthenticationProperties());

            }

            }

            return null;

            }

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

            Cookie的生成

            從 AuthenticationTicket變成 Cookie字節(jié)串,最關(guān)鍵的一步在這里:

            string cookieValue=Options.TicketDataFormattect(model);

            在接下來的代碼中,只提到使用 CookieManager將該 Cookie字節(jié)串添加到 Http響應(yīng)中,翻閱 CookieManager可以看到如下代碼:

            public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)

            {

            if (context==null)

            {

            throw new ArgumentNullException("context");

            }

            if (options==null)

            {

            throw new ArgumentNullException("options");

            }

            IHeaderDictionary responseHeaders=context.Response.Headers;

            // 省去“1萬”行計(jì)算chunk和處理細(xì)節(jié)的流程

            responseHeaders.AppendValues(Constants.Headers.SetCookie, chunks);

            }

            有興趣的朋友可以訪問 Github看原始版本的代碼:

            github/aspnet/AspNetKatana/blob/0fc4611e8b04b73f4e6bd68263e3f90e1adfa447/src/Microsoft.Owin/Infrastructure/ChunkingCookieManager.cs#L125-L215

            可見這個(gè)實(shí)現(xiàn)比較……簡單,就是往 Response.Headers中加了個(gè)頭,重點(diǎn)只要看 TicketDataFormattect方法即可。

            逐漸明朗

            該方法源代碼如下:

            public string Protect(TData data)

            {

            byte[] userData=_serializer.Serialize(data);

            byte[] protectedData=_protectortect(userData);

            string protectedText=_encoder.Encode(protectedData);

            return protectedText;

            }

            可見它依賴于 _serializer、 _protector、 _encoder三個(gè)類,其中, _serializer的關(guān)鍵代碼如下:

            public virtual byte[] Serialize(AuthenticationTicket model)

            {

            using (var memory=new MemoryStream())

            {

            using (var compression=new GZipStream(memory, CompressionLevel.Optimal))

            {

            using (var writer=new BinaryWriter(compression))

            {

            Write(writer, model);

            }

            }

            return memory.ToArray();

            }

            }

            其本質(zhì)是進(jìn)行了一次二進(jìn)制序列化,并緊接著進(jìn)行了 gzip壓縮,確保 Cookie大小不要失去控制(因?yàn)?.NET的二進(jìn)制序列化結(jié)果較大,并且微軟喜歡搞 xml,更大)。

            然后來看一下 _encoder源代碼:

            public string Encode(byte[] data)

            {

            if (data==null)

            {

            throw new ArgumentNullException("data");

            }

            return Convert.ToBase64String(data).TrimEnd('=').Replace('+', '-').Replace('/', '_');

            }

            可見就是進(jìn)行了一次簡單的 base64-url編碼,注意該編碼把 =號刪掉了,所以在 base64-url解碼時(shí),需要補(bǔ) =號。

            這兩個(gè)都比較簡單,稍復(fù)雜的是 _protector,它的類型是 IDataProtector。

            IDataProtector

            它在

            CookieAuthenticationMiddleware中進(jìn)行了初始化,創(chuàng)建代碼和參數(shù)如下:

            IDataProtector dataProtector=app.CreateDataProtector(

            typeof(CookieAuthenticationMiddleware).FullName,

            Options.AuthenticationType, "v1");

            注意它傳了三個(gè)參數(shù),第一個(gè)參數(shù)是

            CookieAuthenticationMiddleware的 FullName,也就是 "

            Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",第二個(gè)參數(shù)如果沒定義,默認(rèn)值是

            CookieAuthenticationDefaults.AuthenticationType,該值為定義為 "Cookies"。

            但是,在默認(rèn)創(chuàng)建的 ASP.NET MVC模板項(xiàng)目中,該值被重新定義為 ASP.NETIdentity的默認(rèn)值,即 "ApplicationCookie",需要注意。

            然后來看看 CreateDataProtector的源碼:

            public static IDataProtector CreateDataProtector(this IAppBuilder app, params string[] purposes)

            {

            if (app==null)

            {

            throw new ArgumentNullException("app");

            }

            IDataProtectionProvider dataProtectionProvider=GetDataProtectionProvider(app);

            if (dataProtectionProvider==null)

            {

            dataProtectionProvider=FallbackDataProtectionProvider(app);

            }

            return dataProtectionProvider.Create(purposes);

            }

            public static IDataProtectionProvider GetDataProtectionProvider(this IAppBuilder app)

            {

            if (app==null)

            {

            throw new ArgumentNullException("app");

            }

            object value;

            if (appperties.TryGetValue("security.DataProtectionProvider", out value))

            {

            var del=value as DataProtectionProviderDelegate;

            if (del !=null)

            {

            return new CallDataProtectionProvider(del);

            }

            }

            return null;

            }

            可見它先從 IAppBuilder的 "

            security.DataProtectionProvider"屬性中取一個(gè) IDataProtectionProvider,否則使用

            DpapiDataProtectionProvider。

            我們翻閱代碼,在 OwinAppContext中可以看到,該值被指定為

            MachineKeyDataProtectionProvider:

            builderperties[Constants.SecurityDataProtectionProvider]=new MachineKeyDataProtectionProvider().ToOwinFunction();

            文中的

            Constants.SecurityDataProtectionProvider,剛好就被定義為 "

            security.DataProtectionProvider"。

            我們翻閱 MachineKeyDataProtector的源代碼,剛好看到它依賴于 MachineKey:

            internal class MachineKeyDataProtector

            {

            private readonly string[] _purposes;

            public MachineKeyDataProtector(params string[] purposes)

            {

            _purposes=purposes;

            }

            public virtual byte[] Protect(byte[] userData)

            {

            return MachineKeytect(userData, _purposes);

            }

            public virtual byte[] Unprotect(byte[] protectedData)

            {

            return MachineKey.Unprotect(protectedData, _purposes);

            }

            }

            最終到了我們的老朋友 MachineKey。

            逆推過程,破解Cookie

            首先總結(jié)一下這個(gè)過程,對一個(gè)請求在 Mvc中的流程來說,這些代碼集中在 ASP.NETIdentity中,它會經(jīng)過:

            AccountControllerSignInManagerAuthenticationManager設(shè)置 AuthenticationResponseGrant

            然后進(jìn)入 CookieAuthentication的流程,這些代碼集中在 Owin中,它會經(jīng)過:

            CookieAuthenticationMiddleware(讀取 AuthenticationResponseGrant)ISecureDataFormat(實(shí)現(xiàn)類: SecureDataFormat)IDataSerializer(實(shí)現(xiàn)類: TicketSerializer)IDataProtector(實(shí)現(xiàn)類: MachineKeyDataProtector)ITextEncoder(實(shí)現(xiàn)類: Base64UrlTextEncoder)

            這些過程,結(jié)果上文中找到的所有參數(shù)的值,我總結(jié)出的“祖?zhèn)髌平獯a”如下:

            string cookie="nZBqV1M-Az7yJezhb6dUzS_urj1urB0GDufSvDJSa0pv27CnDsLHRzMDdpU039j6ApL-VNfrJULfE85yU9RFzGV_aAGXHVkGckYqkCRJUKWV8SqPEjNJ5ciVzW--uxsCBNlG9jOhJI1FJIByRzYJvidjTYABWFQnSSd7XpQRjY4lb082nDZ5lwJVK3gaC_zt6H5Z1k0lUFZRb6afF52laMc___7BdZ0mZSA2kRxTk1QY8h2gQh07HqlR_p0uwTFNKi0vW9NxkplbB8zfKbfzDj7usep3zAeDEnwofyJERtboXgV9gIS21fLjc58O-4rR362IcCi2pYjaKHwZoO4LKWe1bS4r1tyzW0Ms-39Njtiyp7lRTN4HUHMUi9PxacRNgVzkfK3msTA6LkCJA3VwRm_UUeC448Lx5pkcCPCB3lGat_5ttGRjKD_lllI-YE4esXHB5eJilJDIZlEcHLv9jYhTl17H0Jl_H3FqXyPQJR-ylQfh";

            var bytes=TextEncodings.Base64Url.Decode(cookie);

            var decrypted=MachineKey.Unprotect(bytes,

            "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware",

            "ApplicationCookie",

            "v1");

            var serializer=new TicketSerializer();

            var ticket=serializer.Deserialize(decrypted);

            ticket.Dump(); // Dump為LINQPad專有函數(shù),用于方便調(diào)試顯示,此處可以用循環(huán)輸出代替

            運(yùn)行前請?jiān)O(shè)置好 app.config/ web.config中的 machineKey節(jié)點(diǎn),并安裝 NuGet包: Microsoft.Owin.Security,運(yùn)行結(jié)果如下(完美破解):

            抽絲剝繭:淺議ASP.NET Cookie的生成原理

            總結(jié)

            學(xué)習(xí)方式有很多種,其中看代碼是我個(gè)人非常喜歡的一種方式,并非所有代碼都會一馬平川。像這個(gè)例子可能還需要有一定 ASP.NET知識背景。

            注意這個(gè)“祖?zhèn)鞔a”是基于 .NETFramework,由于其用到了 MachineKey,因此無法在 .NETCore中運(yùn)行。我稍后將繼續(xù)深入聊聊 MachineKey這個(gè)類,看它底層代碼是如何工作的,然后最終得以在 .NETCore中直接破解 ASP.NETIdentity中的 Cookie,敬請期待!