認証の処理

一部のプロキシとサーバーでは、インターネット上のリソースへのアクセスを許可する前に認証が必要です。 WinINet 関数は、http セッションのサーバー認証とプロキシ認証をサポートします。 ftp サーバーの認証は 、InternetConnect 関数によって処理する必要があります。 現在、FTP ゲートウェイ認証はサポートされていません。

HTTP 認証について

認証が必要な場合、クライアント アプリケーションは、サーバーで認証が必要な場合は状態コード 401 を受け取り、プロキシで認証が必要な場合は 407 を受け取ります。 状態コードを使用すると、プロキシまたはサーバーから 1 つ以上の認証応答ヘッダー (プロキシ認証用) またはWWW-Authenticate (サーバー認証用) が送信されます。

各認証応答ヘッダーには、使用可能な認証スキームと領域が含まれています。 複数の認証スキームがサポートされている場合、サーバーは複数の認証応答ヘッダーを返します。 領域の値では大文字と小文字が区別され、プロキシまたはサーバー上の保護領域が定義されます。 たとえば、ヘッダー "WWW-Authenticate: Basic Realm="example" は、サーバー認証が必要な場合に返されるヘッダーの例です。

要求を送信したクライアント アプリケーションは、要求に Authorization ヘッダー フィールドを含めることで、自身を認証できます。 Authorization ヘッダーには、認証スキームとそのスキームに必要な適切な応答が含まれます。 たとえば、クライアントが認証応答ヘッダー "WWW-Authenticate: Basic Realm="example" を受信した場合、ヘッダー "Authorization: Basic <username:password>" が要求に追加され、サーバーに再送信されます。

認証スキームには、次の 2 つの一般的な種類があります。

  • 基本認証スキーム。ユーザー名とパスワードがクリアテキストでサーバーに送信されます。
  • チャレンジ応答スキーム。チャレンジ応答形式を使用できます。

基本認証スキームは、クライアントが各領域のユーザー名とパスワードを使用して自身を認証する必要があるモデルに基づいています。 サーバーは、有効なユーザー名とパスワードを含む Authorization ヘッダーを使用して要求を再送信した場合に、要求を処理します。

チャレンジ応答スキームを使用すると、より安全な認証が可能になります。 要求でチャレンジ応答スキームを使用した認証が必要な場合は、適切な状態コードと認証ヘッダーがクライアントに返されます。 その後、クライアントはネゴシエートを使用して要求を再送信する必要があります。 サーバーはチャレンジを含む適切な状態コードを返し、クライアントは要求されたサービスを取得するために適切な応答で要求を再送信する必要があります。

次の表に、認証スキーム、認証の種類、それらをサポートする DLL、およびスキームの説明を示します。

Scheme Type [DLL] 説明
Basic (クリアテキスト) basic Wininet.dll ユーザー名とパスワードを含む base64 でエンコードされた文字列を使用します。
ダイジェスト challenge-response Digest.dll nonce (サーバー指定のデータ文字列) 値を使用してチャレンジするチャレンジ応答スキーム。 有効な応答には、ユーザー名、パスワード、指定された nonce 値、HTTP メソッド、および要求された Uniform Resource Identifier (URI) のチェックサムが含まれます。 ダイジェスト認証のサポートは、Microsoft インターネット エクスプローラー 5 で導入されました。
NT LAN Manager (NTLM) challenge-response Winsspi.dll チャレンジをユーザー名に基づくチャレンジ応答スキーム。
Microsoft Network (MSN) challenge-response Msnsspc.dll Microsoft Network の認証スキーム。
分散パスワード認証 (DPA) challenge-response Msapsspc.dll MSN 認証と同様に、Microsoft ネットワークでも使用されます。
リモート パスフレーズ認証 (RPA) Compuserve Rpawinet.dll、da.dll CompuServe 認証スキーム。 詳細については、 RPA メカニズムの仕様に関するページを参照してください。

 

基本認証以外の場合は、適切な DLL をインストールするだけでなく、レジストリ キーを設定する必要があります。

認証が必要な場合は、HttpOpenRequest の呼び出しで INTERNET_FLAG_KEEP_CONNECTION フラグを使用する必要があります。 認証プロセスの完了中に接続を維持するには、NTLM やその他の種類の認証には、INTERNET_FLAG_KEEP_CONNECTION フラグが必要です。 接続が維持されない場合は、プロキシまたはサーバーを使用して認証プロセスを再起動する必要があります。

認証が必要な場合でも、 InternetOpenUrl 関数と HttpSendRequest 関数は正常に完了します。 違いは、ヘッダー ファイルと InternetReadFile で返されるデータは、ユーザーに状態コードを通知する HTML ページを受け取る点です。

認証キーの登録

