Kontrola zda není v .NET Core aplikaci použit pouze In-memory EphemeralXmlRepository úložiště klíčů.

Jan Holan       27.12.2018       .NET Core, ASP.NET Core       2805 zobrazení

V .NET Core aplikaci používám CookieAuthentication. Nastal problém, že po nasazení aplikace na IIS nechodilo zapamatování přihlášení, vždy po novém startu nebo recyklaci aplikačního poolu.

V čem byl problém?
Pro zapamatování přihlášení používám authentication cookie, které je persistentní. Cookie se při startu requestu v pořádku předalo, ale nebylo ho možné dešifrovat. To jsem zjistil z logu nalezením těchto trace zpráv (konkrétní klíče jsou v hlášce změněny):

Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:
Performing unprotect operation to key {********-****-****-****-************} with purposes (...). 

Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:
Key {********-****-****-****-************} was not found in the key ring. Unprotect operation cannot proceed. 

Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:
************ was not authenticated. Failure message: Unprotect ticket failed

Po dalším zkoumáním trace logu, tentokrát z doby startupu aplikace, kde se v ConfigureServices volá services.AddDataProtection() (který je právě potřeba pro CookieAuthentication) jsem objevil tyto warning hlášky:

Microsoft.AspNetCore.DataProtection.Repositories.EphemeralXmlRepository
Using an in-memory repository. Keys will not be persisted to storage.

Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager
Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits

.NET Core se v tomto případě nepovedlo najít a použít persistentní storage pro ukládání klíčů, a použilo EphemeralXmlRepository, což je pouze in-memory uložiště klíčů. Důsledek tedy je, že při každém novém spuštění jsou vygenerovány jiné klíče a tedy původní předaný authentication cookie se dešifruje jiným klíčen a tím dešifrovat nelze.
Celé je to způsobeno tím, že v aplikačním poolu na IIS není povolena volba Load User Profile, více je to popsáno například zde nebo zde. Po jejím zapnutí již DataProtection najde FileSystemXmlRepository uložiště do adresáře pod uživatelským profilem, kam klíče ukládá.

Co mi na tom celém ale vadí je to, že až se aplikace nasadí příště (například k jinému zákazníkovi) a zapomene se na toto nastavení, .NET Core jen “potichu” použije toto nepersistentní úložiště a aplikace (v tomto případě zapamatování hesla) nebude fungovat správně. (Vypsaného warningu si nikdo zase nevšimne).

V nastavení jsem ale nenašel žádnou možnost, jak vynutit vynechání EphemeralXmlRepository a vyvolání runtime chyby při startu aplikace. Přes nastavení v metodě .AddDataProtection() lze pouze určit například použití jednoho konkrétního úložiště, tím bych ale přišel o implementovanou logiku výběru vhodného úložiště pro dané prostředí (Windows, Linux, Azure Web Sites, atd).
Pro zajímavost, celý kód této logiky je vidět v metodě GetFallbackKeyRepositoryEncryptorPair zde.

Provedl jsem tedy ve Startup třídě vlastní volání, které provede kontrolu, zda nebyl použit pouze In-memory EphemeralXmlRepository storage, a pokud ano, tak dojde k vyhození výjimky.
Ke zjištění se v instanci Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager pomoci reflection (bohužel) zkontroluje internal vlastnost KeyRepository, zda se nejedná právě o typ EphemeralXmlRepository.

public void Configure(IApplicationBuilder app, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory, Microsoft.AspNetCore.DataProtection.KeyManagement.IKeyManager keyManager)
{
    //Check if only In-memory EphemeralXmlRepository is used in XmlKeyManager from services.AddDataProtection().
    keyManager.VerifyEphemeralXmlRepositoryIsNotUsed();

    ...
}
public static class DataProtectionKeyManagerExtension
{
    public static bool IsEphemeralXmlRepositoryUsed(this IKeyManager xmlKeyManager)
    {
        //Check if only In-memory EphemeralXmlRepository is used in XmlKeyManager from services.AddDataProtection().
        if (xmlKeyManager is XmlKeyManager)
        {
            //Get Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager.KeyRepository internal field
            var keyRepositoryProperty = xmlKeyManager.GetType().GetProperty("KeyRepository", BindingFlags.Instance | BindingFlags.NonPublic);
            object keyRepository = keyRepositoryProperty.GetValue(xmlKeyManager);

            if (keyRepository.GetType().Name.Equals("EphemeralXmlRepository", StringComparison.OrdinalIgnoreCase)) //Microsoft.AspNetCore.DataProtection.Repositories.EphemeralXmlRepository
            {
                return true;
            }
        }

        return false;
    }

    public static void VerifyEphemeralXmlRepositoryIsNotUsed(this IKeyManager xmlKeyManager)
    {
        if (xmlKeyManager.IsEphemeralXmlRepositoryUsed())
        {
            string message = @"Neither user profile nor HKLM registry is available. An in-memory ephemeral key repository will be used.
Keys will not be persisted to storage and protected data will be unavailable when application exits.
If you are using IIS on Windows, this could be due to the loadUserProfile setting on the Application Pool being set to false.";

            throw new ConfigurationErrorsException(message);
        }
    }
}

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback