【テクニカル・上級編】 PAM(Pluggable Authentication Modules)によるパスワードポリシーの強制 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

認証の防壁を決壊させる「人間」という最大の脆弱性

インフラストラクチャのセキュリティ監査において、私たちは幾度となく数千万ドルの次世代ファイアウォールや、厳格にセグメント化されたゼロトラスト・アーキテクチャの向こう側が、たった1行の貧弱なパスワードによって崩壊する瞬間を目撃してきた。

攻撃者は、複雑なゼロデイ脆弱性を探すよりも、人事リストから推測した標的に対し、初期パスワードの総当たりやリスト型攻撃(クレデンシャル・スタッフィング)を仕掛ける方が圧倒的にコストパフォーマンスが高いことを知っている。SSHポートをインターネットに公開した瞬間から、無数のボットネットが root や admin に対するブルートフォース攻撃を浴びせ続けるのは、それが今なお最も確実な侵入経路の一つだからだ。

LinuxサーバーOSの要塞化(ハーデニング)において、PAM(Pluggable Authentication Modules)の適切な設定は、侵入の踏み台を排除するための最も基本的かつ強力な防衛線となる。本稿では、pam_pwquality と pam_faillock を駆使し、単なる「お作法」ではない、攻撃者の自動化スクリプトを完全に沈黙させるためのディープな認証制御アーキテクチャを解説する。

PAMアーキテクチャの内部挙動と認証の4つのスタック

PAMは、アプリケーション(SSH、sudo、loginなど)と実際の認証メカニズム(影のパスワードファイル、LDAP、Kerberosなど)を疎結合にするためのフレームワークだ。しかし、この柔軟性ゆえに、設定ファイルを誤れば「ガバガバの認証バイパス」をいとも簡単に作り出してしまう。

PAMの挙動を理解する上で、制御フラグ(Control Flags)の理解は不可欠である。特に以下の4つが認証フローの運命を握る。

  • requisite:これが失敗した時点で即座に認証が拒否される。ただし、それまでのモジュールの結果は保持され、エラーメッセージは最後にまとめて返される。
  • required:失敗しても直ちに中断せず、スタック内の残りのモジュールをすべて実行した上で、最終的に「失敗」判定を下す(情報漏洩を防ぐため、どのモジュールで失敗したかは隠蔽される)。
  • sufficient:このモジュールが成功した時点で、それ以前の required が失敗していなければ、即座に認証が成功(またはブロック解除)となる。
  • optional:結果は基本的には無視される(特殊なロギング等を除き、合否判定に影響を与えない)。

要塞化において、パスワードの「複雑性検証」と「ロックアウト制御」は、このスタックの適切な位置にミリ単位の精度で差し込まれなければならない。

pam_pwquality によるパスワード複雑性の強制

脆弱なパスワード(Password123! や組織名など)の登録を防ぐには、かつての pam_cracklib の後継である pam_pwquality を用いる。

多くのシステム管理者は /etc/security/pwquality.conf のデフォルト値をそのまま放置しているが、国家資格レベルやグローバルなセキュリティ基準(PCI-DSS、NIST SP800-63Bなど)を満たすには、メモリ上の辞書攻撃やエントロピー低下を防ぐための厳格なチューニングが必要だ。

/etc/security/pwquality.conf の実践的設定例

以下の設定は、単なる文字数制限だけでなく、文字種の強制と辞書攻撃に対する耐性を極限まで高めた実戦仕様である。

# /etc/security/pwquality.conf
# 最小文字数を14文字に設定(NIST推奨値、ブルートフォース耐性の担保)
minlen = 14

# 英大文字、英小文字、数字、記号の4種類の文字種のうち、最低3種類を必須化
minclass = 3

# 同一文字の連続(例: "aaa")を最大2回までに制限
maxrepeat = 2

# ユーザー名を含む、または逆順にした文字列がパスワードに含まれることを禁止
gecoscheck = 1

# パスワード変更時に、旧パスワードから最低4文字以上変更されていない場合は拒否
difok = 4

# システムの辞書ファイル(通常 /usr/share/dict/words)や一般的なパスワード辞書との照合を有効化
# (※ libcrack または pwquality が辞書を認識していることが前提)
# dictpath = /usr/share/dict/words

この設定を適用しても、PAM側のスタックで正しく呼び出されていなければ意味がない。/etc/pam.d/system-auth や /etc/pam.d/password-auth(RHEL/CentOS系)における呼び出し順序を確認し、以下のように pam_unix.so の直前に配置する。

# /etc/pam.d/password-auth の抜粋
# パスワード変更時(passwordスタック)に厳格な品質チェックを挟む
password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok

ここで try_first_pass を指定することで、ユーザーが入力したプレーンテキストのパスワードをメモリ上で効率的に引き回し、二重の入力を防ぎつつ確実にポリシー検証を行わせる。

pam_faillock によるブルートフォース攻撃の完全無力化

かつて主流だった pam_tally2 は、マルチスレッド環境での競合状態(Race Condition)やカウンターファイルの破損といった構造的欠陥を抱えており、現在のモダンなLinuxディストリビューション(RHEL 8以降、Ubuntu 20.04以降など)では非推奨となっている。

