Web 開発者のためのセキュリティベストプラクティス
Web セキュリティはオプションではありません。サイバー脅威が増加し続ける今、開発者はアプリケーションのあらゆる層にセキュリティを組み込む必要があります。本ガイドでは、今すぐ実践すべき基本的なセキュリティ対策を解説します。
パスワードセキュリティ
平文パスワードは絶対に保存しない
パスワードは必ず bcrypt、Argon2、scrypt などのモダンなアルゴリズムでハッシュ化してください。これらのアルゴリズムは意図的に低速に設計されており、ブルートフォース攻撃を現実的に不可能にします。
// 推奨:bcrypt を使用
const bcrypt = require('bcrypt');
const hash = await bcrypt.hash(password, 12);
ハッシュアルゴリズムの比較
すべてのハッシュアルゴリズムが同じではありません。適切なアルゴリズムの選択は、脅威モデルとユースケースに依存します:
| アルゴリズム | 出力サイズ | 速度 | ユースケース | セキュリティ状態 |
|---|---|---|---|---|
| MD5 | 128 ビット | 非常に高速 | チェックサム、非セキュリティハッシュ | セキュリティ用途では破られている |
| SHA-256 | 256 ビット | 高速 | データ完全性、デジタル署名 | 安全 |
| bcrypt | 184 ビット | 低速(調整可能) | パスワードハッシュ | 安全 |
| Argon2 | 設定可能 | 低速(調整可能) | パスワードハッシュ(モダン) | 新規プロジェクトに推奨 |
bcrypt と Argon2 は意図的に低速です。これはバグではなく機能です。各ハッシュ操作に数十から数百ミリ秒かかるため、大規模なブルートフォース攻撃が経済的に実行不可能になります。
パスワードエントロピーの理解
パスワードの強度はエントロピーで数学的に測定できます:entropy = log2(charset_size^length)。小文字(26 文字)のみの 8 文字パスワードのエントロピーは約 37.6 ビットです。大文字、小文字、数字、記号を混合した(95 文字)16 文字のパスワードは約 105 ビットのエントロピーを持ち、解読難易度は指数関数的に増大します。これが、ほとんどのユーザーにとって複雑さよりも長さが重要な理由です。パスワード強度の数学的な詳細については、パスワードエントロピー解説をご覧ください。
パスワードマネージャーの使用
ユーザーにパスワードマネージャーの採用を推奨しましょう。人間が選ぶパスワードは予測可能なパターンに従いがちで、攻撃者は辞書攻撃でこれを悪用します。パスワードマネージャーは真にランダムな文字列を生成し、サービス間でのパスワードの再利用を排除します。パスワードの再利用はクレデンシャルスタッフィング攻撃の最も一般的な攻撃ベクトルの一つです。
十分なソルトラウンドを設定する
ソルトラウンドは計算コストを決定します。値が大きいほど安全ですが、処理速度は低下します。ほとんどのアプリケーションでは 10-12 ラウンドが適切なバランスです。
入力バリデーション
クライアントサイドとサーバーサイドの両方で検証する
クライアントサイドのバリデーションは UX を向上させますが、セキュリティにとって不可欠なのはサーバーサイドのバリデーションです。クライアントからの入力は絶対に信頼してはいけません。
すべてのユーザー入力をサニタイズする
インジェクション攻撃を防ぐために、入力をサニタイズしましょう:
- SQL インジェクション防止にはパラメータ化クエリを使用
- XSS 攻撃防止には HTML 出力をエスケープ
- ファイルアップロードは厳格にバリデーション
具体的な攻撃例
実際の攻撃を理解することが防御に役立ちます。ユーザー入力を直接 HTML にレンダリングするコメントフォームを考えてみましょう。攻撃者が以下を送信します:
<script>alert('xss')</script>
アプリケーションがエスケープせずにこれをレンダリングすると、スクリプトはすべての訪問者のブラウザで実行されます。Cookie の窃取、ユーザーのリダイレクト、キーロガーの注入が可能になります。対策:常にコンテキストに応じた出力エンコーディングを行います。HTML サニタイズには DOMPurify などのライブラリを使用しましょう。
SQL インジェクションも同様に危険です。ログインフォームで、攻撃者がユーザー名として以下を入力します:
' OR 1=1 --
クエリが文字列結合で構築されている場合("SELECT * FROM users WHERE username='" + input + "'")、認証が完全にバイパスされます。-- はクエリの残りをコメントアウトします。対策:常にパラメータ化クエリ(プリペアドステートメントとも呼ばれる)を使用します。すべての主要なデータベースライブラリがサポートしています:
// 誤り:文字列結合
db.query(`SELECT * FROM users WHERE username='${input}'`);
// 正しい:パラメータ化クエリ
db.query('SELECT * FROM users WHERE username = $1', [input]);
コンテンツセキュリティポリシー(CSP)
多層防御として、Content Security Policy ヘッダーをデプロイしましょう。CSP はブラウザに信頼できるコンテンツソースを伝え、インラインスクリプトや不正なリソースの読み込みを効果的にブロックします。コードに XSS 脆弱性が存在しても、厳格な CSP は注入されたスクリプトの実行を防止できます。Content-Security-Policy: default-src 'self' から始めて、必要に応じて例外を追加していきましょう。
ハッシュ関数
適切なハッシュ関数を選ぶ
ユースケースによって適切なハッシュ関数は異なります:
| ユースケース | 推奨アルゴリズム |
|---|---|
| パスワード | bcrypt、Argon2 |
| データの完全性検証 | SHA-256 |
| チェックサム | SHA-256、MD5(セキュリティ用途以外) |
| 高速ハッシュ | BLAKE3 |
ハッシュ出力と衝突の理解
MD5 は 128 ビット(16 進数 32 文字)のハッシュを生成し、SHA-256 は 256 ビット(16 進数 64 文字)のハッシュを生成します。この違いは重要です。より大きな出力空間は指数関数的に多くのハッシュ値を意味し、衝突の可能性が大幅に低くなります。衝突とは、2 つの異なる入力が同じハッシュを生成することです。衝突を生成できる攻撃者はデジタル署名を偽造したり、検証済みデータを改ざんしたりできます。
MD5 の衝突は現代のハードウェアで数秒で生成できます。SHA-256 は衝突耐性を維持しており、既知の実用的な攻撃はありません。適切なコンテキストで適切なアルゴリズムを選ぶことが重要な理由はここにあります:
- チェックサムと重複排除:セキュリティが問題にならない場合、MD5 は許容される
- データの完全性と署名:SHA-256 は強力な衝突耐性を提供
- パスワード保存:bcrypt または Argon2、ソルトと意図的な遅延を追加
メッセージ認証のための HMAC
メッセージの完全性と真正性の両方を検証する必要がある場合は、HMAC(ハッシュベースメッセージ認証コード)を使用します。HMAC はハッシュ関数と秘密鍵を組み合わせ、鍵を知っている当事者のみがタグを生成・検証できることを保証します。これは API 認証、Webhook 検証、セキュアなトークン生成に不可欠です。
MD5 や SHA-1 をセキュリティ目的で使ってはいけない
MD5 と SHA-1 はセキュリティの観点からは既に破られています。暗号学的ハッシュには SHA-256 または SHA-3 を使用してください。
HTTPS をあらゆる場面で
TLS が実際に行うこと
TLS(Transport Layer Security)は 3 つの重要な保護を提供します:転送中の暗号化(盗聴の防止)、サーバー認証(なりすましではなく本物のサーバーと通信していることの証明)、データの完全性(転送中の改ざんの検出)。TLS がなければ、ユーザーとサーバー間のすべてのデータ(パスワード、トークン、個人情報)が平文で送信されます。
常に TLS を使用する
- 信頼された認証局から証明書を取得する(Let’s Encrypt は無料で完全に自動化されている)
- HTTP から HTTPS へリダイレクトする
- HSTS ヘッダーを設定する
- TLS のバージョンを常に最新に保つ
HSTS と混合コンテンツ
HTTP Strict Transport Security(HSTS)ヘッダーは、ユーザーが http:// と入力しても、ブラウザに HTTPS のみで接続するよう指示します。Strict-Transport-Security: max-age=31536000; includeSubDomains を設定すると、すべてのサブドメインで 1 年間強制されます。これにより、攻撃者が接続を HTTP にダウングレードする SSL ストリッピング攻撃を防止できます。
混合コンテンツの警告に注意しましょう。HTTPS ページが HTTP 経由で画像、スクリプト、スタイルシートを読み込むと、ブラウザはブロックまたは警告を表示します。ハードコードされた http:// URL がないかページを監査し、プロトコル相対パスを使用するか、すべてのリソースに HTTPS を強制しましょう。
認証
レート制限を実装する
ブルートフォース攻撃を防ぐためにレート制限を導入しましょう:
- IP アドレスごとのログイン試行回数を制限
- 失敗後に遅延を追加
- 不審なアクティビティには CAPTCHA を使用
JWT 認証の基本
JSON Web Token(JWT)は header.payload.signature の構造を持つステートレスな認証メカニズムを提供します。サーバーが秘密鍵でトークンに署名し、クライアントは後続のリクエストにそれを含めます。トークンにユーザーのクレームが含まれているため、サーバーはリクエストごとにセッション状態を検索する必要がありません。これにより JWT は分散システムやマイクロサービスに適しています。
アクセストークンには常に短い有効期限(例:15 分)を設定し、リフレッシュトークンを使用して新しいアクセストークンを取得します。リフレッシュトークンは安全に保存し(httpOnly Cookie、localStorage ではなく)、トークンローテーションを実装して各リフレッシュトークンが一度しか使用できないようにします。
多要素認証(MFA)
本格的なアプリケーションでは、MFA はもはやオプションではありません。第二要素の要求(TOTP コード(Google Authenticator)、ハードウェアキー(YubiKey)、プッシュ通知など)により、パスワード漏洩の影響を劇的に軽減できます。攻撃者が有効な認証情報を入手しても、第二要素なしでは認証を完了できません。
セッション固定攻撃の防止
セッション固定攻撃は、攻撃者がユーザーの認証前に既知のセッション ID を設定する場合に発生します。ログイン後、攻撃者はそのセッション ID を使用して認証済みセッションを乗っ取ります。これを防ぐには、認証成功後に必ずセッション ID を再生成し、古い ID を無効化します。
安全なセッション管理を行う
- 暗号学的に安全な乱数でセッション ID を生成
- Cookie に secure フラグと httpOnly フラグを設定
- セッションタイムアウトを実装
- ログアウト時にセッションを無効化
セキュリティヘッダーチェックリスト
適切な HTTP レスポンスヘッダーのデプロイは、アプリケーションを堅牢化する最も効果的かつ低コストな方法の一つです。以下は必須セキュリティヘッダーのクイックリファレンス表です:
| ヘッダー | 目的 | 値の例 |
|---|---|---|
| Content-Security-Policy | XSS とデータインジェクションの防止 | default-src 'self' |
| Strict-Transport-Security | HTTPS 接続の強制 | max-age=31536000; includeSubDomains |
| X-Content-Type-Options | MIME タイプスニッフィングの防止 | nosniff |
| X-Frame-Options | クリックジャッキングの防止 | DENY |
| Referrer-Policy | リファラー情報の制御 | strict-origin-when-cross-origin |
これらのヘッダーは、Web サーバーレベル(Nginx、Apache)、CDN/エッジレベル(Cloudflare、Vercel)、またはアプリケーションフレームワーク内で設定できます。securityheaders.com などのツールでヘッダーをテストしましょう。A+ 評価を目指してください。ほとんどのヘッダーは 1 行の設定で済み、デプロイコストはゼロです。
Go Tools のセキュリティツールを活用する
開発に役立つセキュリティツールをぜひお試しください:
- MD5 ハッシュジェネレーター - チェックサムやレガシーシステム向け
- UUID ジェネレーター - 安全なランダム識別子の生成
- ランダムパスワードジェネレーター - 強力なパスワードの生成
エンコーディング、ハッシュ、変換ツールが開発ワークフローにどう組み込まれるかの全体像については、開発者ツール必携ガイドをご覧ください。
よくある質問
最も一般的な Web セキュリティの脆弱性は何ですか?
クロスサイトスクリプティング(XSS)は、OWASP によると最も広く見られる Web 脆弱性です。アプリケーションが適切なバリデーションなしに信頼できないデータを Web ページに含めると発生します。すべてのユーザー入力をサニタイズし、Content Security Policy ヘッダーを使用し、コンテキスト(HTML、JavaScript、URL、CSS)に応じた出力エンコーディングで XSS を防止しましょう。
MD5 はまだパスワードハッシュに安全ですか?
いいえ — MD5 をパスワードハッシュに使用してはいけません。計算速度が非常に速いため、ブルートフォース攻撃やレインボーテーブル攻撃に脆弱です。最新の GPU は毎秒数十億回の MD5 ハッシュを計算できます。代わりに bcrypt、scrypt、Argon2 を使用してください。これらは意図的に低速で、組み込みのソルトにより攻撃に耐性があります。
2026 年に安全なパスワードの長さはどのくらい必要ですか?
最低 12 文字が推奨されますが、16 文字以上にすると大幅に強力な保護が得られます。複雑さよりも長さが重要です。「correct-horse-battery-staple」のような 20 文字のパスフレーズは、「P@ss1!」のような短く複雑なパスワードよりも安全です。重要なアカウントでは、パスワードの長さに関係なく多要素認証(MFA)を有効にしましょう。
暗号化とハッシュの違いは何ですか?
暗号化は可逆的です — 鍵を使ってデータを元の形に復号できます。ハッシュは一方向です — ハッシュ値から元のデータを復元することはできません。取得が必要なデータ(保存されたユーザーデータなど)には暗号化を、検証のみが必要なデータ(パスワードやチェックサムなど)にはハッシュを使用してください。
自前の認証システムを実装すべきですか?
いいえ — 認証をゼロから構築するのはリスクが高く、エラーが発生しやすいです。Auth0、Firebase Auth、Supabase Auth などの実績あるフレームワークやサービスを使用してください。これらはパスワードハッシュ、セッション管理、トークンローテーション、MFA、ブルートフォース保護を処理します。開発時間はアプリケーション独自の機能に集中させましょう。
まとめ
セキュリティは一度きりのタスクではなく、継続的なプロセスです。最新の脆弱性情報を常にキャッチアップし、定期的にコードを監査し、最小権限の原則に従いましょう。ユーザーは自分のデータをあなたに預けています。堅牢なセキュリティ対策でその信頼に応えてください。