In uno scenario in cui dobbiamo effettuare chiamate server-to-server in modalità sicura, una possibile alternativa a OAuth2 è l'utilizzo di certificati X509. Con questo sistema, il client allegherà alla richiesta un particolare certificato, la cui thumbprint verrà poi verificata dal server.
Ci sono vari vantaggi nell'adozione di un sistema di questo tipo: se pensiamo per esempio a un'architettura basata su decine di micro servizi, tutti questi componenti possono condividere il medesimo certificato memorizzato su Azure KeyVault, così da semplificare scenari di rotazione delle chiavi.
Come implementare un sistema del genere in ASP.NET Core?
Per prima cosa abbiamo bisogno di generare due certificati di test:
1) una Certificate Authority
2) un certificato di test vero e proprio, generato a partire dalla CA precedente
Entrambi questi task possono essere eseguiti tramite il codice PowerShell in basso:
# Generiamo il certificato per la CA $rootCert = New-SelfSignedCertificate -DnsName "localhost", "localhost" ` -CertStoreLocation "cert:\CurrentUser\My" ` -NotAfter (Get-Date).AddYears(20) ` -FriendlyName "CertDemoRoot" ` -KeyUsageProperty All ` -KeyUsage CertSign, CRLSign, DigitalSignature $rootThumbprint = $rootCert.Thumbprint $mypwd = ConvertTo-SecureString -String "0000" -Force -AsPlainText Get-ChildItem -Path "cert:\CurrentUser\My\$rootThumbprint" ` | Export-PfxCertificate -FilePath .\CertDemoRoot.pfx -Password $mypwd > $null # Installiamo il CA Cert nei TrustedRoot Import-PfxCertificate -FilePath .\CertDemoRoot.pfx ` -CertStoreLocation "cert:\CurrentUser\Root" ` -Password $mypwd > $null # Creiamo il client certificate e installiamo su My $clientCert = New-SelfSignedCertificate ` -certstorelocation cert:\CurrentUser\my ` -dnsname "localhost" ` -Signer $rootCert ` -NotAfter (Get-Date).AddYears(20) ` -FriendlyName "CertDemoClient" $clientThumbprint = $clientCert.Thumbprint Get-ChildItem -Path "cert:\CurrentUser\my\$clientThumbprint" ` | Export-PfxCertificate -FilePath .\CertDemoClient.pfx -Password $mypwd > $null # Rimuoviamo il CA Cert da My Get-ChildItem -Path "cert:\CurrentUser\My\$rootThumbprint" | Remove-Item > $null Write-Host "Thumbprint: $clientThumbprint"
Grazie a questo script, avremo nel nostro store personale due nuovi certificati pronti all'uso. Ora non dobbiamo far altro che creare un progetto ASP.NET Core Web API, e aggiungere una reference al package:
Microsoft.AspNetCore.Authentication.Certificate
A questo punto, possiamo iniziare a modificare il nostro metodo Main come segue:
public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // .. altro codice qui .. builder.Services.Configure<KestrelServerOptions>(options => { options.ConfigureHttpsDefaults(options => { options.ClientCertificateMode = ClientCertificateMode.AllowCertificate; }); }); var cert = new X509Certificate2(@"C:\[folder]\CertDemoClient.pfx", "0000"); builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme) .AddCertificate(options => { options.AllowedCertificateTypes = CertificateTypes.SelfSigned; options.Events = new CertificateAuthenticationEvents() { OnCertificateValidated = ctx => { if (ctx.ClientCertificate.Thumbprint == cert.Thumbprint) { Console.WriteLine("Certs match!"); ctx.Success(); } else { Console.WriteLine("certs don't match"); ctx.Fail("wrong cert"); } return Task.CompletedTask; } }; }); builder.Services.AddAuthorization(); // .. altro codice qui .. app.UseAuthentication(); app.UseAuthorization(); // .. altro codice qui .. }
Il codice in alto è un estratto del metodo Main, dove per prima cosa andiamo a configurare Kestrel per accettare un certificato dal client attivando la modalità AllowCertificate. Questa è solo una delle due opzioni utili (l'altra è RequireCertificate) e, nello specifico, è utile se vogliamo far sì che quella basata su certificate non sia l'unica autenticazione disponibile, per esempio vogliamo supportare anche JWT o avere degli endpoint pubblici. Se utilizzassimo RequireCertificate, invece, la richiesta in assenza di certificato verrebbe rigettata a livello di web server, prima cioè che raggiunga la nostra applicazione.
Successivamente, carichiamo in memoria il certificato a partire dal file che abbiamo generato per recuperarne la Thumbprint, e aggiungiamo uno schema di autenticazione all'interno del quale abbiamo inserito un controllo sulla Thumbprint ricevuta, per verificare che sia la stessa che ci aspettiamo.
Come ultimo passo, ovviamente, dovremo anche aggiungere i servizi di Authorization, così da poter impostare i controller come protetti, oltre che i relativi middleware.
Se abbiamo effettuato tutti i passaggi correttamente, non ci resta che marcare il nostro WeatherForecastController con l'attributo Authorize, e provare a eseguire l'applicazione.
[ApiController] [Authorize] [Route("[controller]")] public class WeatherForecastController : ControllerBase { ... }
All'apertura del browser, ci verrà richiesto di selezionare un certificate da utilizzare per il client:
Come possiamo facilmente verificare, solo utilizzando quello denominato CertDemoClient avremo effettivamente accesso all'endpoint.
Per rimuovere i certificati generati e tornare a una situazione "pulita" sul nostro sistema, possiamo utilizzare ancora PowerShell:
Get-ChildItem -Path "cert:\CurrentUser\Root\$rootThumbprint" | Remove-Item Get-ChildItem -Path "cert:\CurrentUser\My\$clientThumbprint" | Remove-Item
Un'ultima nota riguarda il fatto che questo codice è da intendersi per sole finalità di test e sviluppo, ma non è adatto per uno scenario di produzione: tipicamente, in questi casi, non utilizzeremo un Self Signed certificate, ma uno creato da una Trusted Authority, che installeremo verosimilmente in uno storage sicuro come KeyVault. Pertanto il codice per recuperarne la thumbprint e per configurare l'autenticazione sarà leggermente diverso. Ce ne occuperemo in un prossimo script.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Utilizzare QuickGrid di Blazor con Entity Framework
Creare alias per tipi generici e tuple in C#
Gestione dell'annidamento delle regole dei layer in CSS
Creare una libreria CSS universale: Immagini
Cancellare una run di un workflow di GitHub
Usare il colore CSS per migliorare lo stile della pagina
Ottimizzare il mapping di liste di tipi semplici con Entity Framework Core
Ottenere un token di accesso per una GitHub App
Miglioramenti nell'accessibilità con Angular CDK
Effettuare il refresh dei dati di una QuickGrid di Blazor
Change tracking e composition in Entity Framework
Configurare lo startup di applicazioni server e client con .NET Aspire
I più letti di oggi
- Simulare Azure Cosmos DB in locale con Docker
- Utilizzare il metodo Index di LINQ per scorrere una lista sapendo anche l'indice dell'elemento
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!
- .NET Conference Italia 2024 - Milano
- .NET Conference Italia 2023 - Milano e Online