Security
7 min read
9711 views

1MBパスワード:ハッシング枯渇によるバックエンドのクラッシュ 🏎️💥

IT
InstaTunnel Team
Published by our engineering team
1MBパスワード:ハッシング枯渇によるバックエンドのクラッシュ 🏎️💥

サイバーセキュリティの世界では、遅い方が良いと教えられることが多いです。パスワードのハッシュ化に関しては、Argon2idやBcryptのような「コストの高い」アルゴリズムを意図的に選びます。これらはCPUサイクルやメモリを消費させ、ブルートフォース攻撃を経済的に不可能にするために設計されています。

しかし、その「コスト」が逆にあなたに向けられたらどうなるでしょうか?

登場するのは1MBパスワード攻撃です。これは静かでエレガント、そして非常にシンプルなサービス拒否(DoS)攻撃のベクターです。ランダムな文字列を1メガバイトのサイズでログインや登録フィールドに送ることで、攻撃者はサーバーに対し、1リクエストのハッシュに数秒から数分を費やさせることが可能です。

数十回のリクエストだけでCPUを100%に張り付かせ、バックエンド全体をダウンさせることもできます。本記事では、ハッシング枯渇の仕組み、現代のフレームワークがなぜ脆弱になりやすいのか、そしてセキュリティを犠牲にせずに対策する方法について解説します。

1. 「遅い」ハッシュ化のパラドックス

この脆弱性を理解するには、ハッシュ関数の仕組みを見てみる必要があります。BcryptやScrypt、Argon2のような現代的なアルゴリズムは「適応型」です。これらはWork Factor(またはコスト)を用いて、何回ハッシュを実行するかを決定します。

$O(n)$とWork Factorの比較

ほとんどの開発者はWork Factorに注目します。例えば、Bcryptのコストを12に設定すると、パスワードのハッシュには約250msかかります。13に上げると500msです。これは、パスワードが「password123」でも「correct-horse-battery-staple」でも一定の時間がかかることを意味します。

しかし、隠れた変数があります:入力の長さ ($n$)

Work Factorはブルートフォースに対して指数関数的な保護を提供しますが、ハッシュ関数は入力文字列の全バイトを処理しなければなりません。計算量はおおよそ$O(n \times \text{work factor})$です。

$n$が15文字なら影響は小さいですが、$n$が1,000,000文字(1MB)になると、CPUは大量のデータを処理しながら何千回も繰り返し計算を行う必要があります。

これが「静かな」殺し手である理由

多くのDoS攻撃はボリューム(ネットワークの洪水)やプロトコル(SYN洪水)に基づいています。ファイアウォールやWebアプリケーションファイアウォール(WAF)はこれらをブロックするのに優れています。しかし、1MBのPOSTリクエストは、見た目は普通の(やや大きい)ユーザーデータに見えます。アプリケーション層をターゲットにしているため、多くの従来のネットワーク防御を回避します。

2. 攻撃の構造:1MBペイロード

標準的なログインエンドポイントを想像してください:POST /api/v1/login

一般的なリクエストは次のようになります:

{
  "username": "victim@example.com",
  "password": "pA$$w0rd123!"
}

次に、攻撃者が送るのはこうです:

{
  "username": "victim@example.com",
  "password": "a...[999,990個の'a']...a"
}

CPUスパイク

サーバーがこれを受け取ると、1MBの文字列をハッシュライブラリに渡します。

  • メモリの負荷:サーバーはリクエスト本文の文字列を保持するために少なくとも1MBのRAMを割り当てる必要があります。
  • ハッシュ処理の負荷:CPUは処理を開始します。Argon2idはメモリハードなため、1ハッシュあたり64MBのRAMを割り当て、3回のイテレーションを行います。
  • スレッドのブロック:多くの環境(Node.jsやPython/Djangoなど)では、ハッシュはメインスレッドまたは制限されたワーカープールで行われます。CPUがその1MBの文字列をハッシュしている間、他のリクエストは処理できません。

