¿Es seguro inyectar encabezados de autenticación personalizados?
Los encabezados de autenticación personalizados necesitan una validación a nivel de bytes antes de que una puerta de enlace los inyecte. Rechaza saltos de línea, trampas Unicode y campos duplicados.

Una puerta de enlace que inyecta credenciales debe tratar un encabezado HTTP como sintaxis de protocolo, no como un campo de texto cómodo. Si un agente, una pantalla de configuración, una importación desde la bóveda o una plantilla puede introducir un byte inesperado en ese campo, la puerta de enlace podría enviar una solicitud con un significado distinto del que sugieren la pantalla de aprobación, el registro de auditoría o el servicio de destino.
La regla segura es estricta: reserva cada campo de autenticación para la puerta de enlace, valida los bytes finales justo antes de construir la solicitud y rechaza la entrada malformada en vez de limpiarla. Así se detectan saltos de línea, trampas Unicode, controles ocultos y nombres de campo duplicados antes de que cualquier credencial salga del almacenamiento local.
El límite de inyección necesita un contrato de bytes
Un encabezado de autenticación personalizado debe tener un contrato más estricto que el análisis HTTP genérico. El análisis genérico debe admitir una internet amplia y desordenada. Una puerta de enlace que mueve un secreto desde una bóveda hasta una solicitud tiene una tarea más limitada, así que debe aceptar menos.
Empieza separando cuatro cosas que los equipos suelen agrupar bajo un único «valor de encabezado»:
- El nombre de campo configurado, como
X-Api-Key. - Los bytes secretos almacenados para ese campo.
- Los encabezados de solicitud proporcionados por el agente para una acción concreta.
- La lista final de encabezados salientes entregada al cliente HTTP.
Cada elemento tiene un propietario distinto. Un administrador o desarrollador configura el nombre. La bóveda es propietaria del secreto. El agente puede solicitar una operación HTTP y proporcionar encabezados ordinarios de la aplicación. Solo la puerta de enlace ensambla el campo protegido final.
Esta distinción importa porque un agente nunca debería recibir un secreto solo para concatenarlo con una etiqueta de encabezado. También evita un error más sutil: permitir que el agente proporcione x-api-key mientras la puerta de enlace añade después X-Api-Key. Los nombres de los campos HTTP no distinguen entre mayúsculas y minúsculas, así que son dos instancias enfrentadas del mismo campo, no dos encabezados independientes. RFC 9110 define los nombres de campo como insensibles a mayúsculas y minúsculas y describe como singleton los campos que esperan un solo miembro.
Para un encabezado de credenciales, haz explícito el contrato de bytes:
- El nombre usa únicamente el conjunto de caracteres token de HTTP y se guarda en una forma canónica en minúsculas para las comparaciones.
- El valor es una secuencia no vacía de bytes ASCII imprimibles entre
0x21y0x7e. - El valor no contiene espacios, tabulaciones, CR, LF, NUL ni ningún otro byte de control.
- La puerta de enlace añade exactamente una instancia del nombre protegido.
- Una solicitud del agente que mencione el campo protegido falla. No se edita en silencio.
Esta política rechaza deliberadamente parte del contenido legal de un campo genérico. Ese es el objetivo. Una clave API, un token bearer o una credencial de proveedor no deberían necesitar una tabulación, un espacio inicial ni un carácter acentuado para viajar de forma segura en un encabezado personalizado. Si un proveedor tiene un formato de credencial que necesita bytes arbitrarios, codifícalo antes de guardarlo usando la representación de transporte documentada por el proveedor. Base64url es habitual cuando el protocolo exige un token imprimible. No inventes una conversión con pérdida en la puerta de enlace.
La alternativa más tentadora es aceptar texto arbitrario, recortarlo, sustituir los controles por espacios y confiar en que la biblioteca HTTP rechace lo demás. Eso crea varias representaciones de una misma credencial. La interfaz puede mostrar una cadena, el registro puede contener otra y la biblioteca puede transmitir una tercera. Un control de seguridad debe decidir una vez si una acción es válida. No debe transformar la acción en otra cosa.
CR, LF y NUL deben fallar antes de construir la solicitud
Rechaza el retorno de carro (0x0d), el salto de línea (0x0a) y NUL (0x00) en cualquier posición del valor de un encabezado protegido. Rechaza un CR o un LF aislado con la misma firmeza que la conocida secuencia CRLF.
HTTP/1.1 usa CRLF para delimitar las líneas de los campos. RFC 9112 también permite que los receptores reconozcan un LF aislado en ciertas circunstancias, una tolerancia que explica precisamente por qué una puerta de enlace saliente no puede suponer que todos los componentes posteriores reaccionarán igual. La norma indica que los emisores no deben crear un CR aislado dentro de los elementos del protocolo y desaconseja las antiguas líneas de campo plegadas.
RFC 9110 es aún más claro sobre los valores de los campos: CR, LF y NUL son inválidos y peligrosos porque las implementaciones pueden analizarlos de forma distinta. Indica que los receptores deben rechazarlos o sustituirlos por espacios antes de continuar el procesamiento. La sustitución es una regla de recuperación para receptores, no un buen diseño para una puerta de enlace. La puerta de enlace sabe que está creando una solicitud nueva, así que debe rechazarla en vez de heredar la ambigüedad.
Considera un valor que llega al constructor de solicitudes así:
team-secret\r\nX-Approval: bypassed
Un validador correcto ve dos bytes prohibidos y deniega la acción. Un validador débil que solo busca los caracteres literales \\r\\n ve barras invertidas inofensivas. Uno que se ejecuta antes de expandir la plantilla puede inspeccionar ${TOKEN} y no revisar nunca el valor expandido. Otro que sustituye el salto de línea por un espacio convierte una denegación evidente en una solicitud con una credencial malformada y alterada.
La única comprobación útil opera sobre la secuencia final de bytes, después de la decodificación y la interpolación, y antes de que la biblioteca HTTP acepte los encabezados. Debería producir un resultado como este:
reject header value: field=x-api-key reason=forbidden-byte byte=0x0a offset=11
Ese mensaje es para el registro de la acción local, no para un interlocutor externo. Identifica el campo configurado y el byte defectuoso sin copiar el valor. Evita registrar una forma entre comillas, escapada en JSON, codificada en base64 o normalizada del secreto. Todas esas formas siguen siendo material secreto y tienden a sobrevivir más tiempo en los registros que la propia solicitud.
No limites esto a HTTP/1.1. HTTP/2 transporta los campos en bloques comprimidos en lugar de líneas de texto, pero RFC 9113 sigue prohibiendo NUL, LF y CR en cualquier posición del valor de un campo. Advierte específicamente que los campos no validados pueden provocar contrabando de solicitudes cuando un intermediario los traduce a HTTP/1.1. HTTP/3 señala lo mismo para los campos que después pasan a un protocolo basado en texto.
Los nombres de encabezado necesitan reglas de propiedad ASCII
Rechaza los nombres de encabezado no ASCII. No los normalices, transliteres ni intentes decidir que un parecido es «lo bastante cercano» a un nombre protegido.
Los nombres de los campos HTTP son tokens. La regla genérica excluye espacios, controles, delimitadores y caracteres no ASCII. HTTP/2 añade una regla de transporte: los nombres deben estar en minúsculas y rechaza ASCII no visible, letras mayúsculas y bytes superiores a 0x7f. Una puerta de enlace puede usar una regla aún más sencilla: valida el nombre configurado como token HTTP ASCII, conviértelo a minúsculas con reglas ASCII y conserva la escritura original solo para mostrarla.
La comparación debe basarse en bytes. Si el nombre protegido es x-api-key, todos estos nombres proporcionados por el agente deben coincidir con él después de convertir ASCII a minúsculas:
X-Api-Key
x-api-key
X-API-KEY
Estos deben fallar directamente la validación del nombre, no repararse:
x-api‐key // Unicode hyphen
x-api-kеy // Cyrillic e
x-api-key\n
x api key
El comentario visible en ese ejemplo importa. Una persona puede no distinguir entre un guion ASCII y uno Unicode, o entre una e ASCII y una е cirílica. Un analizador no debería tener que volverse experto en tipografía. Rechaza todos los nombres de encabezado no ASCII y la ambigüedad desaparece.
La propiedad es independiente de la sintaxis. Un nombre puede ser sintácticamente válido y aun así estar prohibido porque pertenece a la puerta de enlace. Mantén un conjunto de nombres protegidos en minúsculas ASCII. Comprueba cada encabezado de solicitud proporcionado por el agente contra ese conjunto antes de combinar las colecciones. Después construye desde cero la lista saliente:
encabezados ordinarios del agente que superaron la validación
+ encabezado de credenciales propiedad de la puerta de enlace
+ metadatos de solicitud propiedad de la puerta de enlace, si los hay
No empieces con la lista del agente para sobrescribir después determinadas entradas. Algunas bibliotecas HTTP conservan valores repetidos, otras los combinan y otras exponen los encabezados como un mapa que pierde la duplicación original. Cuando un campo protegido ya entró en la colección incorrecta, es demasiado tarde para confiar en una sobrescritura informal.
La normalización Unicode no es una operación de reparación
No normalices los valores de autenticación en Unicode durante la inyección. La normalización tiene un papel legítimo en sistemas que definen la igualdad textual, pero un secreto suele ser una secuencia opaca de bytes cuyo significado decide el emisor.
Las formas de normalización Unicode, especificadas por Unicode Standard Annex #15, describen transformaciones entre representaciones de texto canónica o compatiblemente equivalentes. Eso no las convierte en transformaciones seguras para una credencial. Las cadenas café y café pueden parecer idénticas aunque usen secuencias de puntos de código distintas. Un proveedor podría rechazar una, aceptar otra, aplicar un hash a una o tratarlas como secretos distintos. La puerta de enlace no puede adivinar qué comportamiento eligió el proveedor.
Hay dos preguntas de normalización independientes:
Normaliza los campos de identidad solo cuando su protocolo lo defina
Un producto puede definir cómo compara nombres de usuario, etiquetas visibles o metadatos de aplicación. Aplica esa regla en el límite donde el producto controla ese significado y conserva un registro claro de la representación elegida. No reutilices la regla para inyectar credenciales HTTP solo porque ambas entradas llegaron como cadenas.
Los nombres de encabezado son más sencillos. Son identificadores de protocolo, no prosa de usuario. Rechaza la entrada no ASCII y usa minúsculas ASCII para comparar. NFC, NFD, NFKC, NFKD, el plegado de mayúsculas Unicode y los esqueletos de caracteres confusables no tienen lugar en esa decisión.
Nunca uses coincidencias de caracteres confusables para combinar encabezados protegidos
Unicode Technical Standard #39 proporciona datos de detección de caracteres confusables para revisiones de seguridad. Indica expresamente que los mapeos de esqueletos confusables no deben convertirse en una normalización de identificadores. La advertencia es acertada: un mapeo que ayuda a marcar una etiqueta sospechosa no es una regla segura para cambiar en silencio la entrada de protocolo.
Usa la detección de confusables en una interfaz de configuración si quieres advertir que alguien escribió una etiqueta extraña. No la uses en la ruta de solicitud. Esa ruta debe decir: el nombre del campo es ASCII y está reservado, o es inválido. El valor es ASCII imprimible y exacto, o es inválido.
Aunque la política de credenciales ASCII sea estricta, sigue siendo necesario probar la normalización Unicode. Esto revela conversiones ocultas en las interfaces y las capas de almacenamiento. Introduce un valor descompuesto, su equivalente compuesto, un espacio no separable, un carácter de ancho cero y un control de derecha a izquierda en cada ruta de entrada. El resultado correcto para un valor de autenticación personalizado es el rechazo con la misma categoría genérica: byte o carácter no ASCII. El resultado no debe depender de si el texto llegó desde una pantalla de configuración, un archivo importado o un argumento del agente.
Los campos duplicados son un fallo de autorización
Para un nombre de autenticación protegido, un campo duplicado debe denegar la acción aunque ambos valores coincidan. Un único propietario es más fácil de auditar, aprobar y mucho menos dependiente del comportamiento del destino.
A menudo se afirma que los duplicados son inofensivos cuando los valores son idénticos. Eso piensa en limpieza de datos, no en autorización. Un duplicado puede aparecer antes o después de que un proxy reordene los campos. El destino puede elegir la primera instancia, la última, concatenarlas, rechazar la solicitud o aplicar una regla específica para ese campo. Un intermediario puede elegir otra opción. La aprobación de la acción deja de describir una única solicitud inequívoca.
No resuelvas esto diciendo que Authorization es especial mientras cada encabezado X-... es informal. Un proveedor puede definir su campo de credenciales personalizado como de valor único, y la mayoría de los esquemas de autenticación lo hacen. La puerta de enlace debe mantener una lista explícita de nombres protegidos por conexión o vinculación de credenciales. La lista puede incluir authorization, x-api-key, x-api-token o un campo del proveedor configurado por el desarrollador. No debe depender de una convención de nombres.
La detección de duplicados debe producirse antes de que la biblioteca cliente HTTP convierta la lista de encabezados en su propia representación. Muchas API cómodas usan un mapa de nombres insensibles a mayúsculas y minúsculas con valores de tipo lista. Eso conserva los duplicados si se usa con cuidado, pero puede ocultar el orden y la propiedad de cada fuente. Otras API ofrecen un método set que reemplaza un valor y un método add que añade otro. Esas operaciones solo son adecuadas después de que la puerta de enlace haya rechazado los intentos del agente de tocar nombres protegidos.
Usa esta tabla de decisión para cada encabezado proporcionado por el agente:
| Condición | Resultado de la puerta de enlace |
|---|---|
| Nombre de campo inválido | Denegar la acción |
| Nombre protegido en cualquier combinación ASCII de mayúsculas y minúsculas | Denegar la acción |
| Nombre ordinario duplicado cuyo protocolo de destino lo permite | Conservar solo mediante una regla explícita por nombre |
| Nombre ordinario duplicado con semántica desconocida | Denegar la acción o exigir una regla específica para la conexión |
| Campo de credenciales de la puerta de enlace | Añadir exactamente una vez después de validar los encabezados del agente |
La tercera fila es deliberadamente limitada. Accept puede tener semántica de lista. Un campo de aplicación personalizado quizá no. Si la puerta de enlace no sabe si la repetición cambia el significado, no debe inventar una regla. Aquí es donde una función genérica de «pasar todos los encabezados» se convierte silenciosamente en una forma de eludir la intención de la persona.
Valida cada límite de transformación y después el resultado final
Una sola comprobación temprana no basta porque los valores de solicitud suelen cambiar de forma después de la primera validación. Cada límite de transformación que pueda crear, decodificar, unir o sustituir bytes necesita una invariante local, y el constructor final de solicitudes necesita la validación decisiva.
Un flujo práctico tiene cinco límites:
- Cuando alguien configura el nombre del encabezado, valida su sintaxis de token ASCII y reserva su forma en minúsculas.
- Cuando un secreto entra en el almacenamiento, valida el contrato de credenciales y guarda sus bytes exactos aprobados.
- Cuando un agente proporciona una operación, valida los nombres y valores de los encabezados ordinarios de la solicitud y rechaza los nombres protegidos.
- Después de plantillas, lecturas de archivos, expansiones de entorno y decodificación de solicitudes estructuradas, vuelve a validar los valores porque esos procesos pueden haber creado bytes nuevos.
- Justo antes de llamar a la biblioteca HTTP, valida la lista completa de campos y afirma que cada nombre protegido aparece una sola vez.
La comprobación final tiene autoridad porque ve el objeto saliente real. Las comprobaciones anteriores sirven para ofrecer buenos mensajes de error y evitar que el estado incorrecto entre en el sistema. No la sustituyen.
Por eso la validación pertenece al núcleo de la puerta de enlace, no a la descripción de una herramienta MCP, a una instrucción ni a un asistente lateral del agente. Esos lugares pueden describir una solicitud prevista. No pueden garantizar qué llega al socket. La barrera que controla la inyección de credenciales debe controlar la última comprobación de bytes.
Sallyport sigue la parte útil de este límite: la aplicación realiza la inyección de credenciales HTTP, mientras el agente recibe el resultado y no el secreto. Para una credencial de encabezado personalizado, la capa de acción debe rechazar igualmente una copia del campo reservado proporcionada por el agente antes de que llegue a ese punto de inyección.
Mantén deterministas los errores de validación. Una solicitud que contiene un CR en el byte 8 y un encabezado protegido duplicado debe informar del primer fallo según un orden fijo, como nombre inválido, colisión con nombre protegido, valor inválido y después cardinalidad final. El determinismo hace fiables las pruebas y evita que un atacante use diferencias de error para aprender más sobre las credenciales almacenadas.
Haz que el validador sea lo bastante pequeño para auditarlo
Un validador corto con un contrato estricto es más seguro que un saneador general con una larga lista de excepciones. El siguiente ejemplo en Go permite deliberadamente solo bytes ASCII imprimibles en un valor de autenticación personalizado. No recorta, normaliza, decodifica ni reescribe la entrada.
package authheader
import (
"fmt"
"strings"
)
func canonicalName(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("empty field name")
}
for i := 0; i < len(name); i++ {
b := name[i]
ok := b == '!' || b == '#' || b == '$' || b == '%' || b == '&' ||
b == '\'' || b == '*' || b == '+' || b == '-' || b == '.' ||
b == '^' || b == '_' || b == '`' || b == '|' || b == '~' ||
('0' <= b && b <= '9') || ('A' <= b && b <= 'Z') ||
('a' <= b && b <= 'z')
if !ok {
return "", fmt.Errorf("invalid field-name byte 0x%02x at offset %d", b, i)
}
}
return strings.ToLower(name), nil
}
func validateCredentialValue(value string) error {
if len(value) == 0 {
return fmt.Errorf("empty credential value")
}
for i := 0; i < len(value); i++ {
b := value[i]
if b < 0x21 || b > 0x7e {
return fmt.Errorf("invalid credential byte 0x%02x at offset %d", b, i)
}
}
return nil
}
func rejectProtected(headers [][2]string, protected map[string]struct{}) error {
for _, h := range headers {
name, err := canonicalName(h[0])
if err != nil {
return err
}
if _, found := protected[name]; found {
return fmt.Errorf("agent supplied protected field %q", name)
}
}
return nil
}
Este ejemplo acepta : en un valor porque ASCII imprimible lo incluye. Es adecuado para muchos tokens de API. Si un destino acepta únicamente un subconjunto documentado, aplica esa regla del proveedor en un validador específico para la conexión. No hagas que una política global de la puerta de enlace suponga que dos puntos, barras o signos de igualdad son sospechosos.
El uso de string en Go no significa que el validador se haya vuelto consciente de Unicode. Indexar una cadena de Go devuelve bytes. Una secuencia UTF-8 multibyte contiene bytes superiores a 0x7e, por lo que este contrato la rechaza limpiamente. Si tu entorno almacena valores escalares Unicode en lugar de cadenas de bytes, codifícalos primero en la representación exacta de bytes salientes y ejecuta después la validación.
La persona que llama no debe exponer validateCredentialValue como una utilidad de limpieza. Sus únicos resultados válidos son aprobar bytes sin cambios o devolver un error. Cualquier método llamado sanitizeHeader, cleanHeader o normalizeToken merece sospecha durante una revisión, porque invita a transformar un secreto bajo la impresión de que se está protegiendo.
Una matriz de pruebas necesita representaciones hostiles, no solo cadenas hostiles
La suite de pruebas debe demostrar que todas las rutas hacia la solicitud saliente llegan al mismo validador final. No debe limitarse a llamar directamente al validador con una entrada CRLF evidente.
Usa primero pruebas unitarias basadas en tablas para los valores sin procesar:
cases := []struct {
name string
value string
want bool
}{
{"ordinary token", "mF_9.B5f-4.1JqM", true},
{"carriage return", "abc\rdef", false},
{"line feed", "abc\ndef", false},
{"crlf", "abc\r\ndef", false},
{"nul", "abc\x00def", false},
{"leading space", " abc", false},
{"trailing tab", "abc\t", false},
{"composed Unicode", "caf\u00e9", false},
{"decomposed Unicode", "cafe\u0301", false},
}
Después prueba el flujo completo de la acción con ejemplos que parecen normales para el código de la aplicación:
- Una solicitud JSON donde un salto de línea llega al decodificar
\n. - Una importación de configuración cuyo archivo termina con un salto de línea después del token.
- El resultado de una plantilla donde una variable ausente se convierte en una cadena vacía.
- Una solicitud del agente con
X-Api-Keyyx-api-keya la vez. - Una solicitud del agente que usa un carácter no ASCII parecido en el nombre y debe fallar la sintaxis del nombre, no eludir la comparación con el nombre protegido.
El caso del final de archivo detecta una costumbre real. Los desarrolladores copian un token en un archivo de texto, añaden un salto de línea final sin darse cuenta y usan una sustitución de comandos o un importador que lo conserva. Un comando de shell puede eliminar los saltos finales en una ruta, mientras un lector de archivos los conserva en otra. El comportamiento correcto de la puerta de enlace es coherente: no recorta. Pide a la persona operadora que corrija la credencial almacenada.
Añade pruebas de propiedades alrededor del constructor final. Genera cadenas cortas de bytes que contengan todos los bytes de control, bytes superiores a 0x7f y ASCII imprimible. Afirma que solo pasan las cadenas no vacías compuestas por completo por el rango permitido. Genera variantes de mayúsculas y minúsculas de cada nombre protegido y afirma que todas deniegan la propiedad al agente. Estas pruebas encuentran errores como comprobar solo \r\n juntos, olvidar un LF aislado o convertir a minúsculas después de buscar en el conjunto protegido.
Por último, usa un listener de integración que capture los encabezados que recibe realmente tu biblioteca HTTP. Nunca debería ver un valor protegido malformado, porque la puerta de enlace debe denegar primero. El listener verifica la construcción, no la política de seguridad. Si esa prueba de integración descubre un segundo campo protegido, trátalo como un defecto de la puerta de enlace aunque el servidor de destino hubiera rechazado la solicitud.
Un fallback permisivo oculta el error que necesitas corregir
Sustituir bytes prohibidos por espacios, recortar espacios en blanco o conservar el duplicado «último» es popular porque hace que las demostraciones funcionen. También dificulta reconstruir el origen de una credencial saliente.
Repasa el caso del recorte. Una persona operadora importa token-42\n desde un archivo. Una ruta lo recorta y envía token-42. Otra guarda el valor sin cambios y después lo rechaza. Ahora la persona ve que una acción funciona en una prueba local, pero falla en la puerta de enlace. Alguien acabará añadiendo el recorte a la puerta de enlace para eliminar la incoherencia. Meses después, una plantilla emite token-42\r\nX-Role: admin; el mismo código de recorte puede eliminar solo el salto de línea final y dejar intacto el CRLF interno. El comportamiento «útil» ha ocultado tanto el error de origen como el límite de seguridad.
La reparación correcta es aburrida. Define un único formato de credencial aceptado, rechaza los demás valores y haz que las herramientas de importación expliquen por qué fallan los bytes. Una pantalla de configuración puede mostrar trailing LF at offset 8 sin revelar el token. Un importador de línea de comandos puede imprimir la misma categoría y terminar con un código distinto de cero. El usuario corrige el origen en vez de enseñar a cada capa posterior a adivinar.
Los registros de auditoría deben distinguir una denegación de autorización de un fallo de transporte. Registra que el proceso del agente solicitó una acción, que la acción fue denegada antes del envío, qué vinculación de credenciales configurada estaba implicada y la categoría de validación. No llames al destino ni crees una respuesta HTTP sintética que parezca un rechazo remoto. No hubo ninguna solicitud remota.
Los registros separados de sesiones y actividades de Sallyport ofrecen un lugar útil para este tipo de denegación: la sesión muestra qué ejecución del agente lo intentó, mientras el registro de la acción puede indicar que la validación local detuvo el envío. El registro debe seguir siendo útil sin conservar el secreto rechazado ni una aproximación escapada del mismo.
La regla es estricta porque la puerta de enlace controla un secreto
Un cliente HTTP normal puede tolerar entradas amplias porque a menudo envía datos que la persona que llama ya posee. Una puerta de enlace de credenciales tiene otra responsabilidad. Decide si un agente puede provocar una acción respaldada por un secreto y debe tomar esa decisión sobre una solicitud cuya sintaxis no pueda cambiar bajo sus pies.
Rechaza controles y caracteres no ASCII en los valores de autenticación personalizados. Reserva los nombres de autenticación y compáralos con reglas de minúsculas ASCII. Rechaza los campos protegidos duplicados. Nunca normalices ni recortes una credencial al enviarla. Valida cada ruta de entrada y vuelve a validar la lista final de encabezados una última vez.
Si el formato de autenticación de un proveedor no encaja en este contrato, documenta la codificación específica del proveedor e impleméntala antes de que el secreto llegue al constructor de solicitudes. No relajes la puerta de enlace para aceptar texto ambiguo porque una integración llegó con un token desordenado. El primer valor malformado debe detenerse en la puerta, con una denegación que explique el problema de bytes y mantenga la credencial fuera de vista.
FAQ
¿Debe una puerta de enlace rechazar tanto CRLF como un salto de línea aislado en el valor de un encabezado HTTP?
Rechaza ambos. Un retorno de carro y un salto de línea pueden cambiar la forma en que un receptor HTTP/1.1 interpreta los límites de los campos, y distintos receptores han aceptado históricamente diferentes terminaciones de línea. No repares ninguno de esos caracteres en un encabezado de credenciales. Rechaza la acción antes de construir la solicitud saliente.
¿Tengo que bloquear los bytes NUL en los encabezados de autenticación personalizados?
NUL no tiene un uso legítimo en el valor de un encabezado de autenticación y HTTP lo considera inválido. Recházalo junto con CR y LF, y registra el desplazamiento del byte y la función del campo en el evento de auditoría, sin registrar el secreto.
¿Debo normalizar las claves API en Unicode antes de añadirlas a un encabezado?
No normalices una credencial durante la inyección. La normalización Unicode puede hacer que dos cadenas se comparen como iguales aunque cambien los bytes que espera el proveedor. Guarda e inyecta los bytes exactos aprobados, o exige una codificación de transporte ASCII, como base64url, antes de que el secreto entre en la bóveda.
¿Cómo comparo de forma segura los nombres duplicados de encabezados HTTP?
Trata los nombres de los campos de encabezado como tokens de protocolo ASCII y compara su forma en minúsculas ASCII. No apliques el plegado de mayúsculas y minúsculas Unicode ni NFKC a los nombres. Un nombre no ASCII debe fallar la validación, en lugar de convertirse en una versión parecida de un campo protegido.
¿Debe una puerta de enlace eliminar o rechazar un encabezado Authorization duplicado?
Para un campo de autenticación inyectado, rechaza la solicitud. Eliminar en silencio la copia proporcionada por el agente oculta un error de autorización y puede dejar a distintas capas con ideas diferentes sobre lo ocurrido. Un campo protegido debe tener un único propietario: la puerta de enlace.
¿Es seguro permitir tabulaciones o Unicode en el valor de un encabezado de clave API?
Un analizador HTTP genérico puede permitir un rango de contenido más amplio del que debería aceptar tu contrato de credenciales. Para autenticación personalizada, una política de caracteres ASCII visibles, sin espacios al principio ni al final, suele ser más fácil de probar y más segura entre versiones HTTP e intermediarios.
¿Qué casos de prueba detectan errores de inyección de encabezados HTTP?
Prueba CR aislado, LF aislado, CRLF, NUL, espacios iniciales y finales, tabulaciones, bytes no ASCII, Unicode descompuesto, Unicode compuesto y nombres duplicados con distintas mayúsculas ASCII. Ejecuta los mismos casos a través de todas las rutas de configuración, bóveda, plantillas y construcción de solicitudes.
¿Dónde debe validarse un encabezado en una puerta de enlace de agentes?
El valor debe comprobarse justo antes de que la biblioteca HTTP lo reciba, porque las comprobaciones anteriores pueden evitarse mediante cambios de decodificación, interpolación o serialización. Valida el nombre configurado cuando se guarda, pero valida la secuencia final de bytes para cada acción saliente.
¿Los saltos de línea en los encabezados solo son peligrosos para HTTP/1.1?
HTTP/2 elimina el encuadre textual CRLF usado por HTTP/1.1, pero sigue prohibiendo CR, LF y NUL en los valores de los campos. Una puerta de enlace también puede atravesar límites de protocolo mediante un proxy, por lo que la validación no puede depender de la versión de transporte actual.
¿Qué debe registrar una auditoría cuando falla la validación de un valor de encabezado?
Registra la acción como denegada, el nombre configurado del campo protegido, la categoría del rechazo y la posición del byte defectuoso. No registres el valor enviado, una copia normalizada ni una representación escapada de la credencial. La notación de escape puede convertirse en otra vía de filtración del secreto.