Lidar com a Autenticação

Alguns proxies e servidores exigem autenticação antes de conceder acesso aos recursos na Internet. As funções WinINet dão suporte à autenticação de servidor e proxy para sessões http. A autenticação de servidores ftp deve ser tratada pela função InternetConnect . Atualmente, não há suporte para a autenticação de gateway FTP.

Sobre a autenticação HTTP

Se a autenticação for necessária, o aplicativo cliente receberá uma status código 401, se o servidor exigir autenticação ou 407, se o proxy exigir autenticação. Com o código status, o proxy ou servidor envia um ou mais cabeçalhos de resposta de autenticação – Autenticação por proxy (para autenticação de proxy) ou WWW-Authenticate (para autenticação de servidor).

Cada cabeçalho de resposta de autenticação contém um esquema de autenticação disponível e um realm. Se houver suporte para vários esquemas de autenticação, o servidor retornará vários cabeçalhos de resposta de autenticação. O valor realm diferencia maiúsculas de minúsculas e define um espaço de proteção no proxy ou no servidor. Por exemplo, o cabeçalho "WWW-Authenticate: Basic Realm="example"" seria um exemplo de um cabeçalho retornado quando a autenticação do servidor é necessária.

O aplicativo cliente que enviou a solicitação pode se autenticar incluindo um campo de cabeçalho de autorização com a solicitação. O cabeçalho Authorization conteria o esquema de autenticação e a resposta apropriada exigida por esse esquema. Por exemplo, o cabeçalho "Authorization: Basic <username:password>" seria adicionado à solicitação e reenviado ao servidor se o cliente recebesse o cabeçalho de resposta de autenticação "WWW-Authenticate: Basic Realm="example"".

Há dois tipos gerais de esquemas de autenticação:

  • Esquema de autenticação básico, em que o nome de usuário e a senha são enviados em texto não criptografado para o servidor.
  • Esquemas de resposta a desafios, que permitem um formato de resposta a desafios.

O esquema de autenticação Básico baseia-se no modelo em que um cliente deve se autenticar com um nome de usuário e senha para cada realm. O servidor atende à solicitação se ela estiver ressentida com um cabeçalho de autorização que inclui um nome de usuário e uma senha válidos.

Os esquemas de resposta a desafios permitem uma autenticação mais segura. Se uma solicitação exigir autenticação usando um esquema de desafio-resposta, o código de status apropriado e os cabeçalhos Authenticate serão retornados ao cliente. Em seguida, o cliente deve reenviar a solicitação com uma negociação. O servidor retornaria um código de status apropriado com um desafio e, em seguida, o cliente precisaria reenviar a solicitação com a resposta adequada para obter o serviço solicitado.

A tabela a seguir lista esquemas de autenticação, o tipo de autenticação, a DLL que dá suporte a eles e uma descrição do esquema.

Esquema Tipo DLL Descrição
Básico (texto não criptografado) básico Wininet.dll Usa uma cadeia de caracteres codificada em base64 que contém o nome de usuário e a senha.
Digest challenge-response Digest.dll Um esquema de desafio-resposta que desafia o uso de um valor nonce (uma cadeia de caracteres de dados especificada pelo servidor). Uma resposta válida contém uma soma de verificação do nome de usuário, da senha, do valor de nonce fornecido, do método HTTP e do URI (Uniform Resource Identifier) solicitado. O suporte à autenticação digest foi introduzido no Microsoft Internet Explorer 5.
NTLM (NT LAN Manager) challenge-response Winsspi.dll Um esquema de desafio-resposta que baseia o desafio no nome de usuário.
MSN (Microsoft Network) challenge-response Msnsspc.dll O esquema de autenticação da Microsoft Network.
DPA (Autenticação de Senha Distribuída) challenge-response Msapsspc.dll Semelhante à autenticação MSN e também é usada pela Microsoft Network.
RPA (Autenticação de Senha Remota) Compuserve Rpawinet.dll, da.dll Esquema de autenticação compuServe. Para obter mais informações, consulte Especificações do mecanismo RPA.

 

Para qualquer outra coisa que não seja a autenticação Básica, as chaves do Registro devem ser configuradas além de instalar a DLL apropriada.

Se a autenticação for necessária, o sinalizador INTERNET_FLAG_KEEP_CONNECTION deverá ser usado na chamada para HttpOpenRequest. O sinalizador INTERNET_FLAG_KEEP_CONNECTION é necessário para NTLM e outros tipos de autenticação para manter a conexão ao concluir o processo de autenticação. Se a conexão não for mantida, o processo de autenticação deverá ser reiniciado com o proxy ou servidor.

As funções InternetOpenUrl e HttpSendRequest são concluídas com êxito mesmo quando a autenticação é necessária. A diferença é que os dados retornados nos arquivos de cabeçalho e InternetReadFile receberiam uma página HTML informando ao usuário sobre o código status.

Registrando chaves de autenticação

INTERNET_OPEN_TYPE_PRECONFIG examina os valores do Registro ProxyEnable, ProxyServer e ProxyOverride. Esses valores estão localizados em HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings.