3. 脆弱性の比較:Bcrypt vs. Argon2 vs. PBKDF2

すべてのハッシュアルゴリズムが長い文字列を同じように処理するわけではありません。皮肉なことに、古い「欠陥」が一部の保護層を提供している場合もあります。

Bcrypt:72バイトの「盾」

Bcryptには有名な特性があります:パスワードの最初の72バイトだけを考慮します。72バイト以降の文字は無視されます。

  • セキュリティの欠点:100文字のパスワードを持つユーザーは、思ったほど安全ではありません。
  • DoSの利点:入力をほぼ即座に切り捨てるため、1MBのパスワードをBcryptに渡しても、通常はCPUスパイクを引き起こしません。ライブラリは最初の72バイトだけを見て、残りを破棄します。

Argon2id:現代の脆弱性

Argon2id(2026年のゴールドスタンダード)は、Bcryptの制限を修正するために設計されました。任意の長さの入力を処理します。これはセキュリティやパスフレーズには良いですが、送信された1MBすべてを忠実にハッシュしようとします。アプリケーションレベルの制限がなければ、Argon2は枯渇攻撃の格好のターゲットです。

PBKDF2:イテレーションの悪夢

PBKDF2はSHA-256とともによく使われます。72バイトの制限はありませんが、純粋にCPUバウンドです。攻撃者が巨大な文字列を送ると、CPU負荷は長さに比例して増加します。Django(デフォルトでPBKDF2を使用)などのフレームワークでは、これによりサーバーの遅延が大きくなることがあります。

4. なぜフレームワークはこれを見逃しやすいのか

2025年や2026年には、多くのフレームワークが「デフォルトで安全」な設定に移行していますが、最小長さに焦点を当てており、最大長さにはあまり注意を払っていません。

  • DjangoMinimumLengthValidator(デフォルト8文字)がありますが、MaximumLengthValidatorはデフォルトで有効になっていません。
  • Laravelmin:8は頻繁に検証されますが、max:128はあまり使われません。
  • Node.js(Bcrypt/Argon2ライブラリ):ほとんどのライブラリは単に文字列またはバッファを受け入れます。アプリケーションが生のリクエスト本文を関数に渡すと、ライブラリは処理を試みます。

哲学は常に「なぜユーザーの長くてより安全なパスワードを止める必要があるのか?」でした。1MBの「パスワード」はパスワードではなく、手榴弾です。

5. 戦略的な対策:バックエンドを守る

ハッシュ枯渇に対抗するには、多層的なアプローチが必要です。できるだけCPUから遠ざけることが重要です。

対策1:ゴールデンルール(最大長さの制限)

最もシンプルで効果的な防御策は、パスワードフィールドにハードリミットを設定することです。

推奨:最大パスワード長を128または256文字に設定します。

極端なセキュリティ志向の人でも、256文字を超えるパスワードは必要ありません。これは合理的なパスフレーズには十分長く、サーバーにとって計算負荷も小さく抑えられます。

Djangoの例

# settings.py
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {'min_length': 12},
    },
    # カスタムまたは組み込みのMaxLengthValidatorを追加可能
]

対策2:SHA-256の「事前ハッシュ」トリック

無限長のパスワードをどうしても許可したい場合(例:特殊な暗号学的理由)、事前ハッシュを使います。

パスワードをBcryptやArgon2に渡す前に、SHA-256のような高速で適応性の低いハッシュを通します。

  1. ユーザーが1MBのパスワードを送信
  2. サーバーがtemp_hash = SHA-256(password)を計算
  3. SHA-256の出力は常に32バイトの固定長文字列
  4. サーバーがfinal_hash = Bcrypt(temp_hash)を計算

これにより、「高価な」アルゴリズム(Bcrypt/Argon2)は常に小さく固定された入力だけを処理します。

注意:これを実装する場合は、一貫性を持たせる必要があります。既存ユーザーのハッシュと互換性を保つためにマイグレーション計画が必要です。

対策3:IPとユーザーネームによるレートリミット

