32blogby StudioMitsu

パスワードはどうやって破られるのか?攻撃手法と最新の防御策

ブルートフォース、辞書攻撃、レインボーテーブルの仕組みをコード例付きで解説。GPU時代にMD5/SHA-1が危険な理由、Argon2id/bcryptの正しい使い方、2025年NIST新基準まで。

33 min read
目次

あなたが今使っているパスワード、何秒で破られるか知っているだろうか。8文字の英数字パスワードなら、最新のGPUを使えば数分で解読される。「自分は大丈夫」と思っている人ほど危ない。

この記事では、パスワードがどのように保存され、どのような手法で破られるのかを具体的なコード例とともに解説する。そして、2025年に改訂されたNISTの新基準を踏まえ、今すぐ実践できる防御策を紹介する。

あなたのパスワードは何秒で破られるか

Hive Systems が2024年に公開したパスワード解読時間の一覧がある。これはRTX 4090を 12台 使い、bcrypt(32反復、いわゆるcost=5)でハッシュ化されたパスワードをブルートフォースで解読する場合の目安だ。本番環境で推奨されるcost=12なら、解読時間はこの表の約128倍になる。

パスワード長数字のみ小文字のみ大小英字大小英数字英数字+記号
6文字即座即座即座即座4秒
8文字即座22時間8ヶ月3年12年
10文字2時間15年41,000年345,000年3,700万年
12文字9日10,000年10億年190億年2兆年
14文字2年2.6億年3兆年
16文字202年7兆年

見ての通り、8文字の小文字パスワードは 22時間 で破られる。bcryptという比較的強いアルゴリズムで保存されていてもこの速さだ。MD5で保存されていた場合は秒単位まで短縮される。

注意してほしいのは、この表はGPU 12台 の場合だということ。クラウドでさらにGPUを借りれば、解読時間はさらに短くなる。10,000台のA100 GPUを使えば、8文字のランダムパスワードでも5日で解読可能とされている。

重要なのはパスワードの 長さ だ。12文字以上にするだけで、解読時間は桁違いに伸びる。文字種を増やすより、長さを確保する方がはるかに効果的だ。数学的に言えば、パスワードの強度は「文字種の数 ^ パスワード長」で決まる。文字種を2倍にするより、パスワードを1文字長くする方が効果が大きい場合がほとんどだ。

パスワードが保存される仕組み -- ハッシュ化とは

まともなWebサービスは、パスワードをそのまま(平文で)保存しない。代わりに ハッシュ関数 を使って変換した値を保存する。

ハッシュ関数には3つの特徴がある。

  1. 一方向性 -- 入力からハッシュ値は計算できるが、ハッシュ値から入力は復元できない
  2. 固定長出力 -- 入力の長さに関係なく、常に同じ長さの出力になる
  3. 衝突耐性 -- 異なる入力が同じハッシュ値になる確率が極めて低い

Pythonで実際に試してみよう。

python
import hashlib

password = "password123"
hashed = hashlib.sha256(password.encode()).hexdigest()
print(hashed)
# ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f

同じ入力には常に同じハッシュ値が返る。だからログイン時には、ユーザーが入力したパスワードをハッシュ化して、保存されたハッシュ値と比較すればいい。平文を保存する必要はない。

一方向性があるので、仮にデータベースが漏洩しても、ハッシュ値から直接パスワードを逆算することはできない。攻撃者は「総当たりでハッシュ値を計算して一致するものを探す」しかない。これがハッシュ化の基本的な防御効果だ。

しかし、すべてのサービスがこの基本を守っているわけではない。2025年5月、セキュリティ研究者のJeremiah Fowlerが 1億8,400万件 のパスワードが平文で保存された公開データベースを発見した。Apple、Google、Facebookのアカウント情報まで含まれていた。ハッシュ化すらされていない状態で、インターネット上に放置されていたのだ。

攻撃手法(1) ブルートフォースと辞書攻撃

パスワードのハッシュ値が漏洩したとき、攻撃者はどうやって元のパスワードを特定するのか。代表的な手法が3つある。

ブルートフォース攻撃

すべての文字の組み合わせを片っ端から試す。最も単純だが、短いパスワードに対しては確実に機能する。

python
import hashlib
import itertools
import string