INTERNET_OPEN_TYPE_PRECONFIG、 レジストリ値 ProxyEnableProxyServerProxyOverride を確認します。 これらの値は、[HKEY_CURRENT_USER\Software\ Microsoft Windows CurrentVersionInternet Settings]\(Microsoft\Windows\CurrentVersion\ インターネット設定\) の下にあります。

Basic 以外の認証スキームの場合は、HKEY_LOCAL_MACHINESOFTWARE\Microsoft\ Internet エクスプローラー\Security の下のレジストリにキー\追加する必要があります。 DWORDFlags は、適切な値で設定する必要があります。 次の一覧は、 Flags 値に使用できる値を示しています。

  • PLUGIN_AUTH_FLAGS_UNIQUE_CONTEXT_PER_TCPIP (value=0x01)

    各伝送制御プロトコル/インターネット プロトコル (TCP/IP) ソケットには、異なるコンテキストが含まれています。 それ以外の場合は、領域またはブロック URL テンプレートごとに新しいコンテキストが渡されます。

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_UI (value=0x02)

    この DLL は、独自のユーザー入力を処理できます。

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_NO_PASSWD (value=0x04)

    この DLL は、ユーザーにパスワードの入力を求めることなく認証を実行できる場合があります。

  • PLUGIN_AUTH_FLAGS_NO_REALM (value=0x08)

    この DLL では、標準の http 領域文字列は使用されません。 領域と思われるデータは、スキーム固有のデータです。

  • PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED (value=0x10)

    この DLL では、チャレンジ応答シーケンスに永続的な接続は必要ありません。

たとえば、NTLM 認証を追加するには、キー NTLM を HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet エクスプローラーSecurity に追加する\必要があります。 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet エクスプローラー\Security\NTLM] で、文字列値 DLLFileDWORDFlags を追加する必要があります。 DLLFile を Winsspi.dll に設定し、 フラグ を 0x08 に設定する必要があります。

[サーバー認証]

サーバーが認証を必要とする要求を受信すると、サーバーは 401 状態コード メッセージを返します。 そのメッセージには、サーバーに 1 つ以上のWWW-Authenticate応答ヘッダーが含まれている必要があります。 これらのヘッダーには、サーバーが使用できる認証方法が含まれます。 WinINet は、認識する最初のメソッドを選択します。

基本認証では、チャネルが SSL または PCT で最初にリンク暗号化されていない限り、脆弱なセキュリティが提供されます。

InternetErrorDlg 関数を使用して、ユーザーからユーザー名とパスワードのデータを取得することも、カスタマイズされたユーザー インターフェイスを使用してデータを取得することもできます。

カスタム インターフェイスでは 、InternetSetOption 関数を使用して 、INTERNET_OPTION_PASSWORDINTERNET_OPTION_USERNAME の値を設定し、要求をサーバーに再送信できます。

プロキシ認証

クライアントが認証を必要とするプロキシを使用しようとすると、プロキシは 407 状態コード メッセージをクライアントに返します。 そのメッセージには、プロキシに 1 つ以上のProxy-Authenticate応答ヘッダーが含まれている必要があります。 これらのヘッダーには、プロキシから使用できる認証方法が含まれます。 WinINet は、認識する最初のメソッドを選択します。

InternetErrorDlg 関数を使用して、ユーザーからユーザー名とパスワード データを取得することも、カスタマイズしたユーザー インターフェイスを設計することもできます。

カスタム インターフェイスでは 、InternetSetOption 関数を使用して 、INTERNET_OPTION_PROXY_PASSWORDINTERNET_OPTION_PROXY_USERNAME の値を設定し、要求をプロキシに再送信できます。

プロキシ ユーザー名とパスワードが設定されていない場合、WinINet はサーバーのユーザー名とパスワードの使用を試みます。 この動作により、クライアントは、サーバー認証の処理に使用されるのと同じカスタマイズされたユーザー インターフェイスを実装できます。

HTTP 認証の処理

HTTP 認証は、 InternetErrorDlg または InternetSetOption を使用するカスタマイズされた関数、または独自の認証ヘッダーを追加することで処理できます。 InternetErrorDlg では 、HINTERNET ハンドルに関連付けられているヘッダーを調べて、プロキシやサーバーの状態コードなどの非表示のエラーを見つけることができます。 InternetSetOption を使用して、プロキシとサーバーのユーザー名とパスワードを設定できます。 MSN および DPA 認証の場合は、ユーザー名とパスワードを設定するために InternetErrorDlg を使用する必要があります。

独自のWWW-AuthenticateまたはProxy-Authenticate ヘッダーを追加するカスタマイズされた関数の場合は、認証を無効にするために INTERNET_FLAG_NO_AUTH フラグを設定する必要があります。

次の例は、 InternetErrorDlg を使用して HTTP 認証を処理する方法を示しています。

HINTERNET hOpenHandle,  hConnectHandle, hResourceHandle;
DWORD dwError, dwErrorCode;
HWND hwnd = GetConsoleWindow();

