Verificatie in WinHTTP
Voor sommige HTTP-servers en proxy's is verificatie vereist voordat toegang tot resources op internet wordt toegestaan. De Functies Microsoft Windows HTTP Services (WinHTTP) ondersteunen server- en proxyverificatie voor HTTP-sessies.
Over HTTP-verificatie
Als verificatie is vereist, ontvangt de HTTP-toepassing een statuscode van 401 (server vereist verificatie) of 407 (proxy vereist verificatie). Naast de statuscode verzendt de proxy of server een of meer verificatieheaders: WWW-Authenticate (voor serververificatie) of Proxy-Authenticate (voor proxyverificatie).
Elke verificatieheader bevat een ondersteund verificatieschema en, voor de Basic- en Digest-schema's, een realm. Als er meerdere verificatieschema's worden ondersteund, retourneert de server meerdere verificatieheaders. De realmwaarde is hoofdlettergevoelig en definieert een set servers of proxy's waarvoor dezelfde referenties worden geaccepteerd. De kop WWW-Authenticate: Basic Realm="example" kan bijvoorbeeld worden geretourneerd wanneer serververificatie is vereist. Deze header geeft aan dat gebruikersreferenties moeten worden opgegeven voor het 'voorbeeld'-domein.
Een HTTP-toepassing kan een autorisatieheaderveld bevatten met een aanvraag die naar de server wordt verzonden. De autorisatieheader bevat het verificatieschema en het juiste antwoord dat voor dat schema is vereist. De header 'Authorization: Basic <username:password>' wordt bijvoorbeeld toegevoegd aan de aanvraag en verzonden naar de server als de client de antwoordheader WWW-Authenticate: Basic Realm="example" heeft ontvangen.
Notitie
Hoewel ze hier worden weergegeven als tekst zonder opmaak, worden de gebruikersnaam en het wachtwoord eigenlijk base64 gecodeerd.
Er zijn twee algemene typen verificatieschema's:
Basisverificatieschema, waarin de gebruikersnaam en het wachtwoord in duidelijke tekst naar de server worden verzonden.
Het basisverificatieschema is gebaseerd op het model dat een client zichzelf moet identificeren met een gebruikersnaam en wachtwoord voor elke realm. De server services de aanvraag alleen als de aanvraag wordt verzonden met een autorisatieheader die een geldige gebruikersnaam en een geldig wachtwoord bevat.
Challenge-response-schema's, zoals Kerberos, waarin de server de client uitdagt met verificatiegegevens. De client transformeert de gegevens met de gebruikersreferenties en verzendt de getransformeerde gegevens terug naar de server voor verificatie.
Challenge-response-schema's maken een veiligere verificatie mogelijk. In een challenge-response-schema worden de gebruikersnaam en het wachtwoord nooit via het netwerk verzonden. Nadat de client een challenge-response-schema heeft geselecteerd, retourneert de server een juiste statuscode met een uitdaging die de verificatiegegevens bevat voor dat schema. De client verzendt de aanvraag vervolgens opnieuw met de juiste reactie om de aangevraagde service te verkrijgen. Met challenge-response-schema's kunnen meerdere uitwisselingen worden voltooid.
De volgende tabel bevat de verificatieschema's die worden ondersteund door WinHTTP, het verificatietype en een beschrijving van het schema.
Plan | Type | Beschrijving |
---|---|---|
Basic (tekst zonder opmaak) | Basisch | Maakt gebruik van een base64-gecodeerde tekenreeks die de gebruikersnaam en het wachtwoord bevat. |
Verteren | Uitdaging-antwoord | Uitdagingen bij het gebruik van een niet-cewaarde (een door de server opgegeven gegevensreeks). Een geldig antwoord bevat een controlesom van de gebruikersnaam, het wachtwoord, de opgegeven niet-cewaarde, het HTTP-werkwoorden de aangevraagde URI (Uniform Resource Identifier). |
NTLM | Uitdaging-antwoord | Vereist dat de verificatiegegevens worden getransformeerd met de gebruikersreferenties om identiteit te bewijzen. Voor een correcte werking van NTLM-verificatie moeten verschillende uitwisselingen plaatsvinden op dezelfde verbinding. Daarom kan NTLM-verificatie niet worden gebruikt als een tussenliggende proxy geen keep alive-verbindingen ondersteunt. NTLM-verificatie mislukt ook als WinHttpSetOption- wordt gebruikt met de vlag WINHTTP_DISABLE_KEEP_ALIVE waarmee keep-alive-semantiek wordt uitgeschakeld. |
Paspoort | Uitdaging-antwoord | Gebruikt Microsoft Passport 1.4. |
Onderhandelen | Uitdaging-antwoord | Als zowel de server als de client Windows 2000 of hoger gebruikt, wordt Kerberos-verificatie gebruikt. Anders wordt NTLM-verificatie gebruikt. Kerberos is beschikbaar in Windows 2000- en hogerbesturingssystemen en wordt beschouwd als veiliger dan NTLM-verificatie. Voor het correct functioneren van negotiate-verificatie moeten verschillende uitwisselingen plaatsvinden op dezelfde verbinding. Onderhandelen over verificatie kan daarom niet worden gebruikt als een tussenliggende proxy geen keep-alive-verbindingen ondersteunt. Onderhandelen over verificatie mislukt ook als WinHttpSetOption- wordt gebruikt met de vlag WINHTTP_DISABLE_KEEP_ALIVE waarmee keep-alive-semantiek wordt uitgeschakeld. Het verificatieschema Negotiate wordt ook wel geïntegreerde Windows-verificatie genoemd. |
Verificatie in WinHTTP-toepassingen
De WinHTTP application programming interface (API) biedt twee functies die worden gebruikt voor toegang tot internetbronnen in situaties waarin verificatie is vereist: WinHttpSetCredentials en WinHttpQueryAuthSchemes.
Wanneer een antwoord wordt ontvangen met een 401- of 407-statuscode, kan WinHttpQueryAuthSchemes worden gebruikt om de verificatieheaders te parseren om de ondersteunde verificatieschema's en het verificatiedoel te bepalen. Het verificatiedoel is de server of proxy die verificatie aanvraagt. WinHttpQueryAuthSchemes bepaalt ook het eerste verificatieschema, op basis van de beschikbare schema's, op basis van de voorkeuren voor verificatieschema's die door de server worden voorgesteld. Deze methode voor het kiezen van een verificatieschema is het gedrag dat wordt voorgesteld door RFC 2616.
WinHttpSetCredentials stelt een toepassing in staat om het verificatieschema op te geven dat samen met een geldige gebruikersnaam en een geldig wachtwoord wordt gebruikt voor gebruik op de doelserver of proxy. Nadat u de referenties hebt ingesteld en de aanvraag opnieuw hebt verzonden, worden de benodigde headers automatisch gegenereerd en toegevoegd aan de aanvraag. Omdat voor sommige verificatieschema's meerdere transacties zijn vereist WinHttpSendRequest de fout kan retourneren, ERROR_WINHTTP_RESEND_REQUEST. Wanneer deze fout optreedt, moet de toepassing de aanvraag blijven verzenden totdat een antwoord wordt ontvangen dat geen 401- of 407-statuscode bevat. Een statuscode van 200 geeft aan dat de resource beschikbaar is en dat de aanvraag is geslaagd. Zie HTTP-statuscodes voor aanvullende statuscodes die kunnen worden geretourneerd.
Als een acceptabel verificatieschema en referenties bekend zijn voordat een aanvraag naar de server wordt verzonden, kan een toepassing WinHttpSetCredentials aanroepen voordat WinHttpSendRequest-wordt aangeroepen. In dit geval probeert WinHTTP vooraf te verifiëren met de server door referenties of verificatiegegevens op te geven in de eerste aanvraag naar de server. Pre-verificatie kan het aantal uitwisselingen in het verificatieproces verminderen en de prestaties van toepassingen verbeteren.
Verificatie vooraf kan worden gebruikt met de volgende verificatieschema's:
- Basic - altijd mogelijk.
- Onderhandelen over het oplossen van oplossingen in Kerberos - zeer waarschijnlijk; de enige uitzondering is wanneer de tijdsverschil tussen de client en de domeincontroller is uitgeschakeld.
- (Onderhandelen over oplossen in NTLM) - nooit mogelijk.
- NTLM - alleen mogelijk in Windows Server 2008 R2.
- Digest - nooit mogelijk.
- Paspoort - nooit mogelijk; na de eerste uitdagingsreactie gebruikt WinHTTP cookies om zich vooraf bij Passport te verifiëren.
Een typische WinHTTP-toepassing voert de volgende stappen uit om verificatie af te handelen.
- Vraag een resource aan met WinHttpOpenRequest en WinHttpSendRequest.
- Controleer de antwoordheaders met WinHttpQueryHeaders.
- Als er een 401- of 407-statuscode wordt geretourneerd die aangeeft dat verificatie is vereist, roept u WinHttpQueryAuthSchemes- aan om een acceptabel schema te vinden.
- Stel het verificatieschema, de gebruikersnaam en het wachtwoord in met WinHttpSetCredentials.
- Verzend de aanvraag opnieuw met dezelfde aanvraaghandgreep door WinHttpSendRequestaan te roepen.
De referenties die zijn ingesteld door WinHttpSetCredentials worden slechts gebruikt voor één aanvraag. WinHTTP slaat de referenties die moeten worden gebruikt in andere aanvragen niet in de cache op. Dit betekent dat toepassingen moeten worden geschreven die op meerdere aanvragen kunnen reageren. Als een geverifieerde verbinding opnieuw wordt gebruikt, kunnen andere aanvragen mogelijk niet worden betwist, maar moet uw code op elk gewenst moment op een aanvraag kunnen reageren.
Voorbeeld: Een document ophalen
Met de volgende voorbeeldcode wordt geprobeerd een opgegeven document op te halen van een HTTP-server. De statuscode wordt opgehaald uit het antwoord om te bepalen of verificatie is vereist. Als er een 200-statuscode wordt gevonden, is het document beschikbaar. Als er een statuscode van 401 of 407 wordt gevonden, is verificatie vereist voordat het document kan worden opgehaald. Voor elke andere statuscode wordt een foutbericht weergegeven. Zie HTTP-statuscodes voor een lijst met mogelijke statuscodes.
#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 );
}
Beleid voor automatische aanmelding
Het beleid voor automatische aanmelding (automatisch aanmelden) bepaalt wanneer winHTTP de standaardreferenties in een aanvraag kan opnemen. De standaardreferenties zijn het huidige threadtoken of het sessietoken, afhankelijk van of WinHTTP wordt gebruikt in de synchrone of asynchrone modus. Het threadtoken wordt gebruikt in de synchrone modus en het sessietoken wordt gebruikt in de asynchrone modus. Deze standaardreferenties zijn vaak de gebruikersnaam en het wachtwoord die worden gebruikt om u aan te melden bij Microsoft Windows.
Het beleid voor automatisch aanmelden is geïmplementeerd om te voorkomen dat deze referenties terloops worden gebruikt om te verifiëren bij een niet-vertrouwde server. Standaard is het beveiligingsniveau ingesteld op WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM, waardoor de standaardreferenties alleen kunnen worden gebruikt voor intranetaanvragen. Het beleid voor automatisch aanmelden is alleen van toepassing op de NTLM- en Negotiate-verificatieschema's. Referenties worden nooit automatisch verzonden met andere schema's.
Het beleid voor automatisch aanmelden kan worden ingesteld met behulp van de functie WinHttpSetOption met de vlag WINHTTP_OPTION_AUTOLOGON_POLICY. Deze vlag is alleen van toepassing op de aanvraag-handle. Wanneer het beleid is ingesteld op WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW, kunnen standaardreferenties naar alle servers worden verzonden. Wanneer het beleid is ingesteld op WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH, kunnen standaardreferenties niet worden gebruikt voor verificatie. Het wordt ten zeerste aanbevolen dat u de automatische aanmelding op MEDIUM-niveau gebruikt.
Opgeslagen gebruikersnamen en wachtwoorden
In Windows XP is het concept opgeslagen gebruikersnamen en wachtwoorden geïntroduceerd. Als de Passport-referenties van een gebruiker worden opgeslagen via de wizard Passport-registratie of het standaarddialoogvenster Referentie, wordt deze opgeslagen in de opgeslagen gebruikersnamen en wachtwoorden. Wanneer u WinHTTP gebruikt in Windows XP of hoger, gebruikt WinHTTP automatisch de referenties in de opgeslagen gebruikersnamen en wachtwoorden als referenties niet expliciet zijn ingesteld. Dit is vergelijkbaar met de ondersteuning van standaardaanmeldingsreferenties voor NTLM/Kerberos. Het gebruik van standaard Passport-referenties is echter niet onderhevig aan de instellingen voor het automatische aanmeldingsbeleid.