現在、インフラの要塞化において標準となるのは pam_faillock である。これは、ログイン失敗の試行回数を監査ログ(/var/run/faillock/ や /var/log/faillock/)に安全に記録し、指定回数を超えた場合にアカウントを一時的、あるいは永続的にロックアウトする。

/etc/security/faillock.conf の要塞化設定

現代のサイバー攻撃者は、数秒間に何千回もリクエストを送るのではなく、IPアドレスを分散させ、人間が入力したかのように数分おきに1回だけログイン試行を行う「低速ブルートフォース(Low-and-Slow Attack)」を多用する。そのため、ロックアウトの持続時間と猶予期間のバランスが極めて重要になる。

# /etc/security/faillock.conf

# 認証失敗を許容する最大回数(3回失敗で即座にロック)
deny = 3

# 失敗試行のカウントをリセットするまでの時間(秒)。
# 最後の失敗からこの時間が経過すれば、失敗カウンターはクリアされる(例: 15分 = 900秒)
fail_interval = 900

# アカウントがロックされる時間(秒)。
# 攻撃者の足を縛るため、最低でも30分(1800秒)、高セキュリティ環境なら2時間(7200秒)に設定
unlock_time = 1800

# rootアカウント自身もロックアウトの対象とするか
# ※注意: 誤設定により全管理者が締め出されるリスクがあるため、踏み台経由の検証を徹底すること
even_deny_root = true

# 正常にログイン成功した際、失敗カウンターをリセットする
reset_on_success = true

# ロックアウトされた旨をログに詳細に出力する
dir = /var/run/faillock

PAMスタック(auth および account)への統合

pam_faillock は、認証前(auth)とアカウント状態の確認時(account)の両方のフェーズに組み込む必要がある。

# /etc/pam.d/system-auth の auth スタックの配置例
# 1. まず失敗カウンターをチェックし、ロック中なら即座に拒否
auth        required      pam_faillock.so preauth silent

# 2. 通常の認証モジュール群(password-auth や pam_unix など)
auth        [success=1 default=bad] pam_unix.so
auth        sufficient    pam_sss.so

# 3. 認証失敗時に失敗カウンターをインクリメント
auth        [default=die] pam_faillock.so authfail

# 4. 認証成功時にカウンターをリセット
auth        sufficient    pam_faillock.so authsucc

さらに、account スタックにも忘れずに記述する。これを怠ると、ssh では弾かれても、cron や内部サービス経由でバイパスされる致命的な隙が生まれる。

# /etc/pam.d/system-auth の account スタック
account     required      pam_faillock.so
account     required      pam_unix.so
account     sufficient    pam_localuser.so

実戦的インシデントハンドリング:ロックアウトされた管理者の救出

厳格な pam_faillock を導入した数日後、必ずと言っていいほど「開発者が誤ったパスワードを連打して自身をロックアウトし、サーバーにログインできなくなった」というインシデントが発生する。チーフセキュリティエンジニアとして、冷静かつ迅速にシステムを復旧させる手順を身につけておかなければならない。

攻撃者による踏み台からの不正アクセスと、正規管理者のインシデントを誤認してはならないが、緊急時の解除コマンドはインフラ管理者にとっての必須知識である。

1. 特定ユーザーの失敗ステータスの確認

現在のロック状態や失敗回数を監査するコマンド:

# ターゲットユーザー(例: developer01)の失敗履歴を確認
faillock --user developer01

出力結果例:

Vulnerability/Lock status for user developer01:
--
    When            Type  Source                Port
    202X-10-25 10:12:05  T-Auth  192.168.10.55         ssh
    202X-10-25 10:12:12  T-Auth  192.168.10.55         ssh
    202X-10-25 10:12:18  T-Auth  192.168.10.55         ssh
Lock: YES

2. ロックアウトの強制解除

管理権限を持つ別ルート(シリアルコンソール、クラウドのマネージドコンソール、あるいは別の特権アカウント)からログインし、以下のコマンドでロックを即座にクリアする:

# 該当ユーザーの失敗カウンターをリセットし、ロックを解除
faillock --user developer01 --reset

このコマンドを実行した瞬間、該当ユーザーは再びログイン試行が可能になる。

チーフエンジニアからの総括:セキュリティは「面倒臭さ」の総量である

PAMによるパスワードポリシーの強制とアカウントロックアウトは、セキュリティの「基本中の基本」である。しかし、基本であるが故に、現場のエンジニアが利便性を優先して緩めがちな領域でもある。

「パスワードを複雑にするとユーザーがメモに書き留める」「ロックアウトのせいで作業が止まる」といった現場からの不満は、適切なセキュア・オンボーディング(パスワードマネージャーの全社導入など)によって解決すべきであり、OSのセキュリティレベルを下げる言い訳にはならない。

サイバー攻撃者は、人間が作り出す「わずかな油断」と「設定の不備」を常に機械的な冷徹さでスキャンしている。PAMの細部にまでこだわり抜いた要塞化こそが、自動化された脅威の海から自社インフラを守り抜くための、最も確実な防壁となるのだ。

コメント

タイトルとURLをコピーしました