Teilen über


Authentifizierung in WinHTTP

Einige HTTP-Server und Proxys erfordern eine Authentifizierung, bevor der Zugriff auf Ressourcen im Internet zugelassen wird. Die Microsoft Windows HTTP Services (WinHTTP)-Funktionen unterstützen die Server- und Proxyauthentifizierung für HTTP-Sitzungen.

Informationen zur HTTP-Authentifizierung

Wenn die Authentifizierung erforderlich ist, empfängt die HTTP-Anwendung einen Statuscode von 401 (Server erfordert Authentifizierung) oder 407 (Proxy erfordert Authentifizierung). Zusammen mit dem Statuscode sendet der Proxy oder Server einen oder mehrere Authentifizierungsheader: WWW-Authenticate (für die Serverauthentifizierung) oder Proxy-Authenticate (für die Proxyauthentifizierung).

Jeder Authentifizierungsheader enthält ein unterstütztes Authentifizierungsschema und für die Schemas "Basic" und "Digest" einen Bereich. Wenn mehrere Authentifizierungsschemas unterstützt werden, gibt der Server mehrere Authentifizierungsheader zurück. Bei dem Bereichswert wird die Groß-/Kleinschreibung beachtet und eine Gruppe von Servern oder Proxys definiert, für die dieselben Anmeldeinformationen akzeptiert werden. Beispielsweise kann der Header "WWW-Authenticate: Basic Realm="example"" zurückgegeben werden, wenn die Serverauthentifizierung erforderlich ist. Dieser Header gibt an, dass Benutzeranmeldeinformationen für die Domäne "example" angegeben werden müssen.

Eine HTTP-Anwendung kann ein Autorisierungsheaderfeld mit einer Anforderung enthalten, die an den Server gesendet wird. Der Autorisierungsheader enthält das Authentifizierungsschema und die entsprechende Antwort, die für dieses Schema erforderlich ist. Beispielsweise würde der Header "Authorization: Basic <username:password>" zur Anforderung hinzugefügt und an den Server gesendet, wenn der Client den Antwortheader "WWW-Authenticate: Basic Realm="example"" empfangen hat.

Anmerkung

Obwohl sie hier als Nur-Text angezeigt werden, werden der Benutzername und das Kennwort tatsächlich base64-codiert.

 

Es gibt zwei allgemeine Arten von Authentifizierungsschemas:

  • Standardauthentifizierungsschema, in dem der Benutzername und das Kennwort im Klartext an den Server gesendet werden.

    Das Standardauthentifizierungsschema basiert auf dem Modell, das ein Client mit einem Benutzernamen und Kennwort für jeden Bereich identifizieren muss. Der Server dienstt die Anforderung nur, wenn die Anforderung mit einem Autorisierungsheader gesendet wird, der einen gültigen Benutzernamen und ein gültiges Kennwort enthält.

  • Abfrageantwortschemas, z. B. Kerberos, in denen der Server den Client mit Authentifizierungsdatenherausfordert. Der Client transformiert die Daten mit den Benutzeranmeldeinformationen und sendet die transformierten Daten zur Authentifizierung an den Server zurück.

    Abfrageantwortschemas ermöglichen eine sicherere Authentifizierung. In einem Abfrageantwortschema werden der Benutzername und das Kennwort nie über das Netzwerk übertragen. Nachdem der Client ein Abfrageantwortschema ausgewählt hat, gibt der Server einen geeigneten Statuscode mit einer Abfrage zurück, die die Authentifizierungsdaten für dieses Schema enthält. Der Client sendet die Anforderung dann erneut mit der richtigen Antwort, um den angeforderten Dienst abzurufen. Herausforderungsreaktionsschemas können mehrere Austauschmaßnahmen durchführen.

Die folgende Tabelle enthält die Authentifizierungsschemas, die von WinHTTP, dem Authentifizierungstyp und einer Beschreibung des Schemas unterstützt werden.

