Antipatrón: Accede a encabezados HTTP de valores múltiples de forma incorrecta en un proxy de API

Estás viendo la documentación de Apigee y Apigee Hybrid.
Consulta la documentación de Apigee Edge.

Los encabezados HTTP son los pares nombre-valor que permiten a las aplicaciones cliente y a los servicios de backend pasar información adicional sobre solicitudes y respuestas, respectivamente. A continuación, se muestran algunos ejemplos simples:

  • El encabezado de la solicitud de autorización pasa las credenciales del usuario al servidor:
    Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
  • El encabezado Content-Type indica el tipo de contenido de la solicitud o la respuesta que se enviará:
    Content-Type: application/json

Los encabezados HTTP pueden tener uno o más valores según las definiciones del campo del encabezado. Un encabezado con valores múltiples tendrá valores separados por comas. Estos son algunos ejemplos de encabezados que contienen varios valores:

  • Cache-Control: no-cache, no-store, must-revalidate
  • Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
  • X-Forwarded-For: 10.125.5.30, 10.125.9.125

Con Apigee, los desarrolladores pueden acceder a los encabezados de forma sencilla mediante el uso de variables de flujo en cualquiera de las políticas o de los flujos condicionales. A continuación, se muestra la lista de variables que se pueden usar para acceder a un encabezado de respuesta o de solicitud específico en Apigee:

Variables de flujo

  • message.header.header-name
  • request.header.header-name
  • response.header.header-name
  • message.header.header-name.N
  • request.header.header-name.N
  • response.header.header-name.N

Objetos de JavaScript:

  • context.proxyRequest.headers.header-name
  • context.targetRequest.headers.header-name
  • context.proxyResponse.headers.header-name
  • context.targetResponse.headers.header-name

A continuación, se muestra una política AssignMessage de ejemplo en la que se muestra la forma de leer el valor de un encabezado de solicitud y guardarlo en una variable:

<AssignMessage continueOnError="false" enabled="true" name="assign-message-default">
  <AssignVariable>
    <Name>reqUserAgent</Name>
    <Ref>request.header.User-Agent</Ref>
  </AssignVariable>
</AssignMessage>

Antipatrón

Acceder a los valores de los encabezados HTTP en las políticas de Apigee, de manera que solo se muestre el primer valor es incorrecto y puede causar problemas si el encabezado HTTP específico tienen más de un valor.

Las siguientes secciones contienen ejemplos de acceso a encabezados.

Ejemplo 1: Lee un encabezado Accept de varios valores mediante el código JavaScript

Ten en cuenta que el encabezado Accept tiene varios valores como se muestra a continuación:

Accept: text/html, application/xhtml+xml, application/xml

Este es el código de JavaScript que lee el valor del encabezado Accept:

// Read the values from Accept header
var acceptHeaderValues = context.getVariable("request.header.Accept");

El código JavaScript anterior muestra solo el primer valor del encabezado Accept, como text/html.

Ejemplo 2: Lee el encabezado de acceso-control-Allow-Headers de varios valores en la política AssignMessage o RaiseFault

Ten en cuenta que el encabezado Access-Control-Allow-Headers tiene varios valores como se muestra a continuación:

Access-Control-Allow-Headers: content-type, authorization

A continuación, se muestra el fragmento de código de la política AssignMessage o RaiseFault mediante el encabezado Access-Control-Allow-Headers:

<Set>
  <Headers>
    <Header name="Access-Control-Allow-Headers">{request.header.Access-Control-Request-Headers}</Header>
  </Headers>
</Set>

Mediante el código anterior, se establece el encabezado Access-Control-Allow-Headers con solo el primer valor del encabezado de la solicitud Access-Control-Allow-Headers, en este ejemplo content-type.

Impacto

  1. En ambos ejemplos anteriores, observa que solo se muestra el primer valor de los encabezados con valores múltiples. Si otra política del flujo del proxy de API o el servicio de backend usan estos valores posteriormente para realizar alguna función o lógica, podría generar un resultado inesperado.
  2. Cuando se accede a los valores del encabezado de solicitud y se pasan al servidor de destino, el backend podría procesar las solicitudes realizadas a la API de manera incorrecta y, por lo tanto, mostrar resultados incorrectos.
  3. Si la aplicación cliente depende de valores de encabezado específicos de la respuesta de Apigee, también puede procesarse de forma incorrecta y proporcionar resultados incorrectos.

Práctica recomendada

  1. Haz referencia a la forma request.header.header_name.values.string de la variable de flujo para leer todos los valores de un encabezado específico.

    Ejemplo: Fragmento de muestra que se podría usar en RaiseFault o AssignMessage para leer un encabezado de valores múltiples

    <Set>
      <Headers>
        <Header name="Inbound-Headers">{request.header.Accept.values.string}</Header>
      </Headers>
    </Set>
    
  2. Si deseas tener acceso individual a cada uno de los valores distintos, puedes usar las variables de flujo integradas adecuadas: request.header.header_name.values.count, request.header.header_name.N, response.header.header_name.values.count y response.header.header_name.N.

    Luego, itera para recuperar todos los valores de un encabezado específico en las políticas de texto destacado de JavaScript o Java.

    Ejemplo: Código de JavaScript de muestra para leer un encabezado de varios valores

    for (var i = 1; i <=context.getVariable('request.header.Accept.values.count'); i++)
    {
      print(context.getVariable('request.header.Accept.' + i));
    }
    

    Por ejemplo, application/xml;q=0.9, */*;q=0.8 aparecerá como dos valores con el código anterior. El primer valor es application/xml;q=0.9 y el segundo será */*;q=0.8.

    Si los valores del encabezado deben dividirse con punto y coma como delimitador, puedes usar string.split(";") dentro del texto destacado de JavaScript para separar los valores distintos.

  3. Como alternativa, puedes usar la función substring() disponible dentro de una plantilla de mensajes en la variable de flujo request.header.header_name.values para leer todos los valores de un encabezado específico.

    Ejemplo: Usa substring() dentro de una plantilla de mensajes para leer un encabezado de valores múltiples completo.

    <Set>
      <Headers>
       <Header name="Inbound-Headers">{substring(request.header.Accept.values,1,-1)}</Header>
      </Headers>
    </Set>
    

Lecturas adicionales