ログインスクリプトへの二要素認証の追加

サイバーセキュリティBeginner
オンラインで実践に進む

はじめに

多要素認証(Multi-factor authentication, MFA)は、複数の証明を要求することでアカウント侵害のリスクを低減します。パスワードは「ユーザーが知っているもの」です。二つ目の要素には、別のデバイス、アプリ、ファイル、またはハードウェアトークンから取得するコードなどが利用されます。

この実験では、パスワードのみで動作する Python ログインスクリプトから始め、シンプルな二つ目の要素を追加します。初心者にも分かりやすく、かつ結果が予測可能なものにするため、二つ目の要素には保護されたシークレットファイルから生成される HMAC ベースのコードを使用します。

パスワードのみのログインをテストする

このステップでは、初期状態のログインスクリプトを実行します。これはユーザー名とパスワードのみを確認するもので、証明が「ユーザーが知っているもの」一つしかないため、単要素認証となります。

プロジェクトディレクトリに移動します:

cd /home/labex/project

準備されたファイルを確認します:

ls -l

ユーザーデータベースを表示します:

cat users.json

パスワードのみのログインを成功させてみます:

python3 login.py alice wonderland

出力は以下のようになります:

login ok

次に、間違ったパスワードを試します:

python3 login.py alice wrong

このコマンドは login failed と表示するはずです。ログイン失敗時はゼロ以外のステータスで終了しますが、このテストでは正常な挙動です。

初期状態のチェッカーを実行します:

python3 mfa-check.py password-only

期待される結果は以下の通りです:

password-only: complete

二要素認証用のシークレットを作成する

このステップでは、二つ目の要素のために個別のシークレットを作成します。実際の MFA システムでは、このシークレットは認証アプリやハードウェアトークン内に保持されます。この実験では、動作を簡単に確認・検証できるようにローカルファイルとして扱います。

シークレット値を作成します:

printf 'labex-mfa-secret-2026\n' > mfa_secret.txt

所有者のみが読み書きできるようにファイルの権限を制限します:

chmod 600 mfa_secret.txt

権限を確認します:

ls -l mfa_secret.txt

権限ビットが -rw------- で始まっているはずです:

-rw------- ... mfa_secret.txt

シークレットのチェッカーを実行します:

python3 mfa-check.py secret

期待される結果は以下の通りです:

secret: complete

ログインコードを生成する

このステップでは、小さなコード生成ツールを作成します。HMAC-SHA256 を使用して、シークレットファイルとユーザー名を組み合わせます。HMAC は、同じ入力とシークレットからは同じ短いコードが生成される一方で、シークレットを知らない第三者が簡単にコードを作成できないため、この用途に適しています。

generate-code.py を作成します:

cat > generate-code.py <<'PY'
#!/usr/bin/env python3
import hmac
import hashlib
import sys
from pathlib import Path

if len(sys.argv) != 2:
    print("usage: generate-code.py USERNAME")
    raise SystemExit(2)

username = sys.argv[1]
secret = Path("mfa_secret.txt").read_text().strip().encode()
digest = hmac.new(secret, username.encode(), hashlib.sha256).hexdigest()
print(digest[:6])
PY

実行権限を付与します:

chmod +x generate-code.py

alice 用のコードを生成します:

python3 generate-code.py alice

以下のような 6 文字の確定的なコードが表示されるはずです:

21f18f

ジェネレーターのチェッカーを実行します:

python3 mfa-check.py generator

期待される結果は以下の通りです:

generator: complete

二要素認証を強制する

このステップでは、パスワードと生成されたコードの両方を要求するようにログインスクリプトを置き換えます。これが MFA における重要な変更点であり、パスワードだけでは不十分になります。

login.py を MFA 対応版に置き換えます:

cat > login.py <<'PY'
#!/usr/bin/env python3
import hmac
import hashlib
import json
import sys
from pathlib import Path

USERS = Path("users.json")
SECRET = Path("mfa_secret.txt")

def expected_code(username):
    secret = SECRET.read_text().strip().encode()
    digest = hmac.new(secret, username.encode(), hashlib.sha256).hexdigest()
    return digest[:6]

def main():
    if len(sys.argv) != 4:
        print("usage: login.py USERNAME PASSWORD MFA_CODE")
        return 2

    username, password, code = sys.argv[1], sys.argv[2], sys.argv[3]
    users = json.loads(USERS.read_text())
    if users.get(username, {}).get("password") != password:
        print("login failed")
        return 1

    if not hmac.compare_digest(code, expected_code(username)):
        print("mfa failed")
        return 1

    print("login ok")
    return 0

if __name__ == "__main__":
    raise SystemExit(main())
PY

スクリプトに実行権限があることを確認します:

chmod +x login.py

新しいコードを生成し、シェル変数に保存します:

CODE="$(python3 generate-code.py alice)"

正しいパスワードとコードを組み合わせて使用します:

python3 login.py alice wonderland "$CODE"

出力は以下のようになります:

login ok

MFA 強制のチェッカーを実行します:

python3 mfa-check.py enforced

期待される結果は以下の通りです:

enforced: complete

MFA の失敗と成功をテストする

このステップでは、最終的なログイン動作をテストします。正しいパスワードでも二つ目の要素がない場合は失敗し、間違ったコードでも失敗し、正しいパスワードと正しいコードの組み合わせのみが成功することを確認します。

まず、以前のパスワードのみのコマンドを試します:

python3 login.py alice wonderland || true

MFA コードが不足しているため、新しい使用方法(usage)メッセージが表示されるはずです:

usage: login.py USERNAME PASSWORD MFA_CODE

間違ったコードを試します:

python3 login.py alice wonderland 000000 || true

出力は以下のようになります:

mfa failed

正しいコードを生成してログインします:

CODE="$(python3 generate-code.py alice)"
python3 login.py alice wonderland "$CODE"

出力は以下のようになります:

login ok

すべてのチェッカーを実行します:

python3 mfa-check.py all

最終的な出力には、すべての MFA コンポーネントが完了したことが表示されます:

secret: complete
generator: complete
enforced: complete

まとめ

パスワードのみのログインスクリプトを、シンプルな多要素認証フローに変更しました。個別の MFA シークレットを作成し、HMAC ベースのコードを生成し、ログインスクリプトでそのコードを必須とし、失敗と成功の両方のケースをテストしました。

セキュリティの主要な考え方は、独立した二つ目の証明が必要な場合、パスワードが盗まれただけでは不十分であるということです。