Schema Art Beschreibung
Einfach (Nur-Text) Grundlegend Verwendet eine base64-codierte Zeichenfolge, die den Benutzernamen und das Kennwort enthält.
Verdauen Herausforderungsantwort Herausforderungen bei der Verwendung eines Nonce-Werts (einer serverspezifischen Datenzeichenfolge). Eine gültige Antwort enthält eine Prüfsumme des Benutzernamens, des Kennworts, des angegebenen Noncewerts, des HTTP-Verbsund des angeforderten URI (Uniform Resource Identifier).
NTLM Herausforderungsantwort Erfordert, dass die Authentifizierungsdaten mit den Benutzeranmeldeinformationen transformiert werden müssen, um die Identität zu bestätigen. Damit die NTLM-Authentifizierung ordnungsgemäß funktioniert, müssen mehrere Austausche auf derselben Verbindung erfolgen. Daher kann die NTLM-Authentifizierung nicht verwendet werden, wenn ein dazwischen liegender Proxy keine Keep-Alive-Verbindungen unterstützt. Die NTLM-Authentifizierung schlägt auch fehl, wenn WinHttpSetOption- mit dem WINHTTP_DISABLE_KEEP_ALIVE-Flag verwendet wird, das die Keep-Alive-Semantik deaktiviert.
Pass Herausforderungsantwort Verwendet Microsoft Passport 1.4.
Verhandeln Herausforderungsantwort Wenn sowohl der Server als auch der Client Windows 2000 oder höher verwenden, wird die Kerberos-Authentifizierung verwendet. Andernfalls wird die NTLM-Authentifizierung verwendet. Kerberos ist in Windows 2000 und höher verfügbar und gilt als sicherer als die NTLM-Authentifizierung. Damit die Authentifizierung ordnungsgemäß funktioniert, müssen mehrere Austausche auf derselben Verbindung erfolgen. Aus diesem Grund kann die Aushandlungsauthentifizierung nicht verwendet werden, wenn ein dazwischen liegender Proxy keine Keep-Alive-Verbindungen unterstützt. Bei der Aushandlungsauthentifizierung tritt auch ein Fehler auf, wenn WinHttpSetOption- mit dem WINHTTP_DISABLE_KEEP_ALIVE-Flag verwendet wird, das die Keep-Alive-Semantik deaktiviert. Das Negotiate-Authentifizierungsschema wird manchmal als integrierte Windows-Authentifizierung bezeichnet.

 

Authentifizierung in WinHTTP-Anwendungen

Die WinHTTP-Anwendungsprogrammierschnittstelle (API) bietet zwei Funktionen für den Zugriff auf Internetressourcen in Situationen, in denen die Authentifizierung erforderlich ist: WinHttpSetCredentials und WinHttpQueryAuthSchemes.

Wenn eine Antwort mit einem Statuscode 401 oder 407 empfangen wird, können WinHttpQueryAuthSchemes- verwendet werden, um die Authentifizierungsheader zu analysieren, um die unterstützten Authentifizierungsschemas und das Authentifizierungsziel zu bestimmen. Das Authentifizierungsziel ist der Server oder Proxy, der die Authentifizierung anfordert. WinHttpQueryAuthSchemes bestimmt auch das erste Authentifizierungsschema aus den verfügbaren Schemas basierend auf den vom Server vorgeschlagenen Authentifizierungsschemaeinstellungen. Diese Methode zum Auswählen eines Authentifizierungsschemas ist das Verhalten, das von RFC 2616vorgeschlagen wird.

WinHttpSetCredentials- ermöglicht es einer Anwendung, das Authentifizierungsschema anzugeben, das zusammen mit einem gültigen Benutzernamen und Kennwort für die Verwendung auf dem Zielserver oder Proxy verwendet wird. Nachdem Sie die Anmeldeinformationen festgelegt und die Anforderung erneut senden, werden die erforderlichen Header generiert und der Anforderung automatisch hinzugefügt. Da einige Authentifizierungsschemas mehrere Transaktionen erfordern, WinHttpSendRequest den Fehler zurückgeben könnte, ERROR_WINHTTP_RESEND_REQUEST. Wenn dieser Fehler auftritt, sollte die Anwendung die Anforderung weiterhin erneut senden, bis eine Antwort empfangen wird, die keinen Statuscode 401 oder 407 enthält. Ein 200-Statuscode gibt an, dass die Ressource verfügbar ist und die Anforderung erfolgreich ist. Weitere Statuscodes, die zurückgegeben werden können, finden Sie unter HTTP-Statuscodes.

Wenn ein akzeptables Authentifizierungsschema und Anmeldeinformationen bekannt sind, bevor eine Anforderung an den Server gesendet wird, kann eine Anwendung WinHttpSetCredentials- aufrufen, bevor sie WinHttpSendRequest-aufrufen. In diesem Fall versucht WinHTTP die Vorabauthentifizierung mit dem Server, indem Anmeldeinformationen oder Authentifizierungsdaten in der anfänglichen Anforderung an den Server bereitgestellt werden. Vorauthentifizierung kann die Anzahl der Austausche im Authentifizierungsprozess verringern und somit die Anwendungsleistung verbessern.

