2 日で 2 回、同じ形を踏んだ
2026年8月18日、俺は本番のクロスサイトスクリプティング(XSS)を塞いだ。そのとき教訓として刻んだのは「『直した』と『効いている』は別だった」という一行だ。live の出力を確かめずに類推で直したら、直した先が実行されていないコードだった、という失敗だ。
その2日後、8月20日。俺はまったく同じ形を、今度は自分の手ではなく、指示書の形で他人に踏ませかけた。「このファイルを直してくれ」と依頼した先が、実は実行経路が見つからないファイルだった。俺が指定した場所は死んでいて、俺が指定しなかった場所が生きていた。
今回の記事は、この2日間に実際に起きたことから出発して、「そのコードは本当に使われているのか」という、開発の現場では避けて通れない問いに向き合う。結論を先に言えば、この問いに完全な「はい」も「いいえ」も出せないことがある。それでも、確かめる手順は確かに存在する。その手順と、確かめきれなかったときにどう振る舞うべきかを、実例と一次情報の両方から書く。
1回目 — 直した先が、実行されていなかった
本番に、1年前から開いていた穴
8月18日、本番サイトに反射型XSS(Reflected XSS)が見つかった。俺はそれを塞いだ。修正自体は正しかった——後で確かめたら、値は正しくエスケープされていた。
問題はそこではなかった。俺が最初に「直した」と報告した箇所は、実はprodのライブ出力を一度も確認せず、testで見た形を類推してそのまま本番に当てたものだった。testとprodでコードの分岐が違う場合、testで正しく見えても、prodでは別の経路が使われていることがある。
気づいたのは、自分が確かめた数字がずれていたから
「反映した」と報告した数時間後、俺は自分で走らせた検証コマンドの出力を見直した。想定していたパターンの出現数が、実際には合っていなかった。追いかけたら、俺が編集した箇所は、実際にはそのページの出力に一切使われていないコードだった。ビュー側が別のロジックで値を上書きしていたのだ。
塞いだ穴は、別の場所にあった
結局、本当に危険だった箇所(属性値としてブラウザに出力される値)は、俺が最初に触った場所とは別のファイルにあった。そこを実際に確かめて特定し直し、正しくエスケープを入れて、live出力で確認して、ようやく塞がった。
このとき得た教訓がこれだ。
「直した」と「効いている」は別だった。修正した箇所が実際に出力に効いているか、live で確かめる。
この教訓は、2日後の自分にはまだ届いていなかった。
2回目 — 今度は【指示書に書いて】他人に踏ませかけた
別のツール画面に、また同じ種類の穴
8月20日、サイト内の別のツール画面でも同じ種類の脆弱性の疑いが出た。俺は、直すべき箇所を特定し、指示書を書いて、作業を任せた相手に渡した。
俺が渡した指示は「このファイルを直せ」だった
指示書には、ある画面のロジックを持つと思われる特定のPHPファイルを名指しし、「ここを直してくれ」と書いた。俺の中では、grepでそのファイル名を検索して見つけたファイルが「そのツール画面の実装」だと確信していた。
だが、その確信はファイルが存在することと、ファイル名がそれらしいことの2点だけに基づいていた。そのファイルが実際にブラウザから届くリクエストに対して呼ばれているかどうかは、一度も確かめていなかった。
2日前に得た教訓を、自分で破っていた
これは2日前の「1回目」とまったく同じ構造だった。違うのは、今回は俺自身が手を動かして誤ったコードを直したのではなく、誤った指定を指示書として他人に渡したという点だ。指示書の重さは、自分で手を動かすときより大きい。渡された相手は、渡された前提を疑わずにそのまま作業を始めることが多いからだ。
並べてみると、同じ形をしている
| 1回目(8/18・自分の手) | 2回目(8/20・指示書) | |
|---|---|---|
| 俺が信じた根拠 | testで見た形が、prodでも同じだろうという類推 | ファイル名がそれらしく、grep で見つかったという事実 |
| 確認しなかったこと | prodのライブ出力そのもの | 実URLの応答と、そのファイル固有の出力 |
| 実際に危険だった場所 | 俺が最初に触った場所とは別のファイル | 俺が指示した場所とは別のファイル |
| 気づいた経路 | 自分で走らせた検証コマンドの出力を見直した | 相手が②③まで確かめて報告してきた |
この形になって初めて、俺は「同じ失敗を2回した」ではなく「同じ失敗を2つの違う立場で踏んだ」のだと理解した。1回目は自分の手が直接届く範囲での失敗で、まだ自分で気づける余地があった。2回目は、指示書という形で他人の手に判断を委ねた分だけ、被害が広がりうる失敗だった。
確かめた相手が、俺の指示を覆した
①その名前を呼んでいる場所を数える
実際に指示を受けた相手は、まず俺が指定したファイルをgrep -rlnで検索した。すると、そのファイル名をrequire_onceで呼んでいる場所が17本見つかった。俺の指示は、この時点では正しく見えた。
②実URLの応答を叩く
だが相手はそこで止まらなかった。俺が想定していたツール画面の実URLに実際にHTTPリクエストを送ってみたところ、返ってきたのは404だった。ページ自体が存在しない。俺が名指ししたファイルへ、ブラウザから到達するルート(URLとコントローラーの対応関係)が、そもそも見つからなかった。
③固有マーカーが出力に現れるか
念のため、そのファイルにしか存在しない固有の文字列を実際のレンダリング結果から検索させた。結果は0件。17本の呼び出し元が存在するのに、その内容が一度も画面に現れていなかった。
それでも「死んでいる」とは断定しなかった
相手は最初、「これは死んだコードです」と報告してきた。だが、その後、自分自身の判定をもう一度検算し、こう訂正してきた。
「『死んだコード』は誤りだった。17本が require_once している。ただし到達経路は特定できなかった」
この訂正が本節でいちばん重要な一行だ。「死んでいる」と言い切るのは、「使われていない」と言い切るのと同じくらい難しい。呼び出し元が17本もある以上、俺たちの知らない経路(別のルーティング設定、動的なinclude、条件分岐で稀にしか通らないパス)から到達している可能性は消えない。相手は、その可能性を消せないまま、正直に「特定できなかった」と書いた。これは失敗ではなく、正しい報告の仕方だ。
結局、実際に生きていた(=ブラウザから到達し、実際にレンダリングされていた)経路は、俺が名指ししたファイルとは別の場所にあった。そこは、エスケープ処理が一文字も入っていない状態で、そのうちの1本はページを読み込んだ瞬間にスクリプトが実行される状態だった。俺が「危ない」と名指しした場所は空振りで、俺が名指ししなかった場所のほうが、実際には差し迫って危険だった。
なぜ「使われていない」は確定できないのか
この2つの出来事を経て、俺は「あるコードが使われているかどうか」を確定させることの難しさそのものを、少し調べ直した。感覚ではなく、理論と実務の両方から裏を取りたかったからだ。
計算理論の壁 — そもそも決定不能な問題である
静的解析(ソースコードを実行せずに読んで解析すること)の入門記事に、こう書かれている。
"Guaranteeing the safety of array bounds and deciding program termination are equivalent in difficulty. Both are undecidable."
(訳: 配列の境界の安全性を保証することと、プログラムの停止性を判定することは、難しさにおいて等価である。どちらも決定不能である。)
"Evading the halting problem rests on a simple observation: it is not possible to construct a general algorithm for the halting problem for all programs and all inputs."
(訳: 停止性問題を回避する根拠は単純な観察にある。すべてのプログラムとすべての入力に対して機能する、停止性問題を解く汎用アルゴリズムは構築できない、ということだ。)
出典: Matt Might「A simple, no-nonsense introduction to program analysis」
「このコードは実行されるか」という問いは、突き詰めると「このプログラムはこの入力でこの経路を通るか」という問いと地続きになる。そしてこれは、あらゆる入力・あらゆるプログラムに対して汎用的に解ける問題ではない、と計算理論の側から証明されている。俺たちがgrepと確認を積み重ねてやっているのは、この決定不能な問題に対する「実務上、十分な確からしさを積み上げる」作業であって、100%の証明ではない。
静的解析ツール自身が「安全側に倒す」と書いている
デッドコード(使われていないコード)を検出する専門ツールの説明にも、同じ姿勢がはっきり書かれている。
"In order to prevent false positives, we support even calls over unknown types (e.g. `$unknown->method()`) by marking all methods named `method` as used"
(訳: 誤検知を防ぐため、型が不明な呼び出し(例: `$unknown->method()`)についても、`method`という名前を持つすべてのメソッドを「使用中」として扱うことでサポートしている。)
"Most magic methods (e.g. __get, __set etc) are never reported as dead"
(訳: ほとんどのマジックメソッド(`__get`、`__set`など)は、決して「デッドコード」として報告されない。)
"Any property, enum, constant or method accessed via ReflectionClass is detected as used"
(訳: `ReflectionClass`経由でアクセスされるプロパティ、enum、定数、メソッドは、すべて「使用中」として検出される。)
出典: shipmonk-rnd/dead-code-detector(PHP デッドコード検出ツール)README
これはつまり、専門のツールですら「本当に使われていないもの」だけを完璧に検出することを諦め、判定できない対象は「使われている」側に倒すという設計思想を明示的に採用している、ということだ。俺が2回目に踏んだ穴——ファイル名だけで「これは使われていない(or 使われている)」と決めつけたこと——は、専門ツールが最初から避けている判断そのものだった。
カバレッジは「使われていない」の証明にならない
もう一つ、テストカバレッジについても確かめておきたかった。「テストが通っている箇所=生きている」「テストが通っていない箇所=死んでいる」と思い込みがちだが、これも誤りだと、著名なソフトウェア設計者が明言している。
"Test coverage is a useful tool for finding untested parts of a codebase. Test coverage is of little use as a numeric statement of how good your tests are."
(訳: テストカバレッジは、コードベースの中でテストされていない部分を見つけるのには役立つ道具だ。だが、テストの質がどれだけ良いかを示す数値としては、ほとんど役に立たない。)
"The trouble is that high coverage numbers are too easy to reach with low quality testing."
(訳: 困ったことに、質の低いテストでも、カバレッジの数値を高くすることは簡単すぎるほど簡単だ。)
出典: Martin Fowler「TestCoverage」
カバレッジが低い=実行されていない、ではない。テストが書かれていないだけで、本番トラフィックからは日常的に呼ばれているコードは、ごく普通に存在する。俺たちが「使われているか」を確かめるときに頼れる指標は、テストカバレッジでも、grepの検索結果1本だけでもない。
実際に確かめる 3 つの方法
理論の話だけでは前に進まない。俺たちが今回の出来事から実際に組み立て直した、直す前に走らせるべき3行を書いておく。
3つのコマンドと、それぞれが証明すること
| 確認内容 | コマンド例 | 証明できること・できないこと |
|---|---|---|
| ① 呼び出し元の存在 | grep -rln '対象ファイル名' <DocumentRoot> | 「参照されている可能性」を示すだけ。到達性は証明しない |
| ② 実URLの応答 | curl -s -o /dev/null -w '%{http_code}' '実URL' | そのURLが200で応答するかを示す。狙ったコードが呼ばれているかまでは分からない |
| ③ 固有マーカーの出現 | curl -s '実URL' | grep -c '固有マーカー' | そのファイルの内容が実際の出力に現れているかを示す。今回の件ではここで初めて食い違いが露見した |
①だけでは危ない。俺の2回目の失敗はまさに①だけで判断したことだった。②③まで確かめて、初めて「そのコードが今、実際にレンダリングに関与しているか」が見える。
実行時に確かめる — opcacheという選択肢
PHPでは、実行系そのものに「今、何が実行系に乗っているか」を尋ねる方法がある。PHPの公式マニュアルにこうある。
"opcache_get_status — Get status information about the cache"
(訳: opcache_get_status — キャッシュに関する状態情報を取得する。)
"This function returns state information about the in-memory cache instance. It will not return any information about the file cache."
(訳: この関数は、メモリ上のキャッシュインスタンスに関する状態情報を返す。ファイルキャッシュに関する情報は一切返さない。)
出典: PHP 公式マニュアル「opcache_get_status」
この関数にinclude_scriptsという引数をtrueで渡すと、戻り値の中に実際にコンパイルされ、キャッシュされたスクリプトの一覧が入る。これは「ソースコードとして存在する」という静的な事実ではなく、「実行系に一度でも乗った」という動的な事実を示す、直接の証拠になる。grepやcurlより一段深く、実行の痕跡そのものを見にいく方法だ。ただし、これはサーバー側の設定と権限があって初めて使える手段であり、常に叩ける道具ではないことも書き添えておく。
4 億 6,000 万ドルの実例 ── ただし同型だが同一ではない
Knight Capital 事件
「使われていないはずのコードが、実は使われていた」ことが引き起こした最も大きな実例の一つが、2012年8月1日にアメリカの証券会社Knight Capitalで起きた事件だ。米証券取引委員会(SEC)の公式命令書に、経緯が詳しく記されている。
"Upon deployment, the new RLP code in SMARS was intended to replace unused code in the relevant portion of the order router. This unused code previously had been used for functionality called 'Power Peg,' which Knight had discontinued using many years earlier. Despite the lack of use, the Power Peg functionality remained present and callable at the time of the RLP deployment."
(訳: 展開に際して、SMARSの新しいRLPコードは、注文ルーターの該当部分にある未使用コードを置き換えることを意図していた。この未使用コードは、以前「Power Peg」と呼ばれる機能に使われていたが、Knight社は何年も前にその使用を中止していた。使われていなかったにもかかわらず、Power Peg機能はRLP展開の時点で、依然としてその場に存在し、呼び出し可能な状態のままだった。)
"In 2003, Knight ceased using the Power Peg functionality. In 2005, Knight moved the tracking of cumulative shares function in the Power Peg code to an earlier point in the SMARS code sequence. Knight did not retest the Power Peg code after moving the cumulative quantity function to determine whether Power Peg would still function correctly if called."
(訳: 2003年、Knight社はPower Peg機能の使用を停止した。2005年、Knight社は、Power Pegコード内にある累積株数を追跡する機能を、SMARSコードの手順のより早い位置へと移動させた。Knight社は、この累積数量機能を移動させた後、もしPower Pegが呼び出された場合に正しく動作するかどうかを確かめるための再テストを行わなかった。)
"During the deployment of the new code, however, one of Knight's technicians did not copy the new code to one of the eight SMARS computer servers. Knight did not have a second technician review this deployment and no one at Knight realized that the Power Peg code had not been removed from the eighth server, nor the new RLP code added."
(訳: しかし、新しいコードの展開時、Knight社の技術者の一人が、8台あるSMARSコンピュータサーバーのうち1台に、新しいコードをコピーしなかった。Knight社は、この展開を確認する2人目の技術者を配置しておらず、8台目のサーバーからPower Pegコードが削除されていないことにも、新しいRLPコードが追加されていないことにも、Knight社の誰も気づかなかった。)
45分で3億9,700万株、4億6,000万ドルの損失
"For the 212 incoming parent orders that were processed by the defective Power Peg code, SMARS sent millions of child orders, resulting in 4 million executions in 154 stocks for more than 397 million shares in approximately 45 minutes. […] Ultimately, Knight realized a $460 million loss on these positions."
(訳: 不具合のあるPower Pegコードによって処理された212件の親注文に対し、SMARSは数百万件の子注文を送出し、その結果、約45分間で154銘柄・3億9,700万株超に対する400万件の約定が生じた。……最終的にKnight社は、これらのポジションで4億6,000万ドルの損失を計上した。)
"Knight did not design these types of messages to be system alerts, and Knight personnel generally did not review them when they were received."
(訳: Knight社は、この種のメッセージをシステムアラートとして設計しておらず、Knight社の担当者は、それらを受信しても通常は目を通していなかった。)
この最後の一文が、俺には他人事に思えなかった。archives/125では、当サイトが独自に持っていた「自分で測る指標」が、確認したら93日間、静かに止まっていたという話を書いた。仕組みの壊れ方は違うが、「確認するまで、誰も気づけなかった」という構造は同じだ。Knight社のケースも同じ形をしている。Knight社では、エラーを知らせる自動メールが97通、市場が開く前に出ていた。だが、いま引いたSEC命令書がはっきり書いているとおり、それはアラートとして設計されたものではなく、受け取った担当者も目を通していなかった。仕組みが警告していたのに誰も見なかった、のではない。警告として設計されていなかった、が正確だ。
報告書が最後に示した対策
"a written procedure requiring a simple double-check of the deployment of the RLP code could have identified that a server had been missed and averted the events of August 1."
(訳: RLPコードの展開に際して単純な二重確認を求める文書化された手順があれば、1台のサーバーが漏れていたことを特定でき、8月1日の事態を回避できていたはずだ。)
出典: 米SEC 命令書 Release No. 34-70694(Knight Capital 事件)
同型だが同一ではない — この対比を正確に書く
ここで一つ、はっきりさせておきたい。Knight社の事件は、「デッドコード(使われなくなったコード)を消し忘れて、それが復活してしまった」という話だ。復活の原因は、8台中1台だけデプロイが漏れたという単純な作業ミスだった。
俺のケース(8月20日)は逆に近い。「これは使われていないだろう」と推測してファイルを指定したら、実は指定した場所が死んでいて、指定しなかった別の場所が生きていた、という話だ。Knight社は「削除したはずのものが残っていた」のに対し、俺は「生きていると思って直そうとした場所が死んでいて、死んでいると思っていなかった別の場所が生きていた」という、判定の向きが違う失敗をした。
それでも共通する教訓は一つある。「使われているか / いないか」の判定を、確認せずに前提として使うと、必ずどこかで裏切られる。Knight社は「使われていない」という前提を、俺は「ここが使われている(or 使われていない)」という前提を、それぞれ検証せずに使って、それぞれ違う形で失敗した。同型だが、同一の失敗ではない。
それでも「到達不能かもしれない箇所」を直す理由
防御縦深という考え方
相手が「到達経路は特定できなかった」と正直に報告してくれたコードについて、俺は結局、修正自体は実施した。到達性が証明できなくても、修正はやる。理由は、セキュリティの世界で古くから使われている「防御縦深(defense in depth)」という考え方にある。OWASP(Webアプリケーションセキュリティの国際的な非営利団体)の公式開発者ガイドに、こう説明されている。
"Also known as layered defense, defense in depth is a security principle where defense against attack is provided by multiple security controls."
(訳: 多層防御としても知られる防御縦深とは、攻撃に対する防御を複数のセキュリティ制御によって提供する、というセキュリティの原則だ。)
"If one layer of defense turns out to be inadequate then, if diverse defensive strategies are in place, another layer of defense may prevent a full breach and if that one is circumvented then the next layer may block the exploit."
(訳: ある層の防御が不十分だったとしても、多様な防御戦略が用意されていれば、別の層の防御が完全な侵害を防ぐことがある。そしてその層も回避された場合、さらに次の層が攻撃を阻止することがある。)
出典: OWASP Developer Guide「Security Principles」
「今この経路には到達できない」と確かめられても、それは「未来永劫、到達できない」ことの証明ではない。ルーティングの設定が変わる、新しい機能が古いコードを呼び出すようになる、俺たちがまだ知らない経路が実は存在する——こうした可能性は常に残る。到達性が不確かなコードほど、直しておく価値がある。防御は、「今日確認できた事実」だけでなく、「明日、前提が崩れたとき」にも効くように積んでおくものだからだ。
本文の元になった脆弱性そのものの技術的な詳細(どういう文字列を入れると実行されるか、どう防いだかの実装方式)はここには書かない。それを書くこと自体が、次の攻撃のヒントになりうるからだ。「エスケープが一文字も入っていなかった」「1本はページ読み込み時に即座に実行される状態だった」という事実と、「すでに塞いだ」という結果だけを、ここには残しておく。
もう一つ、自分たちの持っているツールの話もしておきたい。当サイトには🔧 外からできるセキュリティ対策監査ツール(要ログイン)があり、外部から到達できる画面を自分で洗い出すことができる。俺が今回、指示書を書く前に本来やるべきだったのは、まさにこの「外から見て何が実際に届くか」を先に確かめることだった。
当サイトの現在地
ここまで書いてきたことを、当サイト自身がどこまでやれているか、正直に並べておく。
- ✅ 当サイト実施済 到達経路が特定できなかった箇所について、「使われていない」と断定せず、「到達経路は特定できなかった」と書いた。この記事の本文が、そのまま証拠になっている。
- ✅ 当サイト実施済 到達経路が不明なままの箇所も、防御としての修正だけは済ませた。動いていないかもしれない場所を、動く前提で塞いである。
- ✅ 当サイト実施済 直した箇所を、実URLのライブ出力で開いて確認した。「テスト環境で直ったから本番も直っているはず」という類推で終わらせない。
- 🔧 着手中 作業を任せる相手に指示を渡す前に、grep・curl・固有マーカーの3行を自分で叩いて、その結果を指示書に貼る。今日から手順に入れた。
- 🔧 未着手 外部から到達できる画面の一覧を、自前のセキュリティ対策監査ツールで洗い出す。「呼ばれている数」ではなく「実際に届く数」のリストを持つ。
- 📋 予告 実行時の情報(
opcache_get_status()など)で「そのファイルが本当に読み込まれたか」を確かめる仕組みを検討する。静的な検索では原理的に届かない層を、実行時の情報で埋められないか。
明日 確認できること
- 直そうとしている画面の実URLをブラウザで開いて、200が返るか確認する
- そのURLの実際のHTMLソースを表示し、直した箇所に固有の文字列が含まれているか検索する
- grep -rln で、直そうとしているファイル名を呼んでいる場所が何本あるかを数える
- 見つかった呼び出し元のうち、実際にルーティングで到達可能なものが何本かを数える
- curlで対象URLを叩き、HTTPステータスコードを控える
- 修正前のファイルのバックアップを取り、md5を控える
- 修正後、必ずライブの出力を実際に開いて確認する(類推で終わらせない)
- 直した箇所が本当に出力に反映されているか、固有マーカーで検索する
- 「使われていない」と判定する前に、opcache_get_status など実行時の情報で確認できないか調べる
- 到達経路が特定できない箇所も、防御としての修正だけは済ませておく
- 他人に指示書を渡す前に、grep・curl・curlの3行を自分で叩いて結果を貼る
- 調査を依頼するときは、結論ではなく確認手順を渡す
- セキュリティ対策監査ツールで、外部から到達できる画面の一覧を洗い出しておく
- 修正が終わった箇所をリストに記録し、次に同じ場所を疑わずに済むよう共有する
もう一つ、Search Consoleの見方に迷ったときのために当サイトが用意している🔧 Search Console の見方に迷ったらこちらの解説ツールも紹介しておく。「200が返っている」ことと「対応できている」ことが別物なのは、セキュリティの話に限らない。「クロール済み」と「検出」、インデックス未登録の違いでも、同じ形の勘違いが起きやすいことを書いた。
このテーマの、これまで
「一致していること」や「数が合っていること」が、正しさの証明にならないという話は、archives/129で書いた。同じものを2通りの数え方(タイトル一致・元記事URL一致)で数えたら、答えが3倍 違った、という結果だ(3つの照合方法を示したが、実際に実行したのはこの2つで、残り2つ〈本文ハッシュ・見出し構成ハッシュ〉は今も未実行だと129自身が書いている)。今回も、俺の「①名前を呼んでいる場所がある」という1本の確認だけでは足りず、②③まで確かめて初めて実態が見えた。1つの確認方法だけを根拠にしないことの重要さは、この記事とつながっている。
自分が公開した記事の測り方を、翌日には自分自身のサイトに当ててみるという習慣については、archives/127に書いた。今回の記事も、書いて終わりにせず、今後の自分の指示書の書き方に対して、そのまま当ててみるつもりだ。
「200が返ることと、対応していることは別だった」という教訓を最初に書いたのはarchives/126だ。今回の記事の②(実URLが200で応答するか)と③(固有マーカーが出力に現れるか)の違いは、この教訓の延長線上にある。200という応答コードだけでは、狙ったコードが実際に動いているかは分からない。
「見えているのは全体の半分にすぎない」という視点も、「確認するまで誰も気づけなかった」という話もarchives/125で書いた。今回で言えば、grepの検索結果は「参照されている可能性」しか見せてくれず、実際に画面へ届いているかどうかは、また別の手段で確かめる必要があった。1つの仕組み・1つの指標が見せてくれる範囲は、いつも思っているより狭い。
2日で2回、同じ形を踏んだ。1回目は自分の手で。2回目は指示書という形で、他人に。幸い、2回目は確かめてくれた相手が、俺の思い込みを覆してくれた。次に同じ場面が来たとき、俺が先に3行のコマンドを先に走らせられるかどうかが、たぶん本当の分かれ目になる。
「使われているか、いないか」は、決定不能な問いになりうる。それでも、俺たちは毎日、直すか直さないかを決めなければならない。証明できないなら、少なくとも確かめられる範囲まで確かめる。それが、この2日間で俺が学んだ、いちばん地味だけれど、いちばん確かな結論だ。
WEBサイト