Notes / Web技術

公開リポジトリで漏れがちな情報3種。generator meta、旧HTML残骸、hidden input

GitHubに上げているサイトのリポジトリで、攻撃対象面を広げる情報が漏れがちです。生成器メタタグ、移行作業で残った旧HTML、フォームのhidden input。3パターンとそれぞれの対処。

security information leak repository hygiene

GitHubでサイトのリポジトリを公開していると、コードが読めるのは当然として、攻撃対象面をじわっと広げる情報が意図せず残ることがあります。漏洩というほどの致命傷ではない。ただ攻撃者の偵察コストを下げる材料にはなります。

自分が見てきた中でよく残るのは、generatorメタタグ、移行時の旧HTML、フォームのhidden inputの3つ。順に対処を書いていきます。

フレームワークバージョン: generatorメタタグ

多くのSSGやCMSは、生成したHTMLに <meta name="generator"> を入れます。

<meta name="generator" content="Astro v6.3.3">
<meta name="generator" content="WordPress 6.4">
<meta name="generator" content="Hugo 0.140.0">

これがあると、サイトを開いたブラウザのDevToolsで誰でもフレームワークとバージョンが分かります。

困るのはバージョンまで出ること。既知の脆弱性(CVE)を狙う攻撃者にとっては偵察の手間が一段減って、「v6.3.3はXが効く」のようにピンポイントで攻め手を選べてしまいます。

対処は、AstroならBaseLayoutから削除すれば終わり。

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <!-- 削除: <meta name="generator" content={Astro.generator} /> -->
</head>

WordPressならfunctions.phpで remove_action('wp_head', 'wp_generator');。Hugoならテーマ側で対応します。

そもそも何のフレームワークを使っているかは、HTTPヘッダーやCSSクラス名、ビルド成果物からだいたい割れるので、generatorを消したところで完全に匿名化できるわけではありません。とはいえバージョンまで出すのは避けたい。ここを塞ぐだけでも、相手の手間は確実に増えます。

移行作業で残った旧HTMLや設定

WordPressや旧CMSからSSGにリプレースしたサイトのリポジトリには、移行参照用に旧HTMLスナップショットを置くことがあります。

migrate/
├── raw-html/
│   ├── home.html      ← GTM ID、Dynatrace エンドポイント、旧プラグインURL
│   ├── contact.html
│   └── ...
├── wp-status.txt      ← WPプラグイン一覧、管理画面URL
└── wp-uploads-and-options.txt

これらは本番デプロイ対象ではない(Astroなら src/ 外、distに含まれない)ので、運用上の影響はありません。けれど公開リポジトリにしている場合はGitHubで誰でも見られます。

漏れて困るのは、旧サイトに紐付いていたGTMコンテナID、Dynatrace Application ID、Google Analytics IDあたり。あわせて旧CMSの管理画面URLやプラグイン構成、バージョンが推測されますし、ベンダー固有のエンドポイントやinternal configが見えることもあります。

退避のやり方はだいたい3通りに落ち着きます。

一番きれいなのは、追跡を外して履歴も消すやり方。.gitignore に足すだけでは追跡済みファイルも過去履歴も消えないので注意で、git rm --cached migrate/raw-html/ で以後の追跡を外し、過去履歴は git filter-repo (旧 git filter-branch)で書き換えます。ただし履歴書き換えはチーム作業に影響するため、リモートと整合させる段取りがいる。

それが重ければ、Google Driveや社内Notion、プライベートリポジトリに移すだけでもいい。参照用には残しつつ公開から退避します。

ファイルを動かしたくないなら、置いたまま旧IDを全部無効化する手もあります(GTMコンテナ削除、APIキー失効、エンドポイント廃止)。漏れていても害がない状態にしてしまう。

機能影響が小さい順は2、3、1。本番切替後、つまり旧IDが完全に使われなくなった後なら1が一番安全です。

フォームのhidden inputに書かれた識別子

<input type="hidden" name="access_key" value="abc-123-xyz" />
<input type="hidden" name="redirect" value="https://example.com/thanks/" />
<input type="hidden" name="subject" value="【ブロトソル】お問い合わせ" />

Web3Forms等のSaaSフォームでaccess_keyをhidden inputに書くのは仕様通りで、これ自体は問題ではありません(公開識別子なので隠せない)。

問題はaccess_key以外のhidden fieldのほうです。redirect URLは自サイトのURL構造を見せてしまう。大した話ではないけれど、偵察材料には足されます。subjectfrom_emailto_email は内部メールアドレスや件名規約が露出。そして webhook_url、Slack等のincoming webhookがhiddenに書かれていたらこれは致命的です。

実害でいうと、内部メアドはスクレイピングされてspamリストに載る。webhook URLが漏れればSlackに任意のペイロードを投げ込まれます。redirect のほうは、宛先をSaaS側が任意URLに書き換えられる仕様だった場合、オープンリダイレクト攻撃の踏み台にされ得る。

なので、メアド(to_email 等)はhidden inputに書かずSaaS管理画面側で設定する。webhook URLは絶対にクライアントへ出さない。出すならサーバーサイドプロキシ経由です。redirect はSaaS側のallowlistで宛先を縛って、任意URLに書き換えられないようにしておきます。

Web3Formsの場合、access_key をhiddenに置き、to_emailやwebhookはdashboard側で設定する設計が標準。これに従えば内部情報は漏れません。


どれも単発では致命傷になりにくい。ただ積み上がると攻撃者の偵察コストを着実に下げます。リポジトリを公開する前に一度こういう観点で見ておけば、外部公開と内部運用の境界線がぶれません。

リポジトリを公開する前のチェック観点づくりは、Webセキュリティ領域の支援範囲です。