def brute_force_sha256(target_hash, max_length=6):
    """SHA-256ハッシュに対するブルートフォース攻撃のデモ"""
    chars = string.ascii_lowercase + string.digits

    for length in range(1, max_length + 1):
        for attempt in itertools.product(chars, repeat=length):
            password = "".join(attempt)
            hashed = hashlib.sha256(password.encode()).hexdigest()
            if hashed == target_hash:
                return password
    return None

# "cat" のSHA-256ハッシュを解読してみる
target = hashlib.sha256("cat".encode()).hexdigest()
result = brute_force_sha256(target)
print(f"Found: {result}")  # Found: cat

このコードは教育目的の単純な実装だ。Pythonのシングルスレッドで動かすと、6文字のパスワードでも数時間かかる。

実際の攻撃では Hashcat というツールが使われる。HashcatはGPUの並列処理能力を活用し、Pythonの何百万倍もの速度でハッシュを計算する。さらに、ルールベースの変換(passwordP@ssw0rdPassword1! など)を組み合わせることで、単純なブルートフォースよりはるかに効率的に攻撃を行う。

bash
# Hashcatの基本的な使い方(教育目的)
# -m 0: MD5ハッシュ
# -a 0: 辞書攻撃モード
hashcat -m 0 -a 0 hashes.txt wordlist.txt

# -a 3: ブルートフォースモード(マスク攻撃)
# ?l: 小文字 ?u: 大文字 ?d: 数字 ?s: 記号
hashcat -m 0 -a 3 hashes.txt ?l?l?l?l?l?l?l?l

辞書攻撃

人間が実際に使うパスワードはパターンがある。password123qwertyiloveyou のような頻出パスワードのリスト(辞書)を使って試す方法だ。

最も有名な辞書ファイルが rockyou.txt だ。2009年にRockYouというSNSサービスから漏洩した約1,400万件のパスワードが含まれている。現在ではさまざまなデータ漏洩から収集された辞書が存在し、数十億件規模のリストも流通している。

辞書攻撃が怖いのは、人間のパスワード選択パターンが驚くほど偏っていることだ。NordPassが2024年に発表した調査によると、世界で最も使われているパスワードのトップ3は 12345612345678912345678 だった。日本では 123456789password12345678 がトップ3だ。これらは辞書攻撃で1秒もかからず突破される。

クレデンシャルスタッフィング

あるサービスから漏洩したメールアドレスとパスワードの組み合わせを、別のサービスで試す攻撃だ。パスワードを使い回している人が多いため、驚くほど成功率が高い。

クレデンシャルスタッフィングの成功率は0.2〜2%程度とされている。低く感じるかもしれないが、100万件の漏洩データを使えば2,000〜20,000アカウントが突破される計算だ。しかも攻撃は完全に自動化されていて、ボットが大量のログイン試行を高速で実行する。

攻撃手法(2) レインボーテーブル

ブルートフォースは毎回ハッシュを計算する。では、あらかじめすべてのハッシュ値を計算しておいて、テーブル(辞書)として保存しておけばどうだろう。これが レインボーテーブル の発想だ。

例えば、8文字以下の英数字パスワードのSHA-256ハッシュをすべて計算してテーブルに保存する。漏洩したハッシュ値があれば、テーブルを引くだけで一瞬で元のパスワードがわかる。

実際のレインボーテーブルは、ストレージ節約のために「チェーン」と呼ばれる仕組みを使っている。すべてのハッシュ値を保存する代わりに、チェーンの起点と終点だけを保存し、中間は必要なときに再計算する。これにより、数TBのテーブルを数百GBに圧縮できる。

レインボーテーブルはインターネット上で無料で配布されている。MD5やSHA-1のテーブルなら、ダウンロードするだけで攻撃準備が整う。

ソルトによる防御

レインボーテーブルへの対策が ソルト(salt) だ。パスワードをハッシュ化する前に、ユーザーごとにランダムな文字列を付加する。

python
import hashlib
import os

def hash_without_salt(password):
    """ソルトなし -- レインボーテーブルで解読可能"""
    return hashlib.sha256(password.encode()).hexdigest()

def hash_with_salt(password):
    """ソルトあり -- レインボーテーブルが使えない"""
    salt = os.urandom(16)
    hashed = hashlib.sha256(salt + password.encode()).hexdigest()
    return salt.hex(), hashed