Para esquemas de autenticação diferentes do Básico, uma chave deve ser adicionada ao registro em software HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security. Um valor DWORD , Flags, deve ser definido com o valor apropriado. A lista a seguir mostra os valores possíveis para o valor Flags .

  • PLUGIN_AUTH_FLAGS_UNIQUE_CONTEXT_PER_TCPIP (value=0x01)

    Cada soquete TCP/IP (Protocolo de Controle de Transmissão) contém um contexto diferente. Caso contrário, um novo contexto será passado para cada modelo de URL de bloco ou realm.

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_UI (value=0x02)

    Essa DLL pode lidar com sua própria entrada de usuário.

  • PLUGIN_AUTH_FLAGS_CAN_HANDLE_NO_PASSWD (value=0x04)

    Essa DLL pode ser capaz de fazer uma autenticação sem solicitar uma senha ao usuário.

  • PLUGIN_AUTH_FLAGS_NO_REALM (value=0x08)

    Essa DLL não usa uma cadeia de caracteres de realm http padrão. Todos os dados que parecem ser um realm são dados específicos do esquema.

  • PLUGIN_AUTH_FLAGS_KEEP_ALIVE_NOT_REQUIRED (value=0x10)

    Essa DLL não requer uma conexão persistente para sua sequência de desafio-resposta.

Por exemplo, para adicionar a autenticação NTLM, a chave NTLM deve ser adicionada a HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security. Em HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Security\NTLM, o valor da cadeia de caracteres, DLLFile e um valor DWORD, Flags, devem ser adicionados. DLLFile deve ser definido como Winsspi.dll e Sinalizadores devem ser definidos como 0x08.

Autenticação do servidor

Quando um servidor recebe uma solicitação que requer autenticação, o servidor retorna uma mensagem de código 401 status. Nessa mensagem, o servidor deve incluir um ou mais cabeçalhos de resposta WWW-Authenticate. Esses cabeçalhos incluem os métodos de autenticação que o servidor tem disponíveis. O WinINet escolhe o primeiro método que reconhece.

A autenticação básica fornece segurança fraca, a menos que o canal seja criptografado primeiro com SSL ou PCT.

A função InternetErrorDlg pode ser usada para obter os dados de nome de usuário e senha do usuário ou uma interface do usuário personalizada pode ser projetada para obter os dados.

Uma interface personalizada pode usar a função InternetSetOption para definir os valores INTERNET_OPTION_PASSWORD e INTERNET_OPTION_USERNAME e, em seguida, reenviar a solicitação ao servidor.

Autenticação de proxy

Quando um cliente tenta usar um proxy que requer autenticação, o proxy retorna uma mensagem de código 407 status para o cliente. Nessa mensagem, o proxy deve incluir um ou mais cabeçalhos de resposta Proxy-Authenticate. Esses cabeçalhos incluem os métodos de autenticação disponíveis no proxy. O WinINet escolhe o primeiro método que reconhece.

A função InternetErrorDlg pode ser usada para obter os dados de nome de usuário e senha do usuário ou uma interface do usuário personalizada pode ser projetada.

Uma interface personalizada pode usar a função InternetSetOption para definir os valores INTERNET_OPTION_PROXY_PASSWORD e INTERNET_OPTION_PROXY_USERNAME e, em seguida, reenviar a solicitação para o proxy.

Se nenhum nome de usuário proxy e senha estiverem definidos, a WinINet tentará usar o nome de usuário e a senha do servidor. Esse comportamento permite que os clientes implementem a mesma interface de usuário personalizada usada para lidar com a autenticação do servidor.

Manipulando a autenticação HTTP

A autenticação HTTP pode ser tratada com InternetErrorDlg ou uma função personalizada que usa InternetSetOption ou adiciona seus próprios cabeçalhos de autenticação. InternetErrorDlg pode examinar os cabeçalhos associados a um identificador HINTERNET para localizar erros ocultos, como códigos de status de um proxy ou servidor. InternetSetOption pode ser usado para definir o nome de usuário e a senha para o proxy e o servidor. Para autenticação MSN e DPA, InternetErrorDlg deve ser usado para definir o nome de usuário e a senha.

Para qualquer função personalizada que adicione seus próprios cabeçalhos WWW-Authenticate ou Proxy-Authenticate, o sinalizador INTERNET_FLAG_NO_AUTH deve ser definido para desabilitar a autenticação.

O exemplo a seguir mostra como InternetErrorDlg pode ser usado para lidar com a autenticação 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.

No exemplo, dwErrorCode é usado para armazenar quaisquer erros associados à chamada para HttpSendRequest. HttpSendRequest é concluído com êxito, mesmo que o proxy ou servidor exija autenticação. Quando o sinalizador FLAGS_ERROR_UI_FILTER_FOR_ERRORS é passado para InternetErrorDlg, a função verifica se há erros ocultos nos cabeçalhos. Esses erros ocultos incluiriam quaisquer solicitações de autenticação. InternetErrorDlg exibe a caixa de diálogo apropriada para solicitar ao usuário os dados necessários. Os sinalizadores FLAGS_ERROR_UI_FLAGS_GENERATE_DATA e FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS também devem ser passados para InternetErrorDlg, para que a função construa a estrutura de dados apropriada para o erro e armazene os resultados da caixa de diálogo no identificador HINTERNET .

O código de exemplo a seguir mostra como a autenticação pode ser tratada usando 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.

Observação

O WinINet não dá suporte a implementações de servidor. Além disso, ele não deve ser usado de um serviço. Para implementações de servidor ou serviços, use Os Serviços HTTP do Microsoft Windows (WinHTTP).