.Net Core 5.0 - Sql Azure + Toujours chiffré + Identité gérée

Dec 11 2020

J'ai un Azure SQL Db avec des colonnes chiffrées (toujours chiffrées avec Azure KeyVault). Je peux accéder à cette base de données à partir de SSMS et je peux voir les données déchiffrées.

J'ai également une application Web créée avec .Net Core 5.0 qui est déployée sur Azure App Service. Le service d'application a activé l'identité gérée et Key Vault qui a des clés enc / dec pour cette base de données SQL a un paramètre de stratégie d'accès permettant à ce service d'application de déchiffrer les données.

L'application Web fonctionne avec une identité gérée car je peux voir que les données non chiffrées sont récupérées sans aucun problème.

En outre, la chaîne de connexion inclut Column Encryption Setting=enabled;. Voici la chaîne de connexion:

Server=tcp:server.database.windows.net,1433;Database=somedb;Column Encryption Setting=enabled;

Le problème est que je ne trouve AUCUN échantillon avec ce type de configuration. J'en ai trouvé et je comprends que je dois m'inscrire SqlColumnEncryptionAzureKeyVaultProvider. Voici mon code pour obtenir SqlConnection:

    internal static class AzureSqlConnection
    {
        private static bool _isInitialized;

        private static void InitKeyVaultProvider(ILogger logger)
        {
            /*
             * from here - https://github.com/dotnet/SqlClient/blob/master/release-notes/add-ons/AzureKeyVaultProvider/1.2/1.2.0.md
             *      and  - https://github.com/dotnet/SqlClient/blob/master/doc/samples/AzureKeyVaultProviderExample.cs
             *
             */

            try
            {
                // Initialize AKV provider
                SqlColumnEncryptionAzureKeyVaultProvider sqlColumnEncryptionAzureKeyVaultProvider =
                    new SqlColumnEncryptionAzureKeyVaultProvider(AzureActiveDirectoryAuthenticationCallback);

                // Register AKV provider
                SqlConnection.RegisterColumnEncryptionKeyStoreProviders(
                    new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(1, StringComparer.OrdinalIgnoreCase)
                    {
                        {SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, sqlColumnEncryptionAzureKeyVaultProvider}
                    });

                _isInitialized = true;
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Could not register SqlColumnEncryptionAzureKeyVaultProvider");
                throw;
            }
        }

        internal static async Task<SqlConnection> GetSqlConnection(string connectionString, ILogger logger)
        {
            if (!_isInitialized) InitKeyVaultProvider(logger);

            try
            {
                SqlConnection conn = new SqlConnection(connectionString);
                /*
                         * This is Managed Identity (not Always Encrypted)
                         *  https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi#modify-aspnet-core
                         *
                         */
#if !DEBUG
                conn.AccessToken = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/");
                logger.LogInformation($"token: {conn.AccessToken}"); #endif await conn.OpenAsync(); return conn; } catch (Exception ex) { logger.LogError(ex, "Could not establish a connection to SQL Server"); throw; } } private static async Task<string> AzureActiveDirectoryAuthenticationCallback(string authority, string resource, string scope) { return await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net/"); //AuthenticationContext? authContext = new AuthenticationContext(authority); //ClientCredential clientCred = new ClientCredential(s_clientId, s_clientSecret); //AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred); //if (result == null) //{ // throw new InvalidOperationException($"Failed to retrieve an access token for {resource}");
            //}

            //return result.AccessToken;
        }
    }

Ce code ne lève aucune exception et fonctionne pour les requêtes non chiffrées. Mais pour les requêtes cryptées, j'obtiens l'erreur suivante:

Échec du déchiffrement d'une clé de chiffrement de colonne. Nom du fournisseur de stockage de clés non valide: "AZURE_KEY_VAULT". Un nom de fournisseur de stockage de clés doit désigner un fournisseur de stockage de clés système ou un fournisseur de stockage de clés personnalisé enregistré. Les noms de fournisseur de stockage de clés système valides sont: "MSSQL_CERTIFICATE_STORE", "MSSQL_CNG_STORE", "MSSQL_CSP_PROVIDER". Les noms de fournisseur de stockage de clés personnalisés valides (actuellement enregistrés) sont:. Veuillez vérifier les informations du fournisseur de stockage de clés dans les définitions de clé principale de colonne de la base de données et vérifier que tous les fournisseurs de stockage de clés personnalisés utilisés dans votre application sont correctement enregistrés. Échec du déchiffrement d'une clé de chiffrement de colonne. Nom du fournisseur de stockage de clés non valide: "AZURE_KEY_VAULT". Un nom de fournisseur de stockage de clés doit désigner un fournisseur de stockage de clés système ou un fournisseur de stockage de clés personnalisé enregistré.Les noms de fournisseur de stockage de clés système valides sont: "MSSQL_CERTIFICATE_STORE", "MSSQL_CNG_STORE", "MSSQL_CSP_PROVIDER". Les noms de fournisseur de stockage de clés personnalisés valides (actuellement enregistrés) sont:. Veuillez vérifier les informations du fournisseur de stockage de clés dans les définitions de clé principale de colonne de la base de données et vérifier que tous les fournisseurs de stockage de clés personnalisés utilisés dans votre application sont correctement enregistrés.

Il semble que le fournisseur de coffre-fort de clés ne soit pas enregistré.

Que dois-je faire pour que l'interrogation des données chiffrées fonctionne?

paquets utilisés

    <PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.6.0" />
    <PackageReference Include="Microsoft.Data.SqlClient" Version="2.1.0" />
    <PackageReference Include="Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider" Version="1.2.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />

Réponses

2 alvipeo Jan 25 2021 at 23:15

Il s'avère qu'il est impossible de lire les données décryptées dans .NET 5 lorsque MSI est utilisé. Il y a un bogue dans les packages MS et le service d'application n'est jamais autorisé.

Vous devez utiliser Service Principal. Ça fonctionne super bien!

Mettre à jour

Je dois remercier les ingénieurs MS qui ont proposé une solution fonctionnelle:

public static async Task<string> KeyVaultAuthenticationCallback(string authority, string resource, string scope)
{
     return await Task.Run(() => new ManagedIdentityCredential().GetToken(new TokenRequestContext(new string [] {"https://vault.azure.net/.default"})).Token);
     /********************** Alternatively, to use User Assigned Managed Identity ****************/
     // var clientId = {clientId_of_UserAssigned_Identity};
     // return await Task.Run(() => new ManagedIdentityCredential(clientId).GetToken(new TokenRequestContext(new string [] {"https://vault.azure.net/.default"})).Token);
}
SpinDoctor Apr 10 2021 at 04:19

J'ai pu utiliser ce code qui utilise fournit un TokenCredential au fournisseur SqlColumnEncryption. DefaultAzureCredential renvoie une identité gérée lorsqu'elle est déployée en tant qu'App Service:

            SqlColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(new DefaultAzureCredential());
            Dictionary<string, SqlColumnEncryptionKeyStoreProvider> providers = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>
            {
                { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider }
            };
            SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);

Appelez-le depuis votre méthode startup.Configure.