Scenari WCF Security (Parte 1 – anonymous security)

WCF permette di implementare diversi scenari di sicurezza più o meno diffusi semplicemente editando i file di configurazione del client e del service. Tutto è apparentemente molto semplice, ma ciò che avviene dietro la facciata non lo è affatto, e per rendersene conto basta dare un’occhiata ai messaggi SOAP effettivamente scambiati. Lo scopo di questa serie di post è di esplorare gli scenari di security più usati implementabili con WCF, andando a vedere come le diverse configurazioni si riflettono nella composizione dei messaggi.

Nessun meccanismo di security

Questo è lo scenario più semplice: infatti non abbiamo alcun meccanismo di autenticazione/autorizzazione e i messaggi viaggiano in chiaro; i client non hanno bisogno di fornire delle credenziali. L’adozione di questo tipo di scenario non è mai raccomandabile, se non in condizioni molto particolari: bisogna tenere presente infatti che i dati viaggiano in chiaro e possono quindi essere facilmente letti (sniffing) da chiunque, se non addirittura manomessi (tampering). Conclusione: sappiate che è possibile disabilitare la security, ma non fatelo mai! Crying face

Anonymous application

E’ possibile configurare client e service al fine di consentire l’accesso anonimo a quest’ultimo: in sostanza il client non fornisce le sue credenziali e di conseguenza non sarà possibile identificarlo. Tutto ciò non significa che non ci sia bisogno di proteggere i messaggi scambiati tra client e service: è infatti comunque raccomandabile preservare sempre l’integrità e la riservatezza dei dati.
Ma quando è possibile utilizzare un simile scenario? Indifferentemente in internet o in una intranet, quando non c’è alcun bisogno di identificare o autorizzare i client, perché l’applicazione non lo richiede (per esempio: un servizio gratuito che fornisce il cambio euro/dollaro aggiornato: che valore aggiunto può avere identificare i client? E’ sempre prudente comunque proteggere i dati: qualcuno potrebbe farci lo scherzo di intercettare e modificare il valore di ritorno del servizio, con consegunze più o meno pesanti.)

L’anonymous security può funzionare sia con l’HTTP che con il TCP, e se prevediamo la presenza di vari intermediari tra i nostri client e il service, si capisce che il meccanismo di sicurezza più adatto è la message security. I binding WCF che ci permettono di configurare la message security con l’anonymous access sono i seguenti: WSHttpBinding, WSDualHttpBinding, NetTcpBinding, NetMsmqBinding; attenzione: non è possibile implementare questo scenario con il basicHttpBinding, NetNamedPipeBinding, NetPeerTcpBinding o WSFederationHttpBinding, in quanto alcuni non supportano la mancanza delle credenziali del client, altri non supportano la message security.

Riepilogando, in questo scenario il client contatta il servizio senza fornire le sue credenziali, mentre il service si autentica con il client e protegge e critta i dati nello stesso tempo con l’ausilio di un certificato.

Ora cercherò di mostrare un po’ gli aspetti pratici, mostrando un esempio di configurazione: il WSHttpBinding con message security e anonymous access. Il binding va configurato come segue:

 <wsHttpBinding>
   <binding name="Binding1">
     <security mode = "Message">
       <message clientCredentialType="None"/>
     </security>
   </binding>
 </wsHttpBinding>

Come si può notare, stiamo impostando la message security con il clientCredentialType impostato a “None”, che vuol dire semplicemente che il client non invierà mai le sue credenziali.

Il servizio deve tuttavia utilizzare un certificato, al fine di autenticare se stesso e proteggere i dati: senza voler entrare troppo in dettaglio nell’argomento “certificati”, è sufficiente sapere che il servizio utilizza un certificato dotato di chiave privata, la quale può essere utilizzata nel seguente modo:

1) Integrità dei dati: il trasmittente firma i messaggi inviati con la sua chiave privata, mentre il ricevente utilizza la chiave pubblica per validare la firma. Se qualcuno manomette il messaggio, il controllo della firma fallirà e il messaggio sarà scartato. In alternativa, le suddette chiavi possono essere utilizzate per scambiarsi in modo protetto una chiave simmetrica, da utilizzare in seguito per la firma dei dati.

2) Riservatezza dei dati: il trasmittente utilizza la chiave pubblica per crittare i dati, assicurandosi così che solo il possessore della chiave privata potrà leggerli. In alternativa, le suddette chiavi possono essere utilizzate per scambiarsi in modo protetto una chiave simmetrica, da utilizzare in seguito per la cifratura dei dati.

3) Autenticazione del servizio: il meccanismo funziona perché il controllo della firma va a buon fine soltanto se il messaggio è stato firmato con quella determinata chiave privata (assumendo che non sia stato manomesso naturalmente), dandoci così la certezza del mittente.

In questo caso verrà utilizzata una chiave simmetrica che il client e il service si scambiano in modo protetto.

Il certificato lato servizio può essere impostato in questo modo:

 <serviceBehaviors>
   <behavior name="myServiceBehavior">
     <serviceCredentials>
       <serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
     </serviceCredentials>
   </behavior>
 </serviceBehaviors>

Nota: Il certificato dovrebbe normalmente avere il nome del server, mentre nell’esempio ho impostato “localhost”.

Il client deve essere in grado di validare il certificato del server, perché altrimenti non potrebbe essere certo di sapere se esso è veramente chi dice di essere: a tal fine i certificati sono sempre firmati dalle cosiddette “certification authority”, degli enti riconosciuti che garantiscono sull’autenticità dei certificati. Non è il caso di entrare troppo in dettaglio sui certificati (non è un post sui certificati questo, ma se l’argomento interessa potrei preparare un bel post), tuttavia non potevo ignorarli visto che è necessario dire al client cosa fare con il certificato del servizio. Lo facciamo in questo modo:

 <endpointBehaviors>
   <behavior name="ClientCredentialsBehavior">
     <clientCredentials>
       <serviceCertificate>
          <authentication certificateValidationMode="PeerOrChainTrust" />
       </serviceCertificate>
     </clientCredentials>
   </behavior>
 </endpointBehaviors>

In questo modo stiamo dicendo al client di validare il certificato del server in questo modo: se il certificato è firmato da una certification authority che conosciamo, oppure se il certificato è presente nello store Trusted People. Questa impostazione viene normalmente usata negli ambienti di test, quando il certificato che stiamo usando è stato prodotto in casa solo per testare in qualche modo la nostra configurazione. L’impostazione di default sarebbe “ChainTrust”, in base alla quale il client valida il certificato server soltanto se è firmato da una certification authority riconosciuta.

Adesso non vi resta che fare una prova, magari prendendo spunto da questo esempio: Message Security Anonymous.