# 同じパスワードでも、ソルトが違えばハッシュ値が変わる
print(hash_without_salt("password123"))
# ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f

salt1, hash1 = hash_with_salt("password123")
salt2, hash2 = hash_with_salt("password123")
print(f"Salt1: {salt1}, Hash: {hash1}")
print(f"Salt2: {salt2}, Hash: {hash2}")
# 同じ "password123" なのに、毎回異なるハッシュ値になる

ソルトがあると、レインボーテーブルはユーザーごとに別のテーブルが必要になる。100万ユーザーがいれば100万個のテーブルが必要だ。事実上、レインボーテーブル攻撃は無力化される。

ソルトの実装で重要なポイントが3つある。

  1. ソルトはユーザーごとに異なる値を使う。 全ユーザーで同一のソルトを使うと、その1つのソルト用のレインボーテーブルを作れば全員分が解読できてしまう
  2. ソルトは暗号論的に安全な乱数で生成する。 Math.random() やタイムスタンプではなく、os.urandom()crypto.randomBytes() を使う
  3. ソルトはハッシュ値と一緒に保存する。 ソルトは秘密にする必要はない。ログイン時にハッシュを再計算するために必要だからだ

ソルトに加えて ペッパー(pepper) を使うこともある。ペッパーはアプリケーション側で保持する秘密値で、データベースには保存しない。ソルトとペッパーの両方を使えば、データベースが漏洩してもペッパーがなければハッシュの再計算ができないため、防御層がもう一段増える。ペッパーは環境変数やシークレットマネージャーで管理するのが一般的だ。

ただし、ソルト付きSHA-256でも十分ではない。次のセクションで、その理由を説明する。

なぜMD5やSHA-1では守れないのか -- GPU時代の現実

MD5やSHA-1は 汎用ハッシュ関数 だ。データの完全性検証や署名のために設計されていて、「速く計算できる」ことが美点だった。しかしパスワード保存においては、この速さが致命的な弱点になる。

NVIDIA RTX 4090(2024年時点の一般消費者向けハイエンドGPU)を1台使った場合のHashcatベンチマーク(v6.2.6)を見てほしい。

ハッシュアルゴリズム速度1秒間の試行回数
MD5164,100 MH/s1,641億回
SHA-150,600 MH/s506億回
SHA-25622,000 MH/s220億回
bcrypt (32反復)184 kH/s184,000回

MD5は1秒に1,641億回計算できる。一方、bcrypt(32反復)でも184,000回。その差は 約89万倍 だ。本番環境で推奨されるcost=12(4,096反復)なら約1,400 H/sまで下がり、MD5との差は 約1.1億倍 に広がる。Argon2idはメモリハードなため、bcryptよりさらに遅い。

8文字の英数字パスワードをMD5で保存していたら、RTX 4090一台であっという間に突破される。

具体的に計算してみよう。8文字の英数字(大小+数字、62種)のパスワードの組み合わせは 62^8 = 約218兆通り。MD5で164,100 MH/sなら、218兆 / 1,641億 ≈ 1,331秒、つまり 約22分 で全探索が完了する。GPUを12台並列にすれば2分以下だ。

一方、同じパスワードをbcrypt(cost=12、約1,400 H/s)で保存していたら、218兆 / 1,400 = 約1,560億秒、つまり 約4,900年 かかる。アルゴリズムの選択だけで、これほどの差が生まれる。

なぜbcryptやArgon2idはこれほど遅いのか。それは 意図的に遅く設計されている からだ。パスワードハッシュ専用の関数は、計算コストを調整できるパラメータを持っている。正規のログイン処理で1回ハッシュを計算するのに0.3秒かかっても、ユーザー体験にはほぼ影響しない。だが攻撃者にとっては、1回の試行に0.3秒かかるなら、1秒間にたった3回しか試行できない。

この「意図的な遅さ」を ストレッチング(key stretching) と呼ぶ。ハッシュ計算を何千回、何万回と繰り返すことで、1回の検証にかかる時間を引き上げる。正規ユーザーの1回のログインには問題にならないが、何十億回も試行する攻撃者には壊滅的な足かせになる。

