Instantly share code, notes, and snippets.
Created
September 1, 2025 01:40
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save kazumich/573189000988b79e966f2246335b3efb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| // filename: clear-cookies.php | |
| // 目的: 実行中のドメインで送信されてきた Cookie(HttpOnly を含む)を、可能な限り失効させる UI 付きツール | |
| // 使い方: このファイルを配置してアクセス。リストが表示され、削除ボタンで失効処理を実行します。 | |
| declare(strict_types=1); | |
| // ------------------------------------------------------------ | |
| // 設定(必要に応じて拡張してください) | |
| // ------------------------------------------------------------ | |
| $extraPaths = [ | |
| '/admin', '/admin/', // よくある発行パス | |
| ]; | |
| // ------------------------------------------------------------ | |
| // 共通情報 | |
| // ------------------------------------------------------------ | |
| $host = $_SERVER['HTTP_HOST'] ?? ''; | |
| $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); | |
| $scriptDir = rtrim(str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '/')), '/'); | |
| if ($scriptDir === '') $scriptDir = '/'; | |
| // 現在パスからルートまでの階層を作る(/a/b/c -> /a/b/c, /a/b, /a, /) | |
| $pathSegments = array_values(array_filter(explode('/', $scriptDir))); | |
| $pathCandidates = ['/']; | |
| if (!empty($pathSegments)) { | |
| for ($i = 1; $i <= count($pathSegments); $i++) { | |
| $p = '/' . implode('/', array_slice($pathSegments, 0, $i)); | |
| $pathCandidates[] = $p; | |
| $pathCandidates[] = rtrim($p, '/') . '/'; | |
| } | |
| } | |
| // 追加パス候補もマージ | |
| $pathCandidates = array_values(array_unique(array_merge($pathCandidates, $extraPaths))); | |
| // ドメイン候補(ホスト名、先頭ドット付き、親ドメインを含む) | |
| // 例: sub.example.co.jp -> ["sub.example.co.jp", ".sub.example.co.jp", ".example.co.jp"] | |
| $domainCandidates = []; | |
| if ($host !== '') { | |
| $domainCandidates[] = $host; | |
| $domainCandidates[] = '.' . $host; | |
| // 親ドメイン(最左ラベルを 1 つ落とす簡易版)※ PSL を見ない簡易実装 | |
| $parts = explode('.', $host); | |
| if (count($parts) > 2) { | |
| $parent = '.' . implode('.', array_slice($parts, 1)); | |
| $domainCandidates[] = $parent; | |
| } | |
| } | |
| $domainCandidates = array_values(array_unique($domainCandidates)); | |
| // ------------------------------------------------------------ | |
| // 削除処理(POST) | |
| // ------------------------------------------------------------ | |
| $deleted = []; | |
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | |
| // 受信している Cookie 名を抽出 | |
| $cookieNames = array_keys($_COOKIE ?? []); | |
| $expires = time() - 3600; // 過去 | |
| foreach ($cookieNames as $name) { | |
| foreach ($pathCandidates as $path) { | |
| // ドメイン未指定(ホスト限定) | |
| @setcookie($name, '', [ | |
| 'expires' => $expires, | |
| 'path' => $path, | |
| // domain を指定しないパターン | |
| 'secure' => $https, | |
| 'httponly' => true, | |
| 'samesite' => 'Lax', | |
| ]); | |
| // ドメイン候補に対しても失効を試行 | |
| foreach ($domainCandidates as $domain) { | |
| @setcookie($name, '', [ | |
| 'expires' => $expires, | |
| 'path' => $path, | |
| 'domain' => $domain, | |
| 'secure' => $https, | |
| 'httponly' => true, | |
| 'samesite' => 'Lax', | |
| ]); | |
| } | |
| } | |
| $deleted[] = $name; | |
| } | |
| // PRG パターン(再読み込みでの再 POST 防止) | |
| $qs = http_build_query(['cleared' => 1, 'count' => count($deleted)]); | |
| $url = strtok($_SERVER['REQUEST_URI'], '?') . '?' . $qs; | |
| header('Location: ' . $url, true, 303); | |
| exit; | |
| } | |
| // ------------------------------------------------------------ | |
| // 表示用データ | |
| // ------------------------------------------------------------ | |
| $currentCookies = $_COOKIE ?? []; | |
| $hasCookies = !empty($currentCookies); | |
| // HTML 出力開始 | |
| ?> | |
| <!DOCTYPE html> | |
| <html lang="ja"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Cookie clear tool</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <!-- Tailwind CSS CDN --> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <meta name="robots" content="noindex,nofollow,noarchive" /> | |
| </head> | |
| <body class="bg-gray-50 text-gray-900"> | |
| <div class="max-w-5xl mx-auto px-4 py-10"> | |
| <header class="mb-8"> | |
| <h1 class="text-2xl font-bold"><?= htmlspecialchars($host, ENT_QUOTES, 'UTF-8') ?> の Cookie 管理</h1> | |
| <p class="text-sm text-gray-600 mt-2"> | |
| HTTPS: <span class="font-mono"><?= $https ? 'true' : 'false' ?></span> | |
| </p> | |
| </header> | |
| <?php if (isset($_GET['cleared'])): ?> | |
| <div class="mb-6 rounded-lg border border-green-200 bg-green-50 p-4"> | |
| <p class="font-medium text-green-800">Cookie の削除処理を実行しました。</p> | |
| <p class="text-green-800 text-sm mt-1">必要に応じてページを再読み込みし、最新の状態を確認してください。</p> | |
| </div> | |
| <?php endif; ?> | |
| <section class="mb-8"> | |
| <div class="flex items-center justify-between mb-3"> | |
| <h2 class="text-xl font-semibold">現在の Cookie 状況</h2> | |
| <span class="text-sm text-gray-600">検出数: <span class="font-medium"><?= count($currentCookies) ?></span></span> | |
| </div> | |
| <?php if ($hasCookies): ?> | |
| <div class="overflow-x-auto rounded-lg border border-gray-200 bg-white"> | |
| <table class="min-w-full text-sm"> | |
| <thead class="bg-gray-50"> | |
| <tr> | |
| <th class="px-4 py-2 text-left font-medium text-gray-600">Name</th> | |
| <th class="px-4 py-2 text-left font-medium text-gray-600">Value (冒頭のみ表示)</th> | |
| </tr> | |
| </thead> | |
| <tbody class="divide-y divide-gray-100"> | |
| <?php foreach ($currentCookies as $name => $value): ?> | |
| <tr> | |
| <td class="px-4 py-2 font-mono text-gray-800"><?= htmlspecialchars((string)$name, ENT_QUOTES, 'UTF-8') ?></td> | |
| <td class="px-4 py-2 font-mono text-gray-700"> | |
| <?php | |
| $v = (string)$value; | |
| $short = mb_strimwidth($v, 0, 80, '…', 'UTF-8'); | |
| echo htmlspecialchars($short, ENT_QUOTES, 'UTF-8'); | |
| ?> | |
| </td> | |
| </tr> | |
| <?php endforeach; ?> | |
| </tbody> | |
| </table> | |
| </div> | |
| <p class="text-xs text-gray-500 mt-2"> | |
| サーバー側からは <span class="font-mono">$_COOKIE</span> に届いたもののみ確認できます。 | |
| </p> | |
| <?php else: ?> | |
| <div class="rounded-lg border border-amber-200 bg-amber-50 p-4"> | |
| <p class="text-amber-800">現在、サーバーに送信されている Cookie は検出されませんでした。</p> | |
| </div> | |
| <?php endif; ?> | |
| </section> | |
| <section class="mb-12"> | |
| <form method="post" class="flex items-center gap-3"> | |
| <button | |
| type="submit" | |
| class="inline-flex items-center rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium hover:bg-gray-50" | |
| onclick="return confirm('このドメインの Cookie を削除します。よろしいですか?');"> | |
| このドメインの Cookie を削除する | |
| </button> | |
| <a href="<?= htmlspecialchars(strtok($_SERVER['REQUEST_URI'], '?'), ENT_QUOTES, 'UTF-8') ?>" | |
| class="inline-flex items-center rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium hover:bg-gray-50"> | |
| 再読み込み | |
| </a> | |
| </form> | |
| <div class="mt-4 text-xs text-gray-500 space-y-1"> | |
| <p>削除時は、以下の組合せで失効させます: <span class="font-mono">Domain</span> =(未指定 / 現在のホスト / 先頭ドット付き / 親ドメイン)、<span class="font-mono">Path</span> = 現在ディレクトリからルートまで + よくあるパス。</p> | |
| <p><span class="font-semibold">注意:</span> 他サイト(例: <span class="font-mono">.twitter.com</span>)の Cookie は、そのサイトで本ツールを実行しない限り削除できません。</p> | |
| </div> | |
| </section> | |
| <footer class="text-xs text-gray-500"> | |
| <p>© <?= date('Y') ?> Cookie clear tool</p> | |
| </footer> | |
| </div> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment