この記事について
作業だけでなく、この記事の文章も AI(Claude)がまとめています。内容は実際の作業ログと実測値に基づいていますが、公開前に管理人が目を通しています。
このサイトには長いあいだ、道具が2つだけ置いてありました。IPアドレス確認とwhois 検索です。
アクセスを数えてみたら、この2つが記事より読まれていました。1ページあたりの価値は道具の方が高い、ということです。
それで増やしました。10個足して、全部で12個です。置き場は /tools/ にまとめました。
前回までのサーバ入れ替え・テーマの作り直しに続いて、これも作業はほぼ AI(Claude Code)にやらせています。
ネットワークとサーバを調べる
DNS 検索
/tools/dns/ で A・AAAA・CNAME・MX・NS・TXT・SOA を引きます。日本語ドメインもそのまま入れられます(内部で xn-- の形に直してから引きます)。URL を貼り付けても、ホスト名だけを拾います。
HTTPヘッダー取得
/tools/http-header/ は、サイトが返すヘッダーを見ます。特徴はリダイレクトを1ホップずつ全部見せることです。http から https へ、さらに www ありへ、と2段3段になっている構成の確認に使えます。
SSL証明書の確認
/tools/ssl/ でホスト名を入れると、発行者・有効期間・対象ドメイン(SAN)の一覧・署名アルゴリズムを出します。残り日数も出して、30日を切っていたら警告します。期限切れの証明書でも中身を読めるように、検証を通さず取得しています。
セキュリティヘッダー診断
/tools/security-headers/ は、HSTS・CSP・X-Frame-Options・Referrer-Policy・Permissions-Policy・Cookie の属性などを ○△× で採点して A〜E を出します。
作った直後に自分のサイトを測ったら C(61点)でした。この話は後半に書きます。
ポート開放確認
/tools/port/ は、いまアクセスしている回線のポートが外から見えているかを調べます。ルーターの設定を変えたあとの確認用です。
宛先は入力できません。自分の回線に固定してあります。理由は後述します。
メールを調べる
メールヘッダー解析
/tools/mail-header/ に、メールソフトの「ソースを表示」で出てくるヘッダーを貼り付けると、配送経路を送信側から順に並べ、Authentication-Results から SPF・DKIM・DMARC の判定を抜き出します。
件名が =?UTF-8?B?…?= の形になっていても読める形に戻します。貼り付けた内容はサーバに保存しません。
メール認証レコード診断
/tools/mail-auth/ はその逆で、自分のドメインが正しく送れる設定になっているかを見ます。SPF・DKIM・DMARC・MX をまとめて診断します。
力を入れたのは SPF の DNS ルックアップ数を数えるところです。SPF は include: を辿った回数が10回を超えると受信側が permerror にしてよいと決まっています(RFC 7208)。「設定してあるのに通らない」の主要な原因がこれです。このツールは include を再帰的に展開して、いま何回かを表示します。
DKIM には面倒な事情があります。DNS には「そのドメインにどのセレクタがあるか」を一覧する仕組みがありません。だからよく使われる名前(default、google、selector1 など12個)を順に当てにいきます。出てこない場合は、実際に届いたメールの DKIM-Signature ヘッダーにある s= の値を入力欄に入れてください。鍵長も出します(RSA 1024 bit なら短すぎます)。
計算と変換(ブラウザの中だけで動きます)
ここから3つは、入力した内容がサーバに送られません。ページを開いた時点で計算する仕組みごとブラウザに渡っていて、あとは手元で完結します。こちらのログにも残りません。
サブネット計算
/tools/subnet/ に 192.168.1.10/26 のように入れると、ネットワークアドレス・ブロードキャスト・使えるホストの範囲・ホスト数・2進表記を出します。10.0.0.0 255.255.0.0 のようなネットマスク表記でも、IPv6 でも通ります。
おまけで分割ができます。/26 を /28 に割ると4つに分かれて、それぞれのホスト範囲まで並びます。拠点にアドレスを配るときに使うやつです。
変換ツール
/tools/convert/ に5つをタブでまとめました。Base64、URLエンコード、日本語ドメイン(punycode)、Unix時間、ハッシュ(MD5 / SHA-1 / SHA-256 / SHA-512 / CRC32)です。
日本語ドメインは、DNS 上では xn-- で始まる形で登録されています。どちらを入れても両方の形を出します。Unix時間はメールの Date: 形式(RFC 2822)でも出すので、上のメールヘッダー解析と並べて使えます。
cron 式の確認
/tools/cron/ に 30 4 * * 0 のように入れると、日本語で読み解いて次に動く時刻を10回ぶん並べます。
これを作った理由は、「日」と「曜日」を両方指定したときの挙動です。cron はこのとき AND ではなく OR で動きます。0 4 1 * 0 は「毎月1日かつ日曜」ではなく「毎月1日または毎週日曜」です。間違えやすいので、両方指定を見つけたら警告を出します。
IPアドレス確認も中身を作り直した
残った2つのうち、IPアドレス確認は表示内容を作り直しました。IPアドレスと逆引きだけでなく、ブラウザが送ってきている情報も出すようにしています。いわゆる「診断くん」の系統です。
出しているのは、User-Agent・言語・受け付ける形式・圧縮方式・参照元・追跡拒否(DNT)、それに最近のブラウザが送る Sec-CH-UA 系。経路として、HTTPS かどうか、プロトコルの版、CDN を経由しているかも出します。
加えて、サーバには届かない情報をブラウザ側で拾って並べています。画面の解像度、表示領域、デバイス比率、色深度、タイムゾーン、ブラウザの言語、Cookie の可否、論理 CPU 数、ダークモードかどうか。「自分が何を見せているか」がひと目で分かります。
ただし、環境変数を総なめにしてはいけない
この手のページは、サーバの環境変数をループで全部出すのがいちばん簡単です。それをやってはいけません。
環境変数には、公開ディレクトリの実際のパスや、サーバの内部的な設定値が混ざります。構成によっては、外に出してはいけない値がそのまま画面に出ます。ループで書くと、あとからサーバ側に何かを足したときに、気づかないまま増えた分まで表示されるのも怖いところです。
なので出す項目を明示的に列挙する形にしました。増やすときは1行足す。足していないものは絶対に出ない。地味ですが、ここは横着してはいけない場所です。
X-Forwarded-For は「末尾」が本物
もうひとつ罠があります。このサイトは CDN を挟んでいるので、アクセス元の IP は X-Forwarded-For というヘッダーで渡ってきます。このヘッダーは訪問者が自分で付けて送れます。CDN は受け取った値の末尾に本物を足すので、先頭を信じると「訪問者が好きな IP を表示させられる」ことになります。
実際に確かめたところ、こちらが付けた偽の値がそのまま前に並び、末尾に本物が足されていました。なので末尾を本物として扱い、申告された値は別枠で「偽装できる値」と明記して出しています。
作るときに決めたこと
この種の道具は、作り方を間違えると他人を攻撃する踏み台になります。3つ決めました。
ポート確認の対象は、アクセス元の回線に固定する。宛先を入力させた瞬間に、ただのポートスキャナになります。
ping と traceroute は作らない。同じ理由です。
メールを送る機能は作らない。送信テストは便利ですが、第三者に送れるとスパムの踏み台になり、送信ドメインの評判が落ちます。メール系はすべて DNS を引くだけにしました。
サーバから外に出るツール(HTTPヘッダー取得・SSL証明書・セキュリティヘッダー診断)には、名前解決した結果の全 IP を検査して、内部向けなら拒否する処理を入れています。リダイレクトも1ホップごとに検査し直します。クラウドのメタデータや社内アドレスを覗かせないためです。
踏んだ罠
WordPress が JavaScript を壊す
今回いちばん時間を取られたのがこれです。
計算・変換・cron の3つは、ブラウザの中だけで動きます。最初はショートコードが返す HTML の中に <script> を直接書いていました。
これが丸ごと動きませんでした。
原因は、WordPress が本文の中の裸の & を & に書き換えることです。つまり if (a && b) が別の文字列になり、構文エラーでスクリプト全体が実行されません。
たちが悪いのは、サーバ側に何のエラーも出ないことです。PHP のログにも Web サーバのログにも何も残りません。症状は「ボタンを押しても何も起きない」だけ。curl で HTML を取ってきても、& を探す気で見ないと気づけません。
しかも || は書き換えられません。&& とビット演算の & だけが壊れます。古い IPアドレス確認のスクリプトが無事だったのは、たまたま & を使っていなかったからでした。安全だったわけではありません。
直し方は単純で、JavaScript を外部ファイルに出して wp_enqueue_script で読み込むだけです。本文を通らないので書き換えられません。
IPv6 の表示が全部ひっくり返っていた
サブネット計算の IPv6 で、2001:db8::1 が ::db8:2001 と表示されていました。内部で扱う数値は正しく、表示に直すところだけが逆順という、目視では気づきにくいバグです。
見つけられたのは、答えを別の実装と突き合わせたからです。ランダムなアドレスを数百件作って、Python の ipaddress モジュールの計算結果と1件ずつ比べました。合計6,438件の照合で、IPv6 側だけが全滅していました。
同じやり方で、MD5 と CRC32 は Node.js の標準ライブラリと、punycode はブラウザの URL 実装と突き合わせています。自分の実装同士で比べても意味がないので、必ず外の実装を相手にしました。
ハッシュが計算できない環境がある
SHA 系はブラウザの crypto.subtle を使っていますが、これは https(安全なコンテキスト)でしか使えません。手元の検証環境が http だったので「計算できない」と出て、一瞬バグを疑いました。
自分の道具で自分のサイトを測ったら C だった
セキュリティヘッダー診断を作って、最初に測ったのが自分のサイトです。結果は C(61点)。
内訳は、HSTS が未設定、CSP が未設定、そして Referrer-Policy が unsafe-url でした。これは「どのページから来たかを、外部サイトにフルURLで渡す」という設定です。
直しにかかったのですが、ここでもうひとつ罠がありました。
HSTS には includeSubDomains という指定があり、付けるのが定石です。ただしこれは配下のサブドメイン全部を HTTPS 必須にするという宣言で、しかも一度ブラウザに覚えられると、指定した期間ずっと効きます。
外部サービスへの転送用に用意したサブドメインなど、HTTPS で応答しないものが1つでも混ざっていると、そこが開けなくなります。付ける前に、配下に何を生やしているかを数え直した方がいい指定です。
このサイトでは、Web サーバ側でホスト名ごとに値を分け、配下にサブドメインを持たない方にだけ includeSubDomains を付ける形にしました。
CSP はもっと慎重にやりました。当てずっぽうで書くと表示が壊れるので、まずブラウザで「実際に何を読み込んでいるか」を数えました。そのうえで Content-Security-Policy-Report-Only(報告するだけで、ブロックはしない)で入れて、記事・トップ・ツール・管理画面を一通り開いて違反を集めました。
出てきた違反は1件だけ。ある外部 CSS を、スクリプトの許可リストには入れたのにスタイルの許可リストに入れ忘れていた、というものでした。机上で書いていたら絶対に気づかない種類の抜けです。それを直してから本適用にしました。
結果は A(94点)。満点にならないのは 'unsafe-inline' を残しているからで、これは WordPress とプラグインがインラインのスクリプトを出す以上、外すのは現実的ではありません。
まとめ
2つだった道具が12個になりました。
サーバ側で処理するものは、いずれも「他人を攻撃できない」形に寄せてあります。計算・変換・cron の3つは、そもそもサーバに何も送りません。
使ってみて変なところがあれば、コメントで教えてください。

コメントを残す