アンチパターン: API プロキシ内で複数値の HTTP ヘッダーに誤った方法でアクセスする

HTTP ヘッダーは名前と値のペアです。クライアント アプリケーションとバックエンド サービスはこれを使用して、リクエストとレスポンスそれぞれの追加情報を渡すことができます。次に、簡単な例をいくつか示します。

  • Authorization リクエスト ヘッダーは、ユーザーの認証情報をサーバーに渡します。
    Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
  • Content-Type ヘッダーは、送信されるリクエストまたはレスポンスのコンテンツ タイプを示します。
    Content-Type: application/json

HTTP ヘッダーはヘッダー フィールドの定義に応じて、1 つまたは複数の値を保持します。1 つのヘッダーが複数の値を保持する場合は、値をカンマで区切って指定します。次に、複数の値を保持するヘッダーの例を示します。

  • 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 Edge では、フロー変数を使用することで、どの Edge ポリシーや条件フローからでもヘッダーに簡単にアクセスできます。次に、Edge で特定のリクエストまたはレスポンスにアクセスするための変数の一覧を示します。

フロー変数:

  • 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

JavaScript オブジェクト:

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

次の AssignMessage ポリシーの例は、リクエスト ヘッダーの値を読み取り、この値を変数に保存する方法を示します。

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

アンチパターン

Edge ポリシー内で HTTP ヘッダー値にアクセスし、最初の値だけを返す方法は誤りです。この方法では、複数の値を持つ HTTP ヘッダーの場合に問題が生じる可能性があります。

以下に、ヘッダーにアクセスするいくつかの例を示します。

例 1: JavaScript コードを使用して、複数の値を持つ Accept ヘッダーを読み取る

Accept ヘッダーが、次のような複数の値を持つとします。

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

次に、Accept ヘッダーから値を読み取る JavaScript コードの例を示します。

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

この JavaScript コードは、Accept ヘッダーから最初の値だけを返します(この例では text/html)。

例 2: AssignMessage または RaiseFault ポリシーで、複数の値を持つ Access-Control-Allow-Headers ヘッダーを読み取る

Access-Control-Allow-Headers ヘッダーが、次のような複数の値を持つとします。

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

次のコードは、Access-Control-Allow-Headers ヘッダーを設定する AssignMessage または RaiseFault ポリシーの一部を示します。

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

このコードでは、リクエスト ヘッダー Access-Control-Allow-Headers からの最初の値(この例では content-type)だけで Access-Control-Allow-Headers ヘッダーが設定されています。

影響

  1. 上記のどちらの例でも、複数の値を持つヘッダーから最初の値だけが返されていることに注目してください。これらの値がその後、なんらかの関数やロジックを実行するために、API プロキシフロー内の別のポリシーまたはバックエンド サービスによって使用されるとすると、予期しない結果につながる可能性があります。
  2. リクエスト ヘッダー値にアクセスし、ターゲット サーバーに渡される時点で、API リクエストがバックエンドによって不適切に処理され、望まない結果が生じる可能性があります。
  3. クライアント アプリケーションが Edge レスポンスからの特定のヘッダー値に依存している場合も、不適切な処理が行われ、望まない結果が生じる可能性があります。

ベスト プラクティス

  1. 次のような、適切な組み込みフロー変数を使用します。request.header.header_name.values.countrequest.header.header_name.Nresponse.header.header_name.values.countresponse.header.header_name.N

    次に、JavaScript または Java Callout ポリシー内で処理を繰り返し、特定のヘッダーからすべての値を取得します。

    例: 複数の値を持つヘッダーを読み取る JavaScript コードの例

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

    たとえば上記のコードでは、application/xml;q=0.9, */*;q=0.8 は 1 つの値のように見えます。

    セミコロンを区切り文字として使用してヘッダー値を分割しなければならない場合は、string.split(";") を使用して値を分離する必要があります。

  2. RaiseFault または AssignMessage ポリシー内で、フロー変数 request.header.header_name.values に対して substring() 関数を実行することで、ヘッダー内のすべての値を読み取ります。

    例: 複数の値を持つヘッダーを読み取る RaiseFault または AssignMessage ポリシーの例

    <Set>
          <Headers>
           <Header name="Access-Control-Allow-Headers">{substring(request.header.Access-Control-Request-Headers.values,1,-1)}</Header>
          </Headers>
        </Set>
        

関連情報