【Azure】App Serviceでリクエストヘッダをキャプチャしてプロトコルバージョンなど詳細を確認するTech TIPS

Azure App ServiceでWeb/APIを運用していて、クライアントが送信するリクエストヘッダから情報を得たい、と思ったことはないだろうか? PHPを前提として、リクエストヘッダをログに記録して集計する手順を紹介する。

» 2025年04月16日 05時00分 公開
[島田広道デジタルアドバンテージ]
「Tech TIPS」のインデックス

連載目次

Azure App Serviceでリクエストヘッダをキャプチャ

対象:Azure App Service

 Azureの「App Service」でWebサイト/APIサーバを運用していて、クライアントデバイスからアクセスされたときのHTTPリクエストヘッダから詳細な情報を得たい、と思ったことはないだろうか?

 例えば、クライアントからHTTPSで接続される際にTLSのバージョンが知りたいということはないだろうか? こうしたプロトコルのバージョンなどの情報は、App Serviceのデフォルトでは記録されないことが多く、その実態は把握しにくい。

 しかし、中にはリクエストヘッダに記載されているものもある。そのヘッダをログに記録して集計できれば、頻度などを確認できる。

 そこで本Tech TIPSでは、Webサーバの構築/運用担当者を対象として、Azure App Serviceがアクセスされたときのリクエストヘッダを記録して、その頻度を集計する手順を紹介する。

 対象はPHPスタックを選択しているサイトとする。情報の抽出にはAzureのログ分析サービス「Log Analytics」を用いる。Log Analyticsの構築や基本的な使い方、またリクエストヘッダの基本仕様の説明については割愛させていただく。

手順1――リクエストヘッダをエラーログに記録するコードを既存のPHPファイルに埋め込む

 PHPには、「getallheaders」という全リクエストヘッダを取得する関数がある。これを使うと、リクエストヘッダをJSON形式でエラーログに記録できる。

 Webサイト上でよくクライアントからアクセスされるPHPファイルを選び、そこに下記のコードを追加する。これで、クライアントから対象のPHPファイルがリクエストされるたびに、そのリクエストヘッダがエラーログに記録されるようになる。

<?php
<前略>

// ---------- 以下を挿入 ----------
// リクエストヘッダをエラーログに記録する関数
function logRequestHeaders(array $headerKeys = []) {
  // 全リクエストヘッダを取得
  $allReqHeaders = (array)getallheaders();

  if (empty($headerKeys)) {
    $reqHeaders = $allReqHeaders; // 未指定時は全ヘッダを格納
  } else {
    $reqHeaders = array_intersect_key(
      $allReqHeaders, // 全ヘッダ
      array_flip($headerKeys) // 抽出するヘッダ名一覧
    );
  }
  
  // 該当するヘッダが1つもない場合に返すJSONで初期化
  $resultsJSON = '{"RequestHeaders":{}}';

  if (!empty($reqHeaders)) {
    try {
      $resultsJSON = json_encode([ // 該当ヘッダをJSONに変換
        'RequestHeaders' => $reqHeaders,
      ], JSON_THROW_ON_ERROR);
    } catch (JsonException $ex) {
      // 必要なら、JSONエンコード失敗時の処理をここに挿入
    }
  }

  error_log($resultsJSON); // PHPのエラーログにJSONを記録
}
// ---------- 挿入終わり ----------

<PHPの実行が始まるところ>

// ---------- 以下を挿入 ----------
logRequestHeaders([]); // 最初: 全リクエストヘッダをエラーログに記録
//logRequestHeaders(['X-Forwarded-Tlsversion']); // 後: 負荷軽減のため、特定ヘッダのみ記録
// ---------- 挿入終わり ----------

<後略>

【PHP】リクエストヘッダの内容をエラーログに出力する
レファレンス: getallheadersarray_intersect_keyarray_flipjson_encodeJSON_THROW_ON_ERROR定数error_log

 リクエストヘッダを記録する関数「logRequestHeaders」で、特定のヘッダのみ記録できるようにしているのは、全ヘッダを記録する負荷が意外と重く、メインの処理が遅延することがあったためだ。

 そこで最初は全ヘッダを記録して、その中から必要なヘッダを決めた後、logRequestHeadersにその名前の一覧を配列で渡すことで、記録するヘッダを絞り込んだ。これにより負荷は軽減され、目立った遅延を抑えることができた。

 さて、App Serviceの場合、PHPのエラーログはAzure Monitorの「AppServiceConsoleLogs」というログに、エラーレベル「Error」で記録される。その「ResultDescription」キーに、前述したリクエストヘッダがJSON形式で記録されているはずだ。以下はその例である。

NOTICE: PHP message: {"RequestHeaders":{"X-Client-Port":"15780","X-Client-Ip":"VVV.WWW.YYY.ZZZ",<中略>,"Accept":"*\/*","Content-Length":"","Content-Type":""}}

Azure Monitorの「AppServiceConsoleLogs」ログに記録されたリクエストヘッダ(例)

 このログの先頭の「NOTICE: PHP message:」を除去しつつJSONのフォーマットを整えたのが以下のリストだ。