hOpenHandle = InternetOpen(TEXT("Example"),
                           INTERNET_OPEN_TYPE_PRECONFIG, 
                           NULL, NULL, 0);

hConnectHandle = InternetConnect(hOpenHandle,
                                 TEXT("www.server.com"), 
                                 INTERNET_INVALID_PORT_NUMBER,
                                 NULL,
                                 NULL, 
                                 INTERNET_SERVICE_HTTP,
                                 0,0);

hResourceHandle = HttpOpenRequest(hConnectHandle, TEXT("GET"),
                                  TEXT("/premium/default.htm"),
                                  NULL, NULL, NULL, 
                                  INTERNET_FLAG_KEEP_CONNECTION, 0);

resend:

HttpSendRequest(hResourceHandle, NULL, 0, NULL, 0);

// dwErrorCode stores the error code associated with the call to
// HttpSendRequest.  

dwErrorCode = hResourceHandle ? ERROR_SUCCESS : GetLastError();

dwError = InternetErrorDlg(hwnd, hResourceHandle, dwErrorCode, 
                           FLAGS_ERROR_UI_FILTER_FOR_ERRORS | 
                           FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
                           FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
                           NULL);

if (dwError == ERROR_INTERNET_FORCE_RETRY)
    goto resend;

// Insert code to read the data from the hResourceHandle
// at this point.

この例では、dwErrorCode を使用して 、HttpSendRequest の呼び出しに関連するエラーを格納します。 プロキシまたはサーバーで認証が必要な場合でも、HttpSendRequest は正常に完了します。 FLAGS_ERROR_UI_FILTER_FOR_ERRORS フラグが InternetErrorDlg に渡されると、関数はヘッダーで非表示のエラーがないかを確認します。 これらの非表示エラーには、認証の要求が含まれます。 InternetErrorDlg は、ユーザーに必要なデータの入力を求める適切なダイアログ ボックスを表示します。 FLAGS_ERROR_UI_FLAGS_GENERATE_DATAフラグとFLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS フラグも InternetErrorDlg に渡す必要があります。この関数は、エラーに適したデータ構造を構築し、ダイアログ ボックスの結果を HINTERNET ハンドルに格納します。

次のコード例は、 InternetSetOption を使用して認証を処理する方法を示しています。

HINTERNET hOpenHandle,  hResourceHandle, hConnectHandle;
DWORD dwStatus;
DWORD dwStatusSize = sizeof(dwStatus);
char strUsername[64], strPassword[64];

// Normally, hOpenHandle, hResourceHandle,
// and hConnectHandle need to be properly assigned.

hOpenHandle = InternetOpen(TEXT("Example"),
                           INTERNET_OPEN_TYPE_PRECONFIG,
                           NULL, NULL, 0);
hConnectHandle = InternetConnect(hOpenHandle,
                                 TEXT("www.server.com"),
                                 INTERNET_INVALID_PORT_NUMBER,
                                 NULL,
                                 NULL,
                                 INTERNET_SERVICE_HTTP,
                                 0,0);

hResourceHandle = HttpOpenRequest(hConnectHandle, TEXT("GET"),
                                  TEXT("/premium/default.htm"),
                                  NULL, NULL, NULL,
                                  INTERNET_FLAG_KEEP_CONNECTION,
                                  0);

resend:

HttpSendRequest(hResourceHandle, NULL, 0, NULL, 0);

HttpQueryInfo(hResourceHandle, HTTP_QUERY_FLAG_NUMBER |
              HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize, NULL);

switch (dwStatus)
{
    // cchUserLength is the length of strUsername and
    // cchPasswordLength is the length of strPassword.
    DWORD cchUserLength, cchPasswordLength;

    case HTTP_STATUS_PROXY_AUTH_REQ: // Proxy Authentication Required
        // Insert code to set strUsername and strPassword.

        // Insert code to safely determine cchUserLength and
        // cchPasswordLength. Insert appropriate error handling code.
        InternetSetOption(hResourceHandle,
                          INTERNET_OPTION_PROXY_USERNAME,
                          strUsername,
                          cchUserLength+1);

        InternetSetOption(hResourceHandle,
                          INTERNET_OPTION_PROXY_PASSWORD,
                          strPassword,
                          cchPasswordLength+1);
        goto resend;
        break;

    case HTTP_STATUS_DENIED:     // Server Authentication Required.
        // Insert code to set strUsername and strPassword.

        // Insert code to safely determine cchUserLength and
        // cchPasswordLength. Insert error handling code as
        // appropriate.
        InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME,
                          strUsername, cchUserLength+1);
        InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD,
                          strPassword, cchPasswordLength+1);
        goto resend;
        break;
}

// Insert code to read the data from the hResourceHandle
// at this point.

注意

WinINet では、サーバーの実装はサポートされていません。 また、サービスから使用しないでください。 サーバーの実装またはサービスの場合は、 Microsoft Windows HTTP サービス (WinHTTP) を使用します。