- ABP Framework version: v10.2.0 (tiered, Separate Auth Server + HttpApi.Host + Web + Hangfire + Web.Public)
- UI type: MVC
- Database System: EF Core + Azure SQL
- Tiered (for MVC) or Identity Server Separated (for Angular): yes — Tiered with separate AuthServer (OpenIddict)
- Exception message and full stack trace: N/A — this is a proactive question about CVE remediation, not a runtime exception.
- Steps to reproduce the issue: N/A — see question below.
Hi team,
Following the out-of-band release of Microsoft.AspNetCore.DataProtection 10.0.7 on April 21 and the CVE-2026-40372 advisory (dotnet/announcements#395), and the corresponding OpenIddict 7.5.0 release on April 22 (release notes), we need authoritative guidance on how to remediate this in an ABP tiered solution. Our production deployment runs on Azure App Service (Linux), which is the primary affected configuration per the advisory.
Our setup:
- Separate hosts:
AuthServer,HttpApi.Host,Web,Web.Public, andHangfire— all referencing the ABP OpenIddict modules (Volo.Abp.OpenIddict.AspNetCore,Volo.Abp.OpenIddict.Domain, etc.). - Shared Data Protection key ring persisted via
PersistKeysToAzureBlobStorage(...)with a sharedSetApplicationName("CabMD-{env}")across all hosts, so that cookies, antiforgery tokens, OpenIddict state, and SignalR handshakes all unprotect correctly across scale-out and slot swaps. - OpenIddict is configured via the ABP module defaults (we are not using JWT opt-out — tokens are in the default ABP/OpenIddict encrypted format). Our questions:
Transitive exposure through ABP packages: Do any current ABP packages (e.g.
Volo.Abp.AspNetCore.DataProtectionor theVolo.Abp.OpenIddict.*packages) pin or transitively bring in a vulnerableMicrosoft.AspNetCore.DataProtectionversion onnet10.0? Adding a top-level<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="10.0.7" />to each host's csproj should win via NuGet's nearest-wins rule, but we'd like confirmation that ABP does not resolve a different asset through the shared framework reference pattern described in the advisory (the "shared framework >= PackageReference" safe case vs. the "PackageReference wins" unsafe case).Recommended revocation pattern for ABP tiered solutions: The advisory and OpenIddict 7.5.0 notes both recommend calling
IKeyManager.RevokeAllKeys()andIOpenIddictTokenManager.RevokeAsync(null, null, null, null). In a tiered ABP solution, which host should own this one-time operation?- Our current thinking: a one-shot
IHostedServicein the AuthServer gated behind a configuration flag (e.g.CabMD:DataProtection:RevokeOnStartup=true), since the AuthServer owns both the key-ring registration and the OpenIddict domain services. Is there an ABP idiomatic approach (a moduleOnApplicationInitializationAsynchook, aBackgroundWorker, or a CLI-styleApplicationInitializer) you'd prefer?
- Our current thinking: a one-shot
ABP-internal consumers of Data Protection: Beyond the obvious ones (cookie auth, antiforgery, OpenIddict state/authorization/device codes in DP format), are there any ABP-specific features whose tokens/URLs become invalid after a
RevokeAllKeyscall that we should communicate to users? Off the top of my head: email confirmation links, password reset links, andIdentityLinkUsertokens. Anything else we should be aware of — especially any features that store DP-protected values at rest in the database (where revocation would break stored data rather than just forcing re-auth)?OpenIddict server/validation encryption key vs. Data Protection key ring: Just to confirm the threat model — the OpenIddict server encryption/signing certificate (the one configured via
AddDevelopmentEncryptionCertificate/AddEncryptionCertificateonOpenIddictServerBuilder) is separate from the ASP.NET Core Data Protection key ring, and does not need to be rotated for this CVE. The advisory is purely about the DP-managed encryptor HMAC flaw. Is that correct?SetApplicationNameinterop after revocation: Our shared-key-ring helper setsSetApplicationName("CabMD-{env}")across all hosts so that a cookie issued by AuthServer can be unprotected by Web/HttpApi.Host. After aRevokeAllKeyscall on one host, the next protect operation auto-generates a new active key in the shared blob. We want to confirm: all other hosts pick up the new key on their next read (i.e. there is no per-host cache that needs to be invalidated by a restart)? Or should we plan a coordinated restart across all hosts immediately after revocation to avoid a transient window where a host is holding a staleXmlKeyManagersnapshot?
Thanks in advance — we are trying to sequence the patch, key revocation, and token revocation cleanly in the same deployment window and want to avoid a second outage from missing an ABP-specific step.
3 Answer(s)
-
0
Hi,
Confirmed — with the default ABP setup you're running, your app isn't hit by CVE-2026-40372. ABP does not call
UseDataProtection()on the OpenIddict server/validation builder, so all OpenIddict tokens (auth code, refresh, device code, state) are encrypted with the X.509 certificate you register viaAddEncryptionCertificate(...), and access tokens are JWTs (DisableAccessTokenEncryption()is applied by default). The DP flaw only reaches the ASP.NET Core side of your app (cookies, antiforgery, ASP.NET Core Identity DP tokens), not OpenIddict tokens.On top of that, Azure App Service Linux has already rolled the shared framework to 10.0.7+, which is what your framework-dependent hosts actually load for
Microsoft.AspNetCore.DataProtection. So in practice you're already on the patched assembly.Per-question answers below.
1. Transitive exposure through ABP packages
ABP core and
Volo.Abp.OpenIddict.*don't pullMicrosoft.AspNetCore.DataProtectionas a NuGet reference onnet10.0— on that TFM,OpenIddict.*.AspNetCoregets DataProtection from theMicrosoft.AspNetCore.Appshared framework. The only NuGet-level DP refs in ABP templates areMicrosoft.AspNetCore.DataProtection.StackExchangeRedisand, on the Pro side, a directMicrosoft.AspNetCore.DataProtectionreference inVolo.Abp.Identity.Pro.DomainandVolo.FileManagement.Domain.Loading rule is
max(sharedFramework, PackageReference), and it's safe iff that max is≥ 10.0.7. As long as your runtime patch is 10.0.7+, the shared framework wins and you're safe even with the current 10.0.2 PackageReference in ABP 10.2.We already pushed PRs on April 23 to bump all
Microsoft.*/System.*packages to 10.0.7 across ABP, ABP Pro, and ABP Studio, so you'll pick that up in the next 10.2.x / 10.3.x patch. If you want a hard floor now without waiting for the bump, add these to each host csproj — this guarantees NuGet wins even if the runtime patch somewhere lags:<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="10.0.7" /> <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="10.0.7" />2. Recommended revocation pattern for tiered solutions
Your plan is exactly what we'd recommend: one-shot
IHostedServiceon the AuthServer, gated by a config flag. The AuthServer owns the key-ring registration and the OpenIddict domain services, so bothIKeyManager.RevokeAllKeys()andIOpenIddictTokenManager.RevokeAsync(null, null, null, null)belong there. ABP doesn't have a dedicated idiomatic hook for this;IHostedServiceorOnApplicationInitializationAsyncboth work. Use a DB row / Redis key as an idempotency marker so it doesn't re-run on every restart.Sketch:
public class CveKeyRevocationHostedService : IHostedService { private readonly IServiceProvider _serviceProvider; private readonly IConfiguration _configuration; public CveKeyRevocationHostedService(IServiceProvider serviceProvider, IConfiguration configuration) { _serviceProvider = serviceProvider; _configuration = configuration; } public async Task StartAsync(CancellationToken cancellationToken) { if (!_configuration.GetValue<bool>("CabMD:DataProtection:RevokeOnStartup")) { return; } using var scope = _serviceProvider.CreateScope(); var keyManager = scope.ServiceProvider.GetRequiredService<IKeyManager>(); keyManager.RevokeAllKeys(DateTimeOffset.UtcNow, "CVE-2026-40372"); var tokenManager = scope.ServiceProvider.GetRequiredService<IOpenIddictTokenManager>(); await tokenManager.RevokeAsync(subject: null, client: null, status: null, type: null); // persist an idempotency marker here (DB row / Redis key) so a later restart skips this } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; }Register it in the AuthServer module's
ConfigureServices:context.Services.AddHostedService<CveKeyRevocationHostedService>();3. ABP-internal consumers of Data Protection
Here's the full list for an ABP tiered setup. Everything below is invalidated by
RevokeAllKeys— but none of it stores DP-protected ciphertext in the database, so revocation only forces re-auth and invalidates pending email/share links. It does not break any at-rest data.- ASP.NET Core cookie authentication (session cookies across AuthServer / Web / Web.Public / Hangfire / HttpApi.Host)
- ASP.NET Core antiforgery tokens
- OIDC external login
stateparameter (Google/Microsoft/etc. handlers) - ASP.NET Core Identity DP-based token providers (ABP extends these):
- email confirmation
- password reset
- change email / change phone number
- link external login
AbpSingleActiveTokenProvider— stores a SHA256 hash inAbpUserTokens, not the DP ciphertext, so revoke is safe
- ABP Pro only:
UserInvitationTokenProvider— pending invitation emails become invalidShareFileTokenProviderin File Management — any outstanding share link becomes invalid
Not affected: all OpenIddict tokens (auth code, refresh, device code, state, access token) — they use the OpenIddict encryption/signing certificate, not DP.
4. OpenIddict encryption/signing certificate vs. DP key ring
Correct — they're completely separate. The certificate registered via
AddEncryptionCertificate/AddSigningCertificateonOpenIddictServerBuilderencrypts OpenIddict's own tokens. The DP key ring is only used by ASP.NET Core (cookies, antiforgery) and ASP.NET Core Identity DP token providers. CVE-2026-40372 is purely about the DP encryptor HMAC flaw, so you do not need to rotate the OpenIddict encryption/signing cert for this CVE.5. SetApplicationName interop after revocation
There is a per-host cache.
KeyRingProvidercaches the active key ring, andKeyManagementOptions.KeyRingRefreshPeriodcontrols refresh (default 24h).RevokeAllKeysdoes not push a notification to other hosts — they'll only pick up the revocation and the new active key on their next refresh.That means there is a transient window after you call
RevokeAllKeyswhere the other hosts are still holding the stale key ring: unprotect will fail (keys revoked), and protect operations on those nodes can race when auto-generating a new active key.Coordinated restart of all hosts (AuthServer + HttpApi.Host + Web + Web.Public + Hangfire) immediately after revocation is the right call. Don't rely on the 24h refresh for incident response.
Recommended sequence for your deployment window:
- Deploy all hosts with the 10.0.7 packages (and, if you want the PackageReference floor, the two lines from Q1).
- Flip
CabMD:DataProtection:RevokeOnStartup=trueon the AuthServer only. - Restart the AuthServer — the hosted service calls
RevokeAllKeys+RevokeAsync. - Immediately restart the remaining hosts (HttpApi.Host / Web / Web.Public / Hangfire) so they flush their cached key ring.
- Flip the flag back to false and redeploy the AuthServer (or just rely on the idempotency marker).
Thanks.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Ok, thank you. We completed this process, used redis for the value. Everything worked perfectly.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)