ハードウェアの進化に合わせて、コストパラメータを引き上げることも重要だ。bcryptのcost=10は2010年ごろには適切だったが、2025年現在ではcost=12以上が推奨される。Argon2idならメモリコストを引き上げることで、GPUの進化に対抗できる。定期的にパラメータを見直し、ログイン時にユーザーのパスワードを新しいパラメータで再ハッシュする仕組みを入れておくのがベストプラクティスだ。

現代の防御策 -- Argon2id・bcrypt・scrypt比較

OWASPが推奨するパスワードハッシュの優先順位はこうなっている。

Argon2id > scrypt > bcrypt > PBKDF2

それぞれの特徴を比較しよう。

アルゴリズムメモリ使用GPU耐性推奨パラメータ備考
Argon2id大(調整可)高いm=47104, t=1, p=1Password Hashing Competition優勝(2015年)
scrypt大(調整可)高いN=2^17, r=8, p=1Argon2以前の選択肢
bcrypt小(4KB固定)中程度cost=12実績豊富。72バイト制限あり
PBKDF2低い600,000回以上レガシー。GPU攻撃に弱い

Argon2idの強みは メモリハード であることだ。計算にCPUだけでなく大量のメモリも要求するため、GPUやASICによる並列攻撃を困難にする。GPUは何千ものコアを持つが、各コアが使えるメモリは限られている。Argon2idが1回の計算に46MBのメモリを要求すると、GPU上で並列実行できるインスタンス数が大幅に制限される。

bcryptもGPUに対してある程度の耐性がある。内部でBlowfishの鍵スケジュールを使い、4KBのメモリに対して頻繁にランダムアクセスするため、GPUのキャッシュ構造と相性が悪い。ただしメモリ使用量が4KBに固定されているため、Argon2idほどの耐性はない。

Argon2には3つのバリエーションがある。

  • Argon2d -- GPUベースの攻撃に強い(データ依存アクセス)が、サイドチャネル攻撃に弱い
  • Argon2i -- サイドチャネル攻撃に強いが、GPUへの耐性がやや劣る
  • Argon2id -- 両方のメリットを組み合わせたハイブリッド。これを使う

Node.jsでbcryptを使う

javascript
import bcrypt from "bcrypt";

const saltRounds = 12;

// パスワードのハッシュ化
async function hashPassword(password) {
  const hash = await bcrypt.hash(password, saltRounds);
  return hash;
  // 例: $2b$12$LJ3m4ys3Lg2VJx.gBHINFOkqGJfJwR5v.Q8sGxfUNzMxDy8VLFhm
}

// パスワードの検証
async function verifyPassword(password, hash) {
  const match = await bcrypt.compare(password, hash);
  return match;
}

// 使用例
const hash = await hashPassword("my-secure-password");
console.log(hash);

const isValid = await verifyPassword("my-secure-password", hash);
console.log(isValid); // true

bcryptはソルトを自動生成してハッシュ値に埋め込む。$2b$12$12 がコスト(ストレッチング回数の指数)だ。この数値を1上げると計算時間が約2倍になる。cost=10なら1,024回、cost=12なら4,096回の内部反復を行う。

bcryptには72バイトの入力制限がある点に注意しよう。73バイト以降は無視される。日本語(UTF-8で1文字3バイト)だと24文字が上限になる。英数字なら72文字まで。通常のパスワードでは問題にならないが、極端に長いパスフレーズを使う場合は知っておくべき制約だ。

PythonでArgon2idを使う

python
from argon2 import PasswordHasher

ph = PasswordHasher(
    time_cost=1,       # 反復回数
    memory_cost=47104,  # メモリ使用量(KB)= 約46MB
    parallelism=1,     # 並列度
)

# パスワードのハッシュ化
hash_value = ph.hash("my-secure-password")
print(hash_value)
# 例: $argon2id$v=19$m=47104,t=1,p=1$...

# パスワードの検証
try:
    ph.verify(hash_value, "my-secure-password")
    print("Password is valid")
except Exception:
    print("Password is invalid")

2025年NIST新基準 -- 常識が変わった

2025年7月に正式公開されたNIST SP 800-63B Revision 4は、パスワードに関する「常識」を大きく覆した。多くの企業が長年従ってきたルールが、科学的根拠に基づいて廃止されている。

