ASP.NET Core从.NET5迁移至.NET6后会话Cookie解密失败问题
我们公司有一个2021年底上线的生产环境Web应用,原本基于ASP.NET Core 5.0部署在Azure App Service(Linux)容器中,系统包含用Duende Identity Server NuGet包构建的自定义OpenID Connect服务器,应用的会话Cookie存储认证信息。
两周前将项目从.NET5迁移至.NET6后,开始抛出如下异常:
warn: Microsoft.AspNetCore.Session.SessionMiddleware[7] Error unprotecting the session cookie. System.Security.Cryptography.CryptographicException: The payload was invalid. For more information go to http://aka.ms/dataprotectionwarning at Microsoft.AspNetCore.DataProtection.Managed.ManagedAuthenticatedEncryptor.Decrypt(ArraySegment`1 protectedPayload, ArraySegment`1 additionalAuthenticatedData) at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.UnprotectCore(Byte[] protectedData, Boolean allowOperationsOnRevokedKeys, UnprotectStatus& status) at Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector.Unprotect(Byte[] protectedData) at Microsoft.AspNetCore.Session.CookieProtection.Unprotect(IDataProtector protector, String protectedText, ILogger logger)
最初怀疑密钥环缺少旧的数据保护密钥,但已确认自首次部署以来生成的所有保护密钥均已持久化到文件系统,最新密钥内容如下:
<key id="xy" version="1"> <creationDate>2023-03-02T02:31:35.2845259Z</creationDate> <activationDate>2023-03-03T16:46:59.1304486Z</activationDate> <expirationDate>2023-05-31T02:31:34.7095525Z</expirationDate> <descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60"> <descriptor> <encryption algorithm="AES_256_CBC"/> <validation algorithm="HMACSHA256"/> <masterKey xmlns:p4="http://schemas.asp.net/2015/03/dataProtection" p4:requiresEncryption="true"> <!-- Warning: the key below is in an unencrypted form. --> <value>xy</value> </masterKey> </descriptor> </descriptor> </key>
随后猜测问题可能出在XML中的deserializerType属性——它引用了.NET5程序集,但当前项目运行在.NET6环境,但不确定这是否会引发问题。另外一个关键情况:
仅部分用户出现该问题,我多次登录登出系统创建、销毁会话Cookie,均无法复现“会话Cookie解密失败”异常。
怀疑部分用户持有旧的会话Cookie,但所有数据保护密钥均存在,不确定旧Cookie是否会导致该异常。
如何解决此问题?
以下是认证配置(已替换敏感数据):
builder.Services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies", options => { options.Cookie.Domain = "xy.com"; options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; options.Cookie.Name = "xy_cookie"; options.Cookie.SameSite = SameSiteMode.None; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; }) .AddOpenIdConnect("oidc", options => { //... });
1. 修复密钥描述符的兼容性问题
你猜测的deserializerType引用.NET5程序集确实是核心问题之一。.NET6的数据保护系统在解析旧版本密钥描述符时,可能因程序集版本差异出现反序列化失败。通过显式配置兼容性模式解决:
在Program.cs中添加数据保护配置:
builder.Services.AddDataProtection() .SetApplicationName("你的应用名称") // 必须和.NET5部署时的应用名称完全一致 .SetCompatibilityMode(CompatibilityMode.Version_2_1); // 强制兼容旧版本密钥格式
2. 自动处理无效旧Cookie
部分用户持有的.NET5生成的Cookie,即使密钥存在,也可能因.NET6的加密逻辑细微变化导致解密失败。通过Cookie认证事件捕获异常并自动清理:
修改AddCookie的配置:
.AddCookie("Cookies", options => { // 原有配置... options.Events = new CookieAuthenticationEvents { OnValidatePrincipal = async context => { try { // 触发主体验证,解密失败会抛出CryptographicException await Task.CompletedTask; } catch (CryptographicException) { // 清除无效Cookie并登出用户 context.RejectPrincipal(); await context.HttpContext.SignOutAsync("Cookies"); } } }; })
3. 验证密钥加载完整性
确保Azure App Service中所有历史密钥都能被.NET6应用读取,可添加日志确认:
在Program.cs中添加:
var serviceProvider = builder.Services.BuildServiceProvider(); var keyManager = serviceProvider.GetRequiredService<IKeyManager>(); var allKeys = keyManager.GetAllKeys(); foreach (var key in allKeys) { builder.Logging.LogInformation($"已加载密钥: {key.KeyId} | 创建时间: {key.CreationDate} | 过期时间: {key.ExpirationDate}"); }
如果发现旧密钥未加载,检查文件存储路径权限或Azure挂载配置是否正常。
4. 生成新密钥避免后续问题
在.NET6环境中手动生成新密钥,新密钥的deserializerType会自动使用.NET6程序集版本,彻底解决版本兼容问题:
var keyManager = serviceProvider.GetRequiredService<IKeyManager>(); keyManager.CreateNewKey(DateTimeOffset.Now, DateTimeOffset.Now.AddMonths(6));
内容的提问来源于stack exchange,提问作者Kerberos

