Autenticación HTTP con Python Requests: Resolución de Errores 401

Al interactuar con APIs o servidores web mediante la biblioteca requests en Python, es común enfrentarse al código de estado HTTP 401 (No Autorizado). Este error indica que el serviddor requiere credenciales válidas para procesar la solicitud. A continuación, se detallan los escenarios más frecuentes y sus respectivas soluciones técnicas.

Escenario 1: Credenciales en la URL

En algunos navegadores, al acceder a ciertos recursos protegidos, se despliega un cuadro de diálogo nativo solicitando usuario y contraseña. A nivel de protocolo, esto suele corresponder a una autenticación básica. Una forma rápida, aunque no siempre recomendada por temas de seguridad y trazabilidad, es incrustar las credenciales directamente en la URL utilizando el formato http://usuario:contraseña@dominio.

Escenario 2: Solicitud estándar sin autenticación

Si realizamos una petición convencional a un endpoint protegido sin proporcionar mecanismos de autenticación, el servidor responderá con un error 401 y un mensaje de denegación de acceso.

import requests

def verificar_acceso_sin_credenciales():
    endpoint = "http://servidor-interno.corp/api/data"
    respuesta = requests.get(endpoint)
    
    print(f"Código de estado: {respuesta.status_code}")
    print(f"Contenido: {respuesta.text[:100]}...")

if __name__ == "__main__":
    verificar_acceso_sin_credenciales()

Solución 1: Autenticación Básica (Basic Auth)

El método más habitual es la autenticación básica, donde el usuario y la contraseña se codifican en Base64 y se envían en el encabezado Authorization. La biblioteca requests facilita esto mediante la clase HTTPBasicAuth o pasando una tupla directamente.

import requests
from requests.auth import HTTPBasicAuth

def ejecutar_peticion_basica():
    target_url = "http://servidor-interno.corp/api/data"
    username = "admin_user"
    secret_key = "SuperSecret123"
    
    # Opción A: Usando la clase explícita
    auth_handler = HTTPBasicAuth(username, secret_key)
    resp = requests.get(target_url, auth=auth_handler)
    
    # Opción B: Pasando una tupla (más conciso)
    # resp = requests.get(target_url, auth=(username, secret_key))
    
    print(f"Resultado Basic Auth: {resp.status_code}")

if __name__ == "__main__":
    ejecutar_peticion_basica()

Solución 2: Autenticación por Resumen (Digest Auth)

Si el servidor exige un nivel de seguridad ligeramente superior al básico, puede requerir autenticación por resumen (Digest). En este caso, las credenciales no viajan en texto plano, sino que se aplica un algoritmo de hash.

import requests
from requests.auth import HTTPDigestAuth

def peticion_con_digest():
    api_endpoint = "http://servidor-interno.corp/secure-area"
    usr = "admin_user"
    pwd = "SuperSecret123"
    
    digest_auth = HTTPDigestAuth(usr, pwd)
    http_resp = requests.get(api_endpoint, auth=digest_auth)
    
    print(f"Resultado Digest Auth: {http_resp.status_code}")

if __name__ == "__main__":
    peticion_con_digest()

Solución 3: Autenticación NTLM

Si tras implementar Basic o Digest Auth el servidor sigue devolviendo 401, es fundamental inspeccionar los enacbezados de la respuesta para identificar el esquema de autenticación requerido.

import requests

def inspeccionar_encabezados():
    url_objetivo = "http://servidor-interno.corp/secure-area"
    resp = requests.get(url_objetivo)
    
    print(f"Esquema requerido: {resp.headers.get('WWW-Authenticate')}")

if __name__ == "__main__":
    inspeccionar_encabezados()

Si la salida muestra algo como Negotiate, NTLM, significa que el servidor (comúnmente IIS de Microsoft) está utilizando el protocolo NTLM de Windows. Para manejar esto, es necesario instalar la extensión requests_ntlm (pip install requests_ntlm) y utilizar su manejador específico.

import requests
from requests_ntlm import HttpNtlmAuth

def autenticacion_ntlm():
    recurso = "http://servidor-interno.corp/secure-area"
    # En NTLM, a veces se requiere el dominio: 'DOMINIO\\usuario'
    cuenta = "CORP\\admin_user"
    clave = "SuperSecret123"
    
    ntlm_handler = HttpNtlmAuth(cuenta, clave)
    resultado = requests.get(recurso, auth=ntlm_handler)
    
    print(f"Estado NTLM: {resultado.status_code}")
    print(f"Headers finales: {resultado.headers}")

if __name__ == "__main__":
    autenticacion_ntlm()

Etiquetas: Python requests http-401 basic-auth digest-auth

Publicado el 7-6 17:14