{
  "RequestHeaders": {
    "X-Client-Port": "15780",
    "X-Client-Ip": "VVV.WWW.YYY.ZZZ",
    "X-Waws-Unencoded-Url": "\/path\/to\/api.php?key1=value1&key2=value2",
    "X-Original-Url": "\/path\/to\/api.php?key1=value1&key2=value2",
    "X-Forwarded-For": "VVV.WWW.YYY.ZZZ:15780",
    "X-Forwarded-Tlsversion": "1.3",
    "X-Arr-Ssl": "2048|256|CN=FujiSSL Public Validation Authority - G3, O=\"SECOM Trust Systems CO.,LTD.\", C=JP|CN=www.example.jp",
    "X-Appservice-Proto": "https",
    "X-Forwarded-Proto": "https",
    "Was-Default-Hostname": "app-fwin2ksample-dev-je-044.azurewebsites.net",
    "X-Site-Deployment-Id": "app-fwin2ksample-dev-je-044",
    "Disguised-Host": "www.example.jp",
    "Client-Ip": "VVV.WWW.YYY.ZZZ:15780",
    "X-Arr-Log-Id": "e48d7767-fe75-48a9-b661-5de412e5924d",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Site": "same-origin",
    "Referer": "https:\/\/www.example.jp\/?id=company1",
    "Max-Forwards": "10",
    "Accept-Language": "ja",
    "Accept-Encoding": "gzip, deflate, br",
    "User-Agent": "Mozilla\/5.0 (iPhone; CPU iPhone OS 18_3_1 like Mac OS X) AppleWebKit\/605.1.15 (KHTML, like Gecko) GSA\/358.1.731895952 Mobile\/15E148 Safari\/604.1",
    "Host": "www.example.jp",
    "Accept": "*\/*",
    "Content-Length": "",
    "Content-Type": ""
  }
}

【JSON】インデントなどフォーマットを整えたリクエストヘッダのJSON出力(例)
例示のため、全ヘッダを記録してみた。また、IPアドレスやホスト名、パスなどは架空のものに置き換えている。

 「X-」から始まる名前のリクエストヘッダは、主にApp Service内蔵のロードバランサーが付加したものだ。ここからクライアントデバイスとの接続に関する情報が幾つか読み取れる。

 例えば、「X-Forwarded-Tlsversion」ヘッダには、クライアントとのHTTPS接続におけるTLS(SSL)のバージョンが格納されている。また「X-Arr-Ssl」ヘッダには、TLSに用いられたサーバ証明書の公開鍵長や署名アルゴリズムの鍵長、発行元、発行先がそれぞれ格納されているように読み取れる。

手順2――エラーログをLog Analyticsでクエリして回数や頻度を集計する

 前述のログはAzure Monitorで記録されているため、その分析サービスであるLog Analyticsで抽出できる。ここでは、クライアントがHTTPSで接続する際のTLSのバージョンを例に、その方法を紹介する。Log Analyticsはあらかじめセットアップ済みとする(セットアップ方法については割愛させていただく)。

 以下のKustoのクエリを実行すると、TLSのバージョンごとのリクエスト数が出力される。

AppServiceConsoleLogs 
// リクエストヘッダのJSON出力のログだけに絞り込む
| where ResultDescription startswith "NOTICE: PHP message: {\"RequestHeaders\":"
// 先頭の「NOTICE: ~」=21文字を除いたJSONのみを取得
| extend reqHeaderJSON = substring(ResultDescription, 21)
// JSONからTLSバージョンを抽出し、浮動小数点に変換
| extend TLSVersion = extract_json("$.RequestHeaders['X-Forwarded-Tlsversion']", reqHeaderJSON, typeof(real))
// 集計
| summarize RequestCount = count() by TLSVersion

【Kusto】AppServiceConsoleLogsに出力されたリクエストヘッダのJSONからTLSバージョンを抽出して集計する
レファレンス: startswith演算子extend 演算子substringextract_jsonsummarize 演算子
TLSバージョンを集計するクエリをAzureポータルで実行したところ TLSバージョンを集計するクエリをAzureポータルで実行したところ
下側の表が集計結果で、「RequestCount」がTLSのバージョンごとのリクエスト数を表している。この例では圧倒的にTLS 1.3での接続が多いことが分かる。また、全く接続がなかったTLS 1.1以下はもとより、TLS 1.2も廃れてきていることが伺える。

 KustoでJSONをパースするには、上記リストにある「extract_json」の他、「parse_json」も利用できる。ただ、後者は全てのキーをパースする分、クエリが重くなる傾向がある。1つのキーのみ抽出するなら、これらの関数を利用するより、文字列のまま正規表現で抽出した方が、それほどクエリを複雑化させることなくクエリの負荷を軽減できるかもしれない。

 TLSバージョン以外を集計したい場合は、前述のPHPコードと上記クエリにある「X-Forwarded-Tlsversion」の部分を該当のリクエストヘッダ名に書き換えつつ、必要に応じて「extract_json」の第3パラメーターを「typeof(string)」などの型に変更すればよい。

■関連リンク

「Tech TIPS」のインデックス

Tech TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

スポンサーからのお知らせPR

Windows Server Insider 記事ランキング

本日月間

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。