Open Closed

CVE-2026-40372 (ASP.NET Core Data Protection) in a Tiered OpenIddict Solution #10620


User avatar
0
  • 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, and Hangfire — 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 shared SetApplicationName("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:
  1. Transitive exposure through ABP packages: Do any current ABP packages (e.g. Volo.Abp.AspNetCore.DataProtection or the Volo.Abp.OpenIddict.* packages) pin or transitively bring in a vulnerable Microsoft.AspNetCore.DataProtection version on net10.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).

  2. Recommended revocation pattern for ABP tiered solutions: The advisory and OpenIddict 7.5.0 notes both recommend calling IKeyManager.RevokeAllKeys() and IOpenIddictTokenManager.RevokeAsync(null, null, null, null). In a tiered ABP solution, which host should own this one-time operation?

    • Our current thinking: a one-shot IHostedService in 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 module OnApplicationInitializationAsync hook, a BackgroundWorker, or a CLI-style ApplicationInitializer) you'd prefer?
  3. 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 RevokeAllKeys call that we should communicate to users? Off the top of my head: email confirmation links, password reset links, and IdentityLinkUser tokens. 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)?

  4. 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/AddEncryptionCertificate on OpenIddictServerBuilder) 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?

  5. SetApplicationName interop after revocation: Our shared-key-ring helper sets SetApplicationName("CabMD-{env}") across all hosts so that a cookie issued by AuthServer can be unprotected by Web/HttpApi.Host. After a RevokeAllKeys call 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 stale XmlKeyManager snapshot?

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.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

3 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    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 via AddEncryptionCertificate(...), 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 pull Microsoft.AspNetCore.DataProtection as a NuGet reference on net10.0 — on that TFM, OpenIddict.*.AspNetCore gets DataProtection from the Microsoft.AspNetCore.App shared framework. The only NuGet-level DP refs in ABP templates are Microsoft.AspNetCore.DataProtection.StackExchangeRedis and, on the Pro side, a direct Microsoft.AspNetCore.DataProtection reference in Volo.Abp.Identity.Pro.Domain and Volo.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 IHostedService on the AuthServer, gated by a config flag. The AuthServer owns the key-ring registration and the OpenIddict domain services, so both IKeyManager.RevokeAllKeys() and IOpenIddictTokenManager.RevokeAsync(null, null, null, null) belong there. ABP doesn't have a dedicated idiomatic hook for this; IHostedService or OnApplicationInitializationAsync both 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 state parameter (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 in AbpUserTokens, not the DP ciphertext, so revoke is safe
    • ABP Pro only:
      • UserInvitationTokenProvider — pending invitation emails become invalid
      • ShareFileTokenProvider in 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 / AddSigningCertificate on OpenIddictServerBuilder encrypts 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. KeyRingProvider caches the active key ring, and KeyManagementOptions.KeyRingRefreshPeriod controls refresh (default 24h). RevokeAllKeys does 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 RevokeAllKeys where 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:

    1. Deploy all hosts with the 10.0.7 packages (and, if you want the PackageReference floor, the two lines from Q1).
    2. Flip CabMD:DataProtection:RevokeOnStartup=true on the AuthServer only.
    3. Restart the AuthServer — the hosted service calls RevokeAllKeys + RevokeAsync.
    4. Immediately restart the remaining hosts (HttpApi.Host / Web / Web.Public / Hangfire) so they flush their cached key ring.
    5. 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)
  • User Avatar
    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)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Glad to hear it all went through cleanly. Feel free to open a fresh ticket if anything else pops up.

    Thanks.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.