項目旧基準(Rev.3)新基準(Rev.4, 2025年)
最低文字数8文字15文字(MFAなしの場合) / 8文字(MFAありの場合)
最大文字数規定なし最低64文字を受け入れること
複雑性要件(大文字・記号の強制)必須廃止
定期的な変更推奨廃止(漏洩時のみ変更)
漏洩パスワードリストとの照合任意必須
パスキー / FIDO2言及なし公式にサポート
ヒント・秘密の質問許容禁止

定期変更が廃止された理由は明快だ。90日ごとにパスワードを変更させると、ユーザーは Password1!Password2!Password3! のようにパターン化する。結果としてセキュリティは下がる。NISTは「漏洩が確認されない限り、変更を強制しない」という方針に転換した。

複雑性要件の廃止も同様だ。「大文字1つ、数字1つ、記号1つ」を強制すると、P@ssw0rd! のような見かけだけ複雑なパスワードが量産される。それよりも 長いパスフレーズ(例: correct-horse-battery-staple)の方が、覚えやすく解読も困難だ。

漏洩パスワードリストとの照合が必須になったのも大きな変化だ。ユーザーがパスワードを設定するとき、Have I Been PwnedのAPIなどを使って、過去に漏洩したパスワードでないかチェックすることが求められる。password123qwerty のような頻出パスワードは、文字数や複雑性の要件を満たしていても、設定段階で弾かれるべきだ。

最大文字数の要件も注目に値する。NISTは「最低でも64文字のパスワードを受け入れること」と規定している。パスフレーズの利用を推奨するなら、十分な長さを許容しなければならない。データベースの列サイズやフォーム入力のmaxlengthで不必要に制限しているサービスは、新基準に適合しない。

パスキーの台頭

NIST新基準がパスキー(FIDO2 / WebAuthn)を公式にサポートしたことも大きい。パスキーはパスワードそのものを不要にする技術だ。公開鍵暗号方式を使い、秘密鍵はデバイスから出ない。フィッシングに対して原理的に安全であり、漏洩リスクもパスワードより大幅に低い。

Apple、Google、Microsoftが既にパスキーに対応しており、GitHubやAmazonなど主要サービスでも利用可能だ。今後2-3年で、パスワードから パスキーへの移行 が加速するだろう。

パスキーの仕組みを簡単に説明しよう。ユーザーがサービスに登録すると、デバイス内で公開鍵と秘密鍵のペアが生成される。公開鍵はサーバーに送られ、秘密鍵はデバイスのセキュアな領域(iPhoneならSecure Enclave、AndroidならTEE)に保存される。ログイン時には、サーバーからのチャレンジに対して秘密鍵で署名し、サーバーが公開鍵で検証する。秘密鍵がデバイスから出ることはない。

フィッシングサイトに騙されてパスワードを入力してしまうリスクもゼロだ。パスキーはオリジン(ドメイン)に紐づいているため、偽サイトでは認証が成立しない。

開発者としてパスキーを実装するなら、WebAuthn APIを使うことになる。サーバー側のライブラリとしては、Node.jsなら @simplewebauthn/server、Pythonなら py_webauthn が定番だ。詳しい実装方法は別の記事で解説する予定だ。

自分のパスワードが漏洩していないか確認する方法

「自分のパスワードは大丈夫なのか」を確認する方法がある。最も信頼性が高いのが Have I Been Pwned(HIBP)だ。

Have I Been Pwnedの仕組み

HIBPのパスワード検索は k-匿名化 という技術を使っている。あなたのパスワードがHIBPのサーバーに送られることはない。

仕組みはこうだ。

  1. パスワードのSHA-1ハッシュを計算する
  2. ハッシュの 最初の5文字だけ をAPIに送信する
  3. APIは、その5文字で始まるハッシュの一覧を返す(数百件)
  4. 残りの文字列をローカルで比較し、一致するものがあるかチェックする

実際にcurlで試してみよう。

bash
# "password" のSHA-1ハッシュを計算
echo -n "password" | sha1sum
# 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8

# 最初の5文字 "5BAA6" をAPIに送信
curl -s https://api.pwnedpasswords.com/range/5BAA6 | head -5
# 1E4C9B93F3F0682250B6CF8331B7EE68FD8:52256179
# ... (5BAA6で始まるハッシュの一覧が返る)

