¿Son normales los errores HTTP 401.1 y 401.2 que veo en mis logs de IIS?
Si alguna vez os habéis molestado en analizar unos logs de IIS, quizá os hayais encontrado con el escenario en el que cuando tenemos habilitada la autenticación de Windows integrada, cada petición que hacemos realmente necesita tres peticiones para llevarse a cabo. Este es el patrón típico de la autenticación NTLM. La primera petición falla dando un error HTTP 401.2, la segunda falla también pero con un error 401.1, y la tercera es la definitiva devolviendo un resultado HTTP 200 - OK.
Os explico el porqué de esta secuencia:
SECUENCIA |
PETICIÓN |
RESPUESTA |
RESULTADO HTTP |
1 |
El cliente (por ejemplo Internet Explorer) hace una petición GET (o POST) a un servidor IIS. Esta primera petición es anónima, puesto que a priori el cliente no tiene porqué saber nada sobre este servidor: no sabe si el servidor requiere algún tipo de autenticación, no sabe qué tipos de autenticación acepta el servidor, etc. |
Supongamos que el servidor sólo tiene habilitada la autenticación de Windows integrada. Puesto que la petición que ha llegado es anónima, el servidor responde con un error HTTP 401.2 (Logon Failed due to server configuration) y le especifica al cliente los métodos de autenticación que acepta. Para este ejemplo supongamos que acepta Negotiate y NTLM. |
401.2[Logon Failed due to server configuration] |
2 |
El cliente elige autenticarse mediante NTLM (el motivo de esta elección es irrelevante para el ejemplo) y le pasa un encabezado HTTP al servidor indicando que se va a autenticar por NTLM. En este encabezado indica también la cuenta de usuario con la que se va a autenticar (pero no la contraseña). |
Para que el servidor compruebe que el cliente es quién dice ser, necesita comprobar que conoce la contraseña sin pedírsela. Para ello genera un número aleatorio de 16-byte, conocido como el “challenge” (o reto) y se lo envía al cliente. En esta segunda petición, el servidor vuelve a responder con un error HTTP 401.1 (Logon Failed) puesto que el cliente todavía no está autenticado. |
401.1[Logon Failed] |
3 |
El cliente cifra el “challenge” con un hash de la contraseña y genera lo que llamamos el “response”. De esta manera, demuestra que conoce dicha contraseña sin que la contraseña en ningún momento se intercambie por la red. Vuelve a enviar la misma petición GET por tercera (y última) vez y con el “response”. |
El servidor (que tampoco conoce la contraseña del cliente) envía los siguientes datos al controlador de dominio: · Nombre de usuario (del cliente) · El “challenge” enviado al cliente · El “response” generado por el cliente El controlador de dominio que sí conoce la contraseña del cliente, cifra el “challenge” con un hash de la contraseña del usuario igual que hizo IE y compara el resultado con el “response” que ha generado IE. Si todo es correcto, por fin se autentica al usuario y se le envía la respuesta definitiva (HTTP 200 –OK). |
200[OK] |
Una vez hemos visto el motivo de este intercambio de peticiones y respuestas HTTP, más de uno se pregunta ¿esto no genera un excesivo tráfico de red? Las respuesta es que depende. Evidentemente, el protocolo de autenticación NTLM genera más tráfico de red que un sitio web configurado con autenticación anónima, pero el tráfico adicional normalmente no supone un problema.
Por un lado, todos los clientes HTTP que soportan autenticación NTLM implementan una optimización, que implica que en la segunda petición del handshake NTLM únicamente se incluyen los encabezados HTTP (y por lo tanto se excluye el HTTP entity-body, es decir los datos asociados a la petición en caso de que los haya). Os pongo un ejemplo, si la petición en cuestión es un POST de un fichero de 100MB, el primer POST anónimo (junto con los 100MB de fichero adjunto) va a fallar con un error 401.1. Cuando el cliente decide autenticarse por NTLM, hace un segundo POST pero esta vez sin entity-body (es decir, sin los 100MB de fichero adjunto) dado que ya sabe que esta petición también va a fallar inevitablemente con un error 401.1. En la última petición, de nuevo volvemos a incluir el entity-body, que será la petición definitiva y cuando finalmente logremos subir el fichero al servidor HTTP.
Adicionalmente, Internet Explorer (en adelante IE) incluye una característica conocida como pre-autenticación NTLM que evita realizar la primera petición anónima una vez determina que un servidor en concreto acepta autenticación NTLM. La pre-autenticación NTLM directamente envía el encabezado HTTP de autenticación por NTLM (paso 2 de la secuencia) indicando al servidor el nombre de usuario para que este genere el “challenge”.
Esta optimización puede dar problemas si una vez iniciado este comportamiento de IE, vamos a parar a un directorio virtual en el mismo sitio web que no admite autenticación NTLM (sino que únicamente utiliza autenticación anónima). En este caso, IE va a hacer una petición enviando los encabezados HTTP (especificando autenticación por NTLM) esperando un 401.1 por parte del servidor. Puesto que IE “cree” que la petición definitiva va a ser la siguiente, elige no mandar los datos del entity-body para ahorrar ancho de banda. Puesto que el servidor no acepta autenticación NTLM pero si acepta autenticación anónima, ignora el encabezado HTTP de autenticación y responde con un HTTP 200 definitivo. Esto provoca que, por ejemplo, los datos del formulario HTML (o el fichero de 100MB del ejemplo anterior) nunca se lleguen a enviar con el POST. El siguiente artículo habla precisamente de este problema:
You cannot post data to a non-NTLM-authenticated Web site
https://support.microsoft.com/kb/251404/en-us
Si tenemos una aplicación ASP.NET que realiza llamadas a un web service, se autentica por NTLM y queremos evitar la primera petición anónima, podemos implementar la pre-autenticación NTLM de la siguiente manera:
MyWebService proxy = new MyWebService();
proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
proxy.PreAuthenticate = true;
proxy.HelloWorld();
Por último, si queremos reducir aún más el ancho de banda causado por la autenticación NTLM, podemos reducir los Custom Errrors que devuelve IIS para los errores HTTP 401.1 y 401.2.
Happy hacking.
- Daniel Mossberg