Antipatrón: acceder incorrectamente a encabezados HTTP con varios valores en un proxy de APIs

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

Los encabezados HTTP son pares de nombre y valor que permiten que las aplicaciones cliente y los servicios de backend transfieran información adicional sobre las solicitudes y las respuestas, respectivamente. Estos son algunos ejemplos sencillos:

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

Los encabezados HTTP pueden tener uno o varios valores en función de las definiciones de los campos de encabezado. Un encabezado con varios valores tendrá valores separados por comas. A continuación se muestran 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

Apigee permite a los desarrolladores acceder a los encabezados fácilmente mediante variables de flujo en cualquiera de las políticas o flujos condicionales. A continuación, se muestra la lista de variables que se pueden usar para acceder a un encabezado de solicitud o respuesta 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 un ejemplo de la política AssignMessage que muestra cómo leer el valor de un encabezado de solicitud y almacenarlo 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 forma que solo se devuelva el primer valor es incorrecto y puede causar problemas si el encabezado HTTP específico tiene más de un valor.

En las siguientes secciones se incluyen ejemplos de acceso a encabezados.

Ejemplo 1: Leer un encabezado Accept con varios valores mediante código JavaScript

Supongamos 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 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 devuelve solo el primer valor del encabezado Accept, como text/html.

Ejemplo 2: Leer un encabezado Access-Control-Allow-Headers con varios valores en una política AssignMessage o RaiseFault

Supongamos 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 la parte del código de la política AssignMessage o RaiseFault que define el encabezado Access-Control-Allow-Headers:

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

El código anterior asigna el encabezado Access-Control-Allow-Headers solo con el primer valor del encabezado de solicitud Access-Control-Allow-Headers, que en este ejemplo es content-type.

Impacto

  1. En ambos ejemplos anteriores, solo se devuelve el primer valor de los encabezados con varios valores. Si estos valores los usa posteriormente otra política en el flujo del proxy de API o el servicio backend para realizar alguna función o lógica, podría dar lugar a un resultado inesperado.
  2. Cuando se accede a los valores de los encabezados de las solicitudes y se transfieren al servidor de destino, el backend podría procesar las solicitudes a la API de forma incorrecta y, por lo tanto, podrían dar resultados incorrectos.
  3. Si la aplicación cliente depende de valores de encabezado específicos de la respuesta de Apigee, es posible que también se procese de forma incorrecta y dé resultados incorrectos.

Práctica recomendada

  1. Consulta 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 varios valores

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

    A continuación, itera para obtener todos los valores de un encabezado específico en las políticas de JavaScript o JavaCallout.

    Ejemplo: código JavaScript de muestra para leer un encabezado con 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 se mostrará 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 de encabezado deben dividirse usando el punto y coma como delimitador, puede usar string.split(";") en la llamada JavaScript para separar los distintos valores.

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

    Ejemplo: Usar substring() en una plantilla de mensaje para leer un encabezado de varios valores completo

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

Más información