返ってきたリストに 1E4C9B93F3F0682250B6CF8331B7EE68FD8 が含まれていれば、そのパスワードは漏洩している。末尾の数字(52256179)は、漏洩データベースに何回登場したかを示す。password は5,200万回以上漏洩している。

この方式なら、パスワード全体はネットワークに送信されないため、確認行為自体が安全だ。

メールアドレスで確認する

haveibeenpwned.com にアクセスしてメールアドレスを入力すれば、そのアドレスに関連するデータ漏洩の一覧が表示される。どのサービスから、いつ、どんな種類のデータが漏洩したかがわかる。

2025年時点で、HIBPには 175億件以上 のアカウント情報が登録されている。自分のメールアドレスが含まれていても珍しくない。重要なのは、漏洩したサービスで使っていたパスワードを他のサービスでも使い回していないか確認することだ。

NordPassのダークウェブモニタリング機能など、常時監視してくれるサービスもある。漏洩が検出されたタイミングで通知を受け取れるので、HIBPを定期的にチェックする手間が省ける。

漏洩が見つかったら

HIBPで漏洩が確認されたパスワードは、すぐに変更しよう。同じパスワードを他のサービスでも使っていたなら、すべて変更する必要がある。対応すべき手順をまとめておく。

  1. 漏洩したサービスのパスワードを即座に変更する -- 新しいパスワードは16文字以上のランダムな文字列にする
  2. 同じパスワードを使っていた全サービスのパスワードを変更する -- 1つずつ確認していく
  3. そのサービスで二要素認証(2FA)を有効にする -- TOTP(Google Authenticatorなど)かセキュリティキーが望ましい。SMS認証はSIMスワップ攻撃のリスクがある
  4. 不審なログイン履歴がないか確認する -- サービスによってはアクティブセッションの一覧を確認できる
  5. パスワードマネージャーを導入する -- 今後の使い回しを防ぐ

パスワードマネージャーを使えば、サービスごとにランダムな長いパスワードを生成・管理できる。1Password、Bitwarden、NordPassなどが代表的なツールだ。マスターパスワード1つを覚えるだけで、何百ものサービスに異なる強力なパスワードを設定できる。

開発者なら、自分のサービスにもHIBPのAPIを組み込むことを検討してほしい。NIST新基準では、パスワード設定時に漏洩リストとの照合が必須とされている。HIBPのPwnedPasswords APIは無料で利用でき、k-匿名化により安全に照合できる。

まとめ

この記事で解説した内容を振り返ろう。

  • 8文字のパスワードはもう安全ではない。 MD5保存ならRTX 4090一台で約22分、12台なら2分以下で解読される。16文字以上を目指そう
  • ハッシュ化は必須だが、アルゴリズムの選択が重要。 ソルト付きでもMD5/SHA-1は秒速で突破される
  • 攻撃手法は多様化している。 ブルートフォース、辞書攻撃、レインボーテーブル、クレデンシャルスタッフィング。それぞれに対する防御が必要だ
  • パスワード保存にはArgon2idを使う。 OWASPの第一推奨。bcrypt(cost=12以上)も許容される。MD5/SHA-1/PBKDF2からは移行を
  • NIST新基準は「長さ > 複雑さ」に転換した。 定期変更と複雑性要件は廃止。15文字以上のパスフレーズが推奨
  • Have I Been Pwnedで漏洩を確認できる。 k-匿名化により、確認行為自体は安全
  • パスキーが次の標準になる。 パスワードそのものを不要にする技術が既に実用段階にある
  • パスワードマネージャーは必須ツール。 サービスごとに異なるランダムなパスワードを管理し、使い回しのリスクを排除できる

パスワードの安全性は、あなたの選択だけで決まるものではない。保存する側がどんなアルゴリズムを使っているかで大きく左右される。

開発者として覚えておくべきことは、パスワードハッシュのアルゴリズム選択は「技術的負債」ではなく「セキュリティ基盤」だということ。Argon2idへの移行は、見えない部分だが最も重要なアップデートの一つだ。

ユーザーとしては、パスワードマネージャーと多要素認証を活用して、漏洩リスクを最小限に抑えてほしい。可能ならパスキーに移行しよう。パスワードは破られる前提で、防御の層を重ねることが最善の戦略だ。