Authentification dans WinHTTP
Certains serveurs ET proxys HTTP nécessitent une authentification avant d’autoriser l’accès aux ressources sur Internet. Les fonctions Microsoft Windows HTTP Services (WinHTTP) prennent en charge le serveur et l’authentification proxy pour les sessions HTTP.
À propos de l’authentification HTTP
Si l’authentification est requise, l’application HTTP reçoit un code d’état 401 (le serveur nécessite l’authentification) ou 407 (le proxy nécessite l’authentification). En plus du code d’état, le proxy ou le serveur envoie un ou plusieurs en-têtes d’authentification : WWW-Authenticate (pour l’authentification du serveur) ou Proxy-Authenticate (pour l’authentification proxy).
Chaque en-tête d’authentification contient un schéma d’authentification pris en charge et, pour les schémas De base et Digest, un domaine. Si plusieurs schémas d’authentification sont pris en charge, le serveur retourne plusieurs en-têtes d’authentification. La valeur du domaine respecte la casse et définit un ensemble de serveurs ou de proxys pour lesquels les mêmes informations d’identification sont acceptées. Par exemple, l’en-tête « WWW-Authenticate : Basic Realm="example » peut être retourné lorsque l’authentification du serveur est requise. Cet en-tête spécifie que les informations d’identification de l’utilisateur doivent être fournies pour le domaine « exemple ».
Une application HTTP peut inclure un champ d’en-tête d’autorisation avec une demande qu’elle envoie au serveur. L’en-tête d’autorisation contient le schéma d’authentification et la réponse appropriée requise par ce schéma. Par exemple, l’en-tête « Authorization : Basic <username :password>» est ajouté à la requête et envoyé au serveur si le client a reçu l’en-tête de réponse « WWW-Authenticate : Basic Realm=»example ».
Note
Bien qu’ils soient affichés ici sous forme de texte brut, le nom d’utilisateur et le mot de passe sont en fait encodés en base64.
Il existe deux types généraux de schémas d’authentification :
Schéma d’authentification de base, dans lequel le nom d’utilisateur et le mot de passe sont envoyés en texte clair au serveur.
Le schéma d’authentification de base est basé sur le modèle qu’un client doit s’identifier avec un nom d’utilisateur et un mot de passe pour chaque domaine. Le serveur services la requête uniquement si la demande est envoyée avec un en-tête d’autorisation qui inclut un nom d’utilisateur et un mot de passe valides.
Schémas de réponse aux défis, tels que Kerberos, dans lesquels le serveur défie le client avec données d’authentification. Le client transforme les données avec les informations d’identification de l’utilisateur et renvoie les données transformées au serveur pour l’authentification.
Les schémas de réponse aux défis permettent une authentification plus sécurisée. Dans un schéma de réponse aux défis, le nom d’utilisateur et le mot de passe ne sont jamais transmis sur le réseau. Une fois que le client a sélectionné un schéma de réponse aux défis, le serveur retourne un code d’état approprié avec un défi qui contient les données d’authentification pour ce schéma. Le client renvoie ensuite la demande avec la réponse appropriée pour obtenir le service demandé. Les schémas de réponse aux défis peuvent prendre plusieurs échanges.
Le tableau suivant contient les schémas d’authentification pris en charge par WinHTTP, le type d’authentification et une description du schéma.
Schéma | Type | Description |
---|---|---|
De base (texte en clair) | Basique | Utilise une chaîne encodée en base64 qui contient le nom d’utilisateur et le mot de passe. |
Digérer | Réponse aux défis | Défis liés à l’utilisation d’une valeur nonce (chaîne de données spécifiée par le serveur). Une réponse valide contient une somme de contrôle du nom d’utilisateur, du mot de passe, de la valeur nonce donnée, du verbe HTTP et de l’URI demandé. |
NTLM | Réponse aux défis | Nécessite que les données d’authentification soient transformées avec les informations d’identification de l’utilisateur pour prouver l’identité. Pour que l’authentification NTLM fonctionne correctement, plusieurs échanges doivent avoir lieu sur la même connexion. Par conséquent, l’authentification NTLM ne peut pas être utilisée si un proxy intermédiaire ne prend pas en charge les connexions keep-alive. L’authentification NTLM échoue également si WinHttpSetOption est utilisé avec l’indicateur de WINHTTP_DISABLE_KEEP_ALIVE qui désactive la sémantique de maintien en vie. |
Passeport | Réponse aux défis | Utilise Microsoft Passport 1.4. |
Négocier | Réponse aux défis | Si le serveur et le client utilisent Windows 2000 ou version ultérieure, l’authentification Kerberos est utilisée. Sinon, l’authentification NTLM est utilisée. Kerberos est disponible dans les systèmes d’exploitation Windows 2000 et ultérieurs et est considéré comme plus sécurisé que l’authentification NTLM. Pour que l’authentification Negotiate fonctionne correctement, plusieurs échanges doivent avoir lieu sur la même connexion. Par conséquent, l’authentification Negotiate ne peut pas être utilisée si un proxy intermédiaire ne prend pas en charge les connexions keep-alive. La négociation de l’authentification échoue également si WinHttpSetOption est utilisé avec l’indicateur de WINHTTP_DISABLE_KEEP_ALIVE qui désactive la sémantique de maintien en vie. Le schéma d’authentification Negotiate est parfois appelé authentification Windows intégrée. |
Authentification dans les applications WinHTTP
L’interface de programmation d’applications WinHTTP fournit deux fonctions utilisées pour accéder aux ressources Internet dans les situations où l’authentification est requise : WinHttpSetCredentials et WinHttpQueryAuthSchemes.
Lorsqu’une réponse est reçue avec un code d’état 401 ou 407, WinHttpQueryAuthSchemes peuvent être utilisés pour analyser les en-têtes d’authentification pour déterminer les schémas d’authentification pris en charge et la cible d’authentification. La cible d’authentification est le serveur ou le proxy qui demande l’authentification. WinHttpQueryAuthSchemes détermine également le premier schéma d’authentification, à partir des schémas disponibles, en fonction des préférences de schéma d’authentification suggérées par le serveur. Cette méthode pour choisir un schéma d’authentification est le comportement suggéré par RFC 2616.
WinHttpSetCredentials permet à une application de spécifier le schéma d’authentification utilisé avec un nom d’utilisateur et un mot de passe valides à utiliser sur le serveur cible ou le proxy. Après avoir défini les informations d’identification et renvoyé la requête, les en-têtes nécessaires sont générés et ajoutés automatiquement à la requête. Étant donné que certains schémas d’authentification nécessitent plusieurs transactions WinHttpSendRequest peut retourner l’erreur, ERROR_WINHTTP_RESEND_REQUEST. Lorsque cette erreur est rencontrée, l’application doit continuer à renvoyer la demande jusqu’à ce qu’une réponse ne contienne pas de code d’état 401 ou 407. Un code d’état 200 indique que la ressource est disponible et que la requête réussit. Consultez codes d’état HTTP pour obtenir des codes d’état supplémentaires qui peuvent être retournés.
Si un schéma d’authentification et des informations d’identification acceptables sont connus avant l’envoi d’une demande au serveur, une application peut appeler WinHttpSetCredentials avant d’appeler WinHttpSendRequest. Dans ce cas, WinHTTP tente de pré-authentification avec le serveur en fournissant des informations d’identification ou données d’authentification dans la demande initiale adressée au serveur. L’authentification préalable peut diminuer le nombre d’échanges dans le processus d’authentification et, par conséquent, améliorer les performances de l’application.
La pré-authentification peut être utilisée avec les schémas d’authentification suivants :
- De base - toujours possible.
- Négocier la résolution dans Kerberos - très probablement possible ; la seule exception est lorsque les décalages temporels sont désactivés entre le client et le contrôleur de domaine.
- (Négocier la résolution dans NTLM) : jamais possible.
- NTLM : possible uniquement dans Windows Server 2008 R2.
- Digest - jamais possible.
- Passeport - jamais possible ; après la réponse initiale du défi, WinHTTP utilise des cookies pour pré-authentifier auprès de Passport.
Une application WinHTTP classique effectue les étapes suivantes pour gérer l’authentification.
- Demandez une ressource avec WinHttpOpenRequest et WinHttpSendRequest.
- Vérifiez les en-têtes de réponse avec WinHttpQueryHeaders.
- Si un code d’état 401 ou 407 est retourné indiquant que l’authentification est requise, appelez WinHttpQueryAuthSchemes pour trouver un schéma acceptable.
- Définissez le schéma d’authentification, le nom d’utilisateur et le mot de passe avec WinHttpSetCredentials.
- Renvoyez la requête avec le même handle de requête en appelant WinHttpSendRequest.
Les informations d’identification définies par WinHttpSetCredentials ne sont utilisées que pour une seule requête. WinHTTP ne met pas en cache les informations d’identification à utiliser dans d’autres requêtes, ce qui signifie que les applications doivent être écrites qui peuvent répondre à plusieurs requêtes. Si une connexion authentifiée est réutilisée, d’autres requêtes peuvent ne pas être contestées, mais votre code doit pouvoir répondre à une demande à tout moment.
Exemple : récupération d’un document
L’exemple de code suivant tente de récupérer un document spécifié à partir d’un serveur HTTP. Le code d’état est récupéré de la réponse pour déterminer si l’authentification est requise. Si un code d’état 200 est trouvé, le document est disponible. Si un code d’état 401 ou 407 est trouvé, l’authentification est requise avant que le document puisse être récupéré. Pour tout autre code d’état, un message d’erreur s’affiche. Consultez codes d’état HTTP pour obtenir la liste des codes d’état possibles.
#include <windows.h>
#include <winhttp.h>
#include <stdio.h>
#pragma comment(lib, "winhttp.lib")
DWORD ChooseAuthScheme( DWORD dwSupportedSchemes )
{
// It is the server's responsibility only to accept
// authentication schemes that provide a sufficient
// level of security to protect the servers resources.
//
// The client is also obligated only to use an authentication
// scheme that adequately protects its username and password.
//
// Thus, this sample code does not use Basic authentication
// becaus Basic authentication exposes the client's username
// and password to anyone monitoring the connection.
if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE )
return WINHTTP_AUTH_SCHEME_NEGOTIATE;
else if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM )
return WINHTTP_AUTH_SCHEME_NTLM;
else if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT )
return WINHTTP_AUTH_SCHEME_PASSPORT;
else if( dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST )
return WINHTTP_AUTH_SCHEME_DIGEST;
else
return 0;
}
struct SWinHttpSampleGet
{
LPCWSTR szServer;
LPCWSTR szPath;
BOOL fUseSSL;
LPCWSTR szServerUsername;
LPCWSTR szServerPassword;
LPCWSTR szProxyUsername;
LPCWSTR szProxyPassword;
};
void WinHttpAuthSample( IN SWinHttpSampleGet *pGetRequest )
{
DWORD dwStatusCode = 0;
DWORD dwSupportedSchemes;
DWORD dwFirstScheme;
DWORD dwSelectedScheme;
DWORD dwTarget;
DWORD dwLastStatus = 0;
DWORD dwSize = sizeof(DWORD);
BOOL bResults = FALSE;
BOOL bDone = FALSE;
DWORD dwProxyAuthScheme = 0;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
// Use WinHttpOpen to obtain a session handle.
hSession = WinHttpOpen( L"WinHTTP Example/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0 );
INTERNET_PORT nPort = ( pGetRequest->fUseSSL ) ?
INTERNET_DEFAULT_HTTPS_PORT :
INTERNET_DEFAULT_HTTP_PORT;
// Specify an HTTP server.
if( hSession )
hConnect = WinHttpConnect( hSession,
pGetRequest->szServer,
nPort, 0 );
// Create an HTTP request handle.
if( hConnect )
hRequest = WinHttpOpenRequest( hConnect,
L"GET",
pGetRequest->szPath,
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
( pGetRequest->fUseSSL ) ?
WINHTTP_FLAG_SECURE : 0 );
// Continue to send a request until status code
// is not 401 or 407.
if( hRequest == NULL )
bDone = TRUE;
while( !bDone )
{
// If a proxy authentication challenge was responded to, reset
// those credentials before each SendRequest, because the proxy
// may require re-authentication after responding to a 401 or
// to a redirect. If you don't, you can get into a
// 407-401-407-401- loop.
if( dwProxyAuthScheme != 0 )
bResults = WinHttpSetCredentials( hRequest,
WINHTTP_AUTH_TARGET_PROXY,
dwProxyAuthScheme,
pGetRequest->szProxyUsername,
pGetRequest->szProxyPassword,
NULL );
// Send a request.
bResults = WinHttpSendRequest( hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
0,
0 );
// End the request.
if( bResults )
bResults = WinHttpReceiveResponse( hRequest, NULL );
// Resend the request in case of
// ERROR_WINHTTP_RESEND_REQUEST error.
if( !bResults && GetLastError( ) == ERROR_WINHTTP_RESEND_REQUEST)
continue;
// Check the status code.
if( bResults )
bResults = WinHttpQueryHeaders( hRequest,
WINHTTP_QUERY_STATUS_CODE |
WINHTTP_QUERY_FLAG_NUMBER,
NULL,
&dwStatusCode,
&dwSize,
NULL );
if( bResults )
{
switch( dwStatusCode )
{
case 200:
// The resource was successfully retrieved.
// You can use WinHttpReadData to read the
// contents of the server's response.
printf( "The resource was successfully retrieved.\n" );
bDone = TRUE;
break;
case 401:
// The server requires authentication.
printf(" The server requires authentication. Sending credentials...\n" );
// Obtain the supported and preferred schemes.
bResults = WinHttpQueryAuthSchemes( hRequest,
&dwSupportedSchemes,
&dwFirstScheme,
&dwTarget );
// Set the credentials before resending the request.
if( bResults )
{
dwSelectedScheme = ChooseAuthScheme( dwSupportedSchemes);
if( dwSelectedScheme == 0 )
bDone = TRUE;
else
bResults = WinHttpSetCredentials( hRequest,
dwTarget,
dwSelectedScheme,
pGetRequest->szServerUsername,
pGetRequest->szServerPassword,
NULL );
}
// If the same credentials are requested twice, abort the
// request. For simplicity, this sample does not check
// for a repeated sequence of status codes.
if( dwLastStatus == 401 )
bDone = TRUE;
break;
case 407:
// The proxy requires authentication.
printf( "The proxy requires authentication. Sending credentials...\n" );
// Obtain the supported and preferred schemes.
bResults = WinHttpQueryAuthSchemes( hRequest,
&dwSupportedSchemes,
&dwFirstScheme,
&dwTarget );
// Set the credentials before resending the request.
if( bResults )
dwProxyAuthScheme = ChooseAuthScheme(dwSupportedSchemes);
// If the same credentials are requested twice, abort the
// request. For simplicity, this sample does not check
// for a repeated sequence of status codes.
if( dwLastStatus == 407 )
bDone = TRUE;
break;
default:
// The status code does not indicate success.
printf("Error. Status code %d returned.\n", dwStatusCode);
bDone = TRUE;
}
}
// Keep track of the last status code.
dwLastStatus = dwStatusCode;
// If there are any errors, break out of the loop.
if( !bResults )
bDone = TRUE;
}
// Report any errors.
if( !bResults )
{
DWORD dwLastError = GetLastError( );
printf( "Error %d has occurred.\n", dwLastError );
}
// Close any open handles.
if( hRequest ) WinHttpCloseHandle( hRequest );
if( hConnect ) WinHttpCloseHandle( hConnect );
if( hSession ) WinHttpCloseHandle( hSession );
}
Stratégie d’ouverture de session automatique
La stratégie d’ouverture de session automatique (ouverture de session automatique) détermine quand il est acceptable pour WinHTTP d’inclure les informations d’identification par défaut dans une requête. Les informations d’identification par défaut sont le jeton de thread actuel ou le jeton de session selon que WinHTTP est utilisé en mode synchrone ou asynchrone. Le jeton de thread est utilisé en mode synchrone et le jeton de session est utilisé en mode asynchrone. Ces informations d’identification par défaut sont souvent le nom d’utilisateur et le mot de passe utilisés pour se connecter à Microsoft Windows.
La stratégie d’ouverture de session automatique a été implémentée pour empêcher l’utilisation occasionnelle de ces informations d’identification pour s’authentifier auprès d’un serveur non approuvé. Par défaut, le niveau de sécurité est défini sur WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM, ce qui permet aux informations d’identification par défaut d’être utilisées uniquement pour les requêtes Intranet. La stratégie d’ouverture de session automatique s’applique uniquement aux schémas d’authentification NTLM et Negotiate. Les informations d’identification ne sont jamais transmises automatiquement avec d’autres schémas.
La stratégie d’ouverture de session automatique peut être définie à l’aide de la fonctionWinHttpSetOptionavec l’indicateur WINHTTP_OPTION_AUTOLOGON_POLICY. Cet indicateur s’applique uniquement au handle de requête. Lorsque la stratégie est définie sur WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW, les informations d’identification par défaut peuvent être envoyées à tous les serveurs. Lorsque la stratégie est définie sur WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH, les informations d’identification par défaut ne peuvent pas être utilisées pour l’authentification. Il est fortement recommandé d’utiliser l’ouverture de session automatique au niveau MEDIUM.
Noms d’utilisateur et mots de passe stockés
Windows XP a introduit le concept de noms d’utilisateur et de mots de passe stockés. Si les informations d’identification Passport d’un utilisateur sont enregistrées via l’Assistant Inscription de passeport ou la boîte de dialogue d’informations d’identification standard , elle est enregistrée dans les noms d’utilisateur et mots de passe stockés. Lorsque vous utilisez WinHTTP sur Windows XP ou version ultérieure, WinHTTP utilise automatiquement les informations d’identification dans les noms d’utilisateurs et mots de passe stockés si les informations d’identification ne sont pas définies explicitement. Cela est similaire à la prise en charge des informations d’identification d’ouverture de session par défaut pour NTLM/Kerberos. Toutefois, l’utilisation des informations d’identification Passport par défaut n’est pas soumise aux paramètres de stratégie d’ouverture de session automatique.