枯渇攻撃はボリュームに依存します。1MBのパスワードのハッシュに2秒かかるとしても、1リクエストだけではサーバーは倒れません。複数のリクエストが同時に来れば負荷は増大します。

  • ログインや登録エンドポイントに厳格なレートリミットを設定
  • 「リキッドバケット」アルゴリズムを使って同じIPからの繰り返し試行を遅らせる

対策4:WAFとペイロード制限

ロードバランサー(Nginx、AWS ALB)やWAF(Cloudflare、Akamai)を設定し、疑わしい大きさのペイロードを拒否します。

ログインフォームにユーザー名とパスワードだけしかない場合、POST本文は10KBを超えてはいけません。エッジで拒否しましょう。

6. 実践例

Node.js(Express + Joi)

Joiのようなバリデーションライブラリを使えば、データがハッシュ処理に届く前にこれらの制限を簡単に適用できます。

const loginSchema = Joi.object({
  username: Joi.string().email().required(),
  password: Joi.string().min(12).max(128).required() // 重要な行
});

app.post('/login', (req, res) => {
  const { error } = loginSchema.validate(req.body);
  if (error) return res.status(400).send(error.details[0].message);
  // ここで初めて高コストのハッシュ処理
  const isValid = await bcrypt.compare(req.body.password, user.hash);
});

PHP(Laravel)

Laravelのバリデーションエンジンはこれを一行で実現します。

$request->validate([
    'email' => 'required|email',
    'password' => 'required|string|min:12|max:255', // 上限を設定
]);

7. 比較表:ハッシュアルゴリズムとDoS脆弱性

アルゴリズム デフォルト最大長 DoS脆弱性(制限なし) 2026年の推奨
Bcrypt 72バイト 低(切り捨てによる) レガシー / RAM制限に適合
Argon2id なし 最良(128文字のアプリ制限と併用)
Scrypt なし 中〜高 良好、ただしArgon2id推奨
PBKDF2 なし Argon2未使用時のみ推奨

結論:可用性はセキュリティの一部

セキュリティと可用性はしばしば別のシロに分けて考えられます。ハッシュを強化してデータ漏洩を防ぐ一方で、応答しないサーバーは失敗したサーバーです(可用性)。

「1MBパスワード」は、ユーザー入力のすべてのバイトにコストがかかることを思い出させてくれます。max_length: 128の制約をパスワードフィールドに追加するだけで、大規模なDoSの抜け穴を塞ぎつつ、最高の暗号保護基準を維持できます。

あなたの「強力」なセキュリティが、実はあなたを倒す原因にならないようにしましょう。

Continue from this article into the most relevant product guides and workflows.

Related Topics

#hashing exhaustion attack, oversized password dos, 1mb password attack, authentication denial of service, bcrypt dos vulnerability, argon2 dos attack, password hashing abuse, cpu exhaustion attack, authentication bypass via dos, login endpoint attack, hashing performance vulnerability, resource exhaustion vulnerability, password field length limit, secure authentication failure, backend dos via hashing, computational denial of service, cryptographic dos attack, application layer dos, slow hashing attack, password abuse attack, authentication system crash, brute force mitigation bypass, login dos vulnerability, web application dos, form field abuse, cpu spike attack, denial of wallet via hashing, authentication performance attack, heavy computation vulnerability, server resource exhaustion, password validation vulnerability, api authentication dos, web security flaw, login endpoint overload, cloud resource exhaustion, cost amplification attack, cryptographic workload attack, password handling vulnerability, secure coding mistakes, input validation failure, application level attack vector, backend performance exploit, security misconfiguration, denial of service technique, web app resilience, auth system hardening, rate limiting bypass, cpu pinning attack, hash algorithm abuse, argon2 security pitfalls, bcrypt performance issues, password hashing attack vector, authentication threat model, login security risks, resource based attacks, web application security 2025, denial of wallet attacks, cloud billing abuse, backend crash vulnerability, defensive coding practices, auth endpoint protection, password length enforcement, hashing attack mitigation, secure authentication design

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles