Добавление второго фактора в скрипт входа в систему

КибербезопасностьBeginner
Практиковаться сейчас

Введение

Многофакторная аутентификация (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

Вы должны увидеть этот детерминированный шестизначный код:

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: 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-код, потребовали этот код в скрипте входа и протестировали как случаи сбоя, так и успешного входа.

Основная идея безопасности заключается в том, что украденного пароля недостаточно, если требуется независимое второе доказательство подлинности.