¿Qué encoding debo usar para la Autenticación Básica HTTP?

El RFC2617 dice que debe codificar el nombre de usuario y la contraseña para base64, pero no dice qué encoding de caracteres usar al crear los octetos para ingresar en el algoritmo base64.

¿Debo asumir US-ASCII o UTF8? ¿O alguien ya ha resuelto esta pregunta en alguna parte?

Especificaciones originales – RFC 2617

RFC 2617 se puede leer como “ISO-8859-1” o “indefinido”. Tu elección. Se sabe que muchos servidores usan ISO-8859-1 (les guste o no) y fallarán cuando envíen algo más. Entonces, probablemente la única opción segura es apegarse a ASCII.

Para obtener más información y una propuesta para corregir la situación, consulte el borrador “Parámetro de encoding para autenticación básica HTTP” (que formó la base para RFC 7617).

Nuevo – RFC 7617

Desde 2015 existe RFC 7617 , que obsoleta RFC 2617. En contraste con el antiguo RFC, el nuevo RFC define explícitamente la encoding de caracteres que se utilizará para nombre de usuario y contraseña.

  • La encoding predeterminada aún no está definida. Solo se requiere que sea compatible con US-ASCII (lo que significa que asigna bytes ASCII a bytes ASCII, como lo hace UTF-8).
  • El servidor puede enviar opcionalmente un parámetro de autenticación adicional charset="UTF-8" en su desafío, como este:
    WWW-Authenticate: Basic realm="myChosenRealm", charset="UTF-8"
    Esto anuncia que el servidor aceptará caracteres que no sean ASCII en el nombre de usuario / contraseña, y que espera que estén codificados en UTF-8 (específicamente el Formulario de Normalización C). Tenga en cuenta que solo UTF-8 está permitido.

Versión completa:

Lee la especificación Si contiene detalles adicionales, como el procedimiento de encoding exacto y la lista de puntos de código Unicode que deberían ser compatibles.

Soporte del navegador

A partir de 2018, los navegadores modernos generalmente tendrán el valor predeterminado UTF-8 si un usuario ingresa caracteres que no sean ASCII como nombre de usuario o contraseña (incluso si el servidor no usa el parámetro charset ).

  • Chrome también parece usar UTF-8
  • Internet Explorer no usa UTF-8 ( problema # 11879588 )
  • Firefox está experimentando con un cambio actualmente planeado para v59 ( error 1419658 )

Reino

El parámetro de reino todavía solo admite caracteres ASCII incluso en RFC 7617.

Respuesta corta: iso-8859-1 a menos que se usen palabras codificadas de acuerdo con RFC2047 (MIME).

Explicación más larga:

RFC2617, sección 2 (Autenticación HTTP) define credenciales básicas :

 basic-credentials = base64-user-pass base64-user-pass =  user-pass = userid ":" password userid = * password = *TEXT 

La especificación no debe leerse sin consultar el RFC2616 (HTTP 1.1) para las definiciones en BNF (como la anterior):

Esta especificación es un complemento de la especificación HTTP / 1.1 2 . Utiliza la sección 2.1 de BNF aumentada de ese documento, y se basa tanto en los no terminales definidos en ese documento como en otros aspectos de la especificación HTTP / 1.1.

RFC2616, sección 2.1 define TEXTO (énfasis mío):

La regla de TEXTO solo se utiliza para los valores de contenido descriptivo y los valores que no están destinados a ser interpretados por el analizador de mensajes. Las palabras de * TEXT PUEDEN contener caracteres de juegos de caracteres que no sean ISO-8859-1 solo cuando están codificados de acuerdo con las reglas de RFC 2047.

 TEXT =  

Por lo tanto, definitivamente es iso-8859-1 a menos que detecte alguna otra encoding de acuerdo con las reglas RFC2047 (MIME pt.3 ):

 // Username: Mike // Password T€ST Mike:=?iso-8859-15?q?T€ST?= 

En este caso, el signo del euro en la palabra se codificaría como 0xA4 acuerdo con iso-8859-15 . Tengo entendido que debe verificar estos delimitadores de palabras codificadas y luego decodificar las palabras dentro de la encoding especificada. Si no lo hace, pensará que la contraseña es =?iso-8859-15?q?T¤ST?= (Observe que 0xA4 se decodificará a ¤ cuando se interpreta como iso-8859-1).

Según tengo entendido, no puedo encontrar una confirmación más explícita que estas RFC. Y algo de eso parece contradictorio. Por ejemplo, uno de los 4 objectives establecidos de RFC2047 (MIME, punto 3) es redefinir:

el formato de mensajes para permitir … información de encabezado textual en juegos de caracteres que no sean US-ASCII.

Pero luego RFC2616 (HTTP 1.1) define un encabezado usando la regla TEXTO que por defecto es iso-8859-1. ¿Eso significa que cada palabra en este encabezado debe ser una palabra codificada (es decir, la forma =?...?= )?

También es relevante, ningún navegador actual hace esto. Usan utf-8 (Chrome, Opera), iso-8859-1 (Safari), la página de códigos del sistema (IE) u otra cosa (como solo el bit más significativo de utf-8 en el caso de Firefox).

Editar: Me acabo de dar cuenta de que esta respuesta analiza el problema más desde la perspectiva del servidor.

Si está interesado en qué hacen los navegadores cuando ingresa caracteres que no son ASCII en el indicador de inicio de sesión, lo intenté con Firefox.

Parece convertir perezosamente todo en ISO-8859-1 tomando el byte menos significativo de cada valor Unicode, por ejemplo:

 User: 豚 (\u8c5a) Password: 虎 (\u864e) 

Están codificados de la misma manera que:

 User: Z (\u005a) Password: N (\u004e) 

0x5a 0x3a 0x4e base64-> WjpO

RFCs a un lado, en la estructura de Spring , la clase BasicAuthenticationFilter , el valor predeterminado es UTF-8 .

El motivo de esta elección, creo, es que UTF-8 es capaz de codificar todos los caracteres posibles, mientras que ISO-8859-1 (o ASCII) no lo es. Intentar utilizar un nombre de usuario / contraseña con caracteres no admitidos en el sistema puede provocar un comportamiento defectuoso o (quizás peor) una seguridad degradada.