Die Vorauthentifizierung kann mit den folgenden Authentifizierungsschemas verwendet werden:

  • Einfach - immer möglich.
  • Aushandeln der Lösung in Kerberos - sehr wahrscheinlich; Die einzige Ausnahme ist, wenn die Zeitverknungen zwischen dem Client und dem Domänencontroller deaktiviert sind.
  • (Aushandeln der Auflösung in NTLM) - nie möglich.
  • NTLM – nur in Windows Server 2008 R2 möglich.
  • Digest - nie möglich.
  • Passport - nie möglich; Nach der anfänglichen Abfrageantwort verwendet WinHTTP Cookies, um sich vor der Authentifizierung bei Passport zu authentifizieren.

Eine typische WinHTTP-Anwendung führt die folgenden Schritte aus, um die Authentifizierung zu verarbeiten.

Die von WinHttpSetCredentials festgelegten Anmeldeinformationen werden nur für eine Anforderung verwendet. WinHTTP speichert die Anmeldeinformationen nicht zwischen, die in anderen Anforderungen verwendet werden sollen. Dies bedeutet, dass Anwendungen geschrieben werden müssen, die auf mehrere Anforderungen reagieren können. Wenn eine authentifizierte Verbindung erneut verwendet wird, werden andere Anforderungen möglicherweise nicht herausgefordert, aber Ihr Code sollte jederzeit auf eine Anforderung antworten können.

Beispiel: Abrufen eines Dokuments

Der folgende Beispielcode versucht, ein angegebenes Dokument von einem HTTP-Server abzurufen. Der Statuscode wird aus der Antwort abgerufen, um festzustellen, ob die Authentifizierung erforderlich ist. Wenn ein Statuscode von 200 gefunden wird, ist das Dokument verfügbar. Wenn ein Statuscode von 401 oder 407 gefunden wird, ist die Authentifizierung erforderlich, bevor das Dokument abgerufen werden kann. Für einen anderen Statuscode wird eine Fehlermeldung angezeigt. Eine Liste möglicher Statuscodes finden Sie unter HTTP-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 );
}

Richtlinie für die automatische Anmeldung

Die Richtlinie für die automatische Anmeldung (automatische Anmeldung) bestimmt, wann winHTTP die Standardanmeldeinformationen in eine Anforderung aufnehmen kann. Die Standardanmeldeinformationen sind entweder das aktuelle Threadtoken oder das Sitzungstoken, je nachdem, ob WinHTTP im synchronen oder asynchronen Modus verwendet wird. Das Threadtoken wird im synchronen Modus verwendet, und das Sitzungstoken wird im asynchronen Modus verwendet. Diese Standardanmeldeinformationen sind häufig der Benutzername und das Kennwort, mit dem sie sich bei Microsoft Windows anmelden.

Die Richtlinie für die automatische Anmeldung wurde implementiert, um zu verhindern, dass diese Anmeldeinformationen bei einem nicht vertrauenswürdigen Server authentifiziert werden. Standardmäßig ist die Sicherheitsstufe auf WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM festgelegt, sodass die Standardanmeldeinformationen nur für Intranetanforderungen verwendet werden können. Die Richtlinie für die automatische Anmeldung gilt nur für die NTLM- und Negotiate-Authentifizierungsschemas. Anmeldeinformationen werden nie automatisch mit anderen Schemas übertragen.

Die Richtlinie für die automatische Anmeldung kann mithilfe der WinHttpSetOption--Funktion mit dem WINHTTP_OPTION_AUTOLOGON_POLICY-Flag festgelegt werden. Dieses Kennzeichen gilt nur für den Anforderungshandle. Wenn die Richtlinie auf WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW festgelegt ist, können Standardanmeldeinformationen an alle Server gesendet werden. Wenn die Richtlinie auf WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH festgelegt ist, können die Standardanmeldeinformationen nicht für die Authentifizierung verwendet werden. Es wird dringend empfohlen, die automatische Anmeldung auf mittlerer Ebene zu verwenden.

Gespeicherte Benutzernamen und Kennwörter

Windows XP hat das Konzept von gespeicherten Benutzernamen und Kennwörtern eingeführt. Wenn die Passport-Anmeldeinformationen eines Benutzers über den Passport-Registrierungs-Assistenten oder das standardmäßige Dialogfeld "Anmeldeinformationen"gespeichert werden, wird er in den gespeicherten Benutzernamen und Kennwörtern gespeichert. Bei Verwendung von WinHTTP unter Windows XP oder höher verwendet WinHTTP automatisch die Anmeldeinformationen in den gespeicherten Benutzernamen und Kennwörtern, wenn anmeldeinformationen nicht explizit festgelegt sind. Dies ähnelt der Unterstützung von Standardanmeldeinformationen für NTLM/Kerberos. Die Verwendung von Standardmäßigen Passport-Anmeldeinformationen unterliegt jedoch nicht den Richtlinieneinstellungen für die automatische Anmeldung.