Compare commits
4 Commits
66b6a5c309
...
b6b5e16d63
| Author | SHA1 | Date | |
|---|---|---|---|
| b6b5e16d63 | |||
| bc1299e966 | |||
| 60a4471a8c | |||
| 07e02401eb |
1
lab2/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
1
lab2/.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.14
|
||||
99
lab2/README.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Lab 2 — Authentication, Authorization & Brute Force Research
|
||||
|
||||
Система доступа к конфиденциальным данным с управлением пользователями и исследованием стойкости паролей.
|
||||
|
||||
## Структура каталогов
|
||||
|
||||
```
|
||||
$PRACTICE2_DIR/ # по умолчанию /usr/local/practice2
|
||||
├── etc/passwd # логин:sha256:id:права:ФИО
|
||||
├── confdata/ # конфиденциальные файлы
|
||||
├── bin/ # утилиты (usermgr, confaccess, bruteforce)
|
||||
└── log/ # usermgr.log, access.log
|
||||
```
|
||||
|
||||
Базовый каталог задаётся через переменную окружения `PRACTICE2_DIR`.
|
||||
Если переменная не задана — используется `/usr/local/practice2`.
|
||||
|
||||
## Установка
|
||||
|
||||
```bash
|
||||
chmod +x setup.sh
|
||||
|
||||
# для пути по умолчанию (/usr/local/practice2) нужен root:
|
||||
sudo ./setup.sh
|
||||
|
||||
# для тестирования без root:
|
||||
PRACTICE2_DIR=/tmp/practice2 ./setup.sh
|
||||
```
|
||||
|
||||
Скрипт создаёт структуру каталогов, копирует утилиты в `bin/` и выставляет права доступа.
|
||||
|
||||
### Добавить bin во временный PATH
|
||||
|
||||
```bash
|
||||
export PATH="/usr/local/practice2/bin:$PATH"
|
||||
|
||||
# или для тестовой директории:
|
||||
export PATH="/tmp/practice2/bin:$PATH"
|
||||
```
|
||||
|
||||
После этого утилиты доступны без полного пути:
|
||||
|
||||
```bash
|
||||
usermgr add alice
|
||||
confaccess
|
||||
bruteforce alice
|
||||
```
|
||||
|
||||
## Использование
|
||||
|
||||
### usermgr — управление пользователями
|
||||
|
||||
```bash
|
||||
usermgr add alice # добавить пользователя (интерактивный ввод)
|
||||
usermgr list # список пользователей
|
||||
usermgr edit alice --permissions rw
|
||||
usermgr edit alice --full-name "Иванов Иван"
|
||||
usermgr passwd alice # сменить пароль
|
||||
usermgr delete alice # удалить пользователя
|
||||
```
|
||||
|
||||
Права: `r` — чтение, `w` — запись, `d` — удаление.
|
||||
|
||||
Требования к паролю: первый символ — буква (A–Z, a–z), далее — буквы, цифры и `!@#$%^&*()`.
|
||||
|
||||
### confaccess — доступ к данным
|
||||
|
||||
```bash
|
||||
confaccess
|
||||
# или с явным указанием базовой директории:
|
||||
PRACTICE2_DIR=/tmp/practice2 confaccess
|
||||
```
|
||||
|
||||
После аутентификации доступны команды:
|
||||
|
||||
```
|
||||
create <file> создать новый пустой файл в confdata [requires: w]
|
||||
read <file> вывести содержимое файла [requires: r]
|
||||
append <file> <text> дописать строку в файл [requires: w]
|
||||
copy <src> <dst> скопировать файл в confdata [requires: r, w]
|
||||
remove <file> удалить файл из confdata [requires: d]
|
||||
help / exit
|
||||
```
|
||||
|
||||
Пути к файлам указываются относительно `confdata/` (или абсолютные).
|
||||
Для `copy`: src — любой путь, dst — внутри confdata, перезапись запрещена.
|
||||
Выход — `exit` или Ctrl+C.
|
||||
|
||||
### bruteforce — взлом пароля
|
||||
|
||||
```bash
|
||||
bruteforce alice
|
||||
bruteforce alice --max-length 4
|
||||
```
|
||||
|
||||
Алгоритм хэширования: **SHA-256**.
|
||||
Перебор выполняется напрямую по хэшу из passwd-файла.
|
||||
Фиксируется время перебора и количество итераций до нахождения пароля.
|
||||
Перебор останавливается автоматически при достижении лимита 8 часов.
|
||||
244
lab2/access.py
Normal file
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env python3
|
||||
import getpass
|
||||
import hashlib
|
||||
import shutil
|
||||
import signal
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from config import CONFDATA_DIR, LOG_DIR, PASSWD_FILE
|
||||
|
||||
HELP_TEXT = """\
|
||||
Commands:
|
||||
create <file> create new empty file [requires: w]
|
||||
read <file> print file contents [requires: r]
|
||||
append <file> <text> append text line to file [requires: w]
|
||||
copy <src> <dst> copy file into confdata [requires: r, w]
|
||||
remove <file> delete file from confdata [requires: d]
|
||||
help show this help
|
||||
exit exit"""
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
return hashlib.sha256(password.encode("ascii")).hexdigest()
|
||||
|
||||
|
||||
def log_action(login: str, action: str) -> None:
|
||||
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
timestamp = datetime.now().isoformat(sep=" ", timespec="seconds")
|
||||
with open(LOG_DIR / "access.log", "a") as f:
|
||||
f.write(f"{timestamp} [{login}] {action}\n")
|
||||
|
||||
|
||||
def read_users() -> dict[str, dict]:
|
||||
users: dict[str, dict] = {}
|
||||
if not PASSWD_FILE.exists():
|
||||
return users
|
||||
with open(PASSWD_FILE) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split(":", 4)
|
||||
if len(parts) != 5:
|
||||
continue
|
||||
users[parts[0]] = {
|
||||
"password_hash": parts[1],
|
||||
"id": parts[2],
|
||||
"permissions": parts[3],
|
||||
"full_name": parts[4],
|
||||
}
|
||||
return users
|
||||
|
||||
|
||||
def authenticate() -> tuple[str, dict]:
|
||||
users = read_users()
|
||||
while True:
|
||||
try:
|
||||
login = input("Login: ").strip()
|
||||
password = getpass.getpass("Password: ")
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print("\nBye.")
|
||||
sys.exit(0)
|
||||
|
||||
user = users.get(login)
|
||||
if user and user["password_hash"] == hash_password(password):
|
||||
return login, user
|
||||
log_action(login if login else "-", "LOGIN_FAILED")
|
||||
print("Invalid credentials. Try again.")
|
||||
|
||||
|
||||
def confdata_path(arg: str) -> Path:
|
||||
p = Path(arg)
|
||||
if not p.is_absolute():
|
||||
p = CONFDATA_DIR / p
|
||||
return p.resolve()
|
||||
|
||||
|
||||
def is_in_confdata(path: Path) -> bool:
|
||||
try:
|
||||
path.relative_to(CONFDATA_DIR.resolve())
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def cmd_read(args: list[str], login: str, perms: str) -> None:
|
||||
if "r" not in perms:
|
||||
print("Permission denied (requires: r)")
|
||||
return
|
||||
if len(args) != 1:
|
||||
print("Usage: read <file>")
|
||||
return
|
||||
path = confdata_path(args[0])
|
||||
if not is_in_confdata(path):
|
||||
print("Access denied: file must be inside confdata")
|
||||
return
|
||||
if not path.exists():
|
||||
print(f"File not found: {path.name}")
|
||||
return
|
||||
print(path.read_text(), end="")
|
||||
log_action(login, f"READ {path}")
|
||||
|
||||
|
||||
def cmd_create(args: list[str], login: str, perms: str) -> None:
|
||||
if "w" not in perms:
|
||||
print("Permission denied (requires: w)")
|
||||
return
|
||||
if len(args) != 1:
|
||||
print("Usage: create <file>")
|
||||
return
|
||||
path = confdata_path(args[0])
|
||||
if not is_in_confdata(path):
|
||||
print("Access denied: file must be inside confdata")
|
||||
return
|
||||
if path.exists():
|
||||
print(f"File already exists: {path.name}")
|
||||
return
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.touch()
|
||||
log_action(login, f"CREATE {path}")
|
||||
print("Done.")
|
||||
|
||||
|
||||
def cmd_append(args: list[str], login: str, perms: str) -> None:
|
||||
if "w" not in perms:
|
||||
print("Permission denied (requires: w)")
|
||||
return
|
||||
if len(args) < 2:
|
||||
print("Usage: append <file> <text>")
|
||||
return
|
||||
path = confdata_path(args[0])
|
||||
if not is_in_confdata(path):
|
||||
print("Access denied: file must be inside confdata")
|
||||
return
|
||||
if not path.exists():
|
||||
print(f"File not found: {path.name}. Use 'create' first.")
|
||||
return
|
||||
text = " ".join(args[1:])
|
||||
with open(path, "a") as f:
|
||||
f.write(text + "\n")
|
||||
log_action(login, f"APPEND {path}")
|
||||
print("Done.")
|
||||
|
||||
|
||||
def cmd_copy(args: list[str], login: str, perms: str) -> None:
|
||||
if "r" not in perms or "w" not in perms:
|
||||
print("Permission denied (requires: r, w)")
|
||||
return
|
||||
if len(args) != 2:
|
||||
print("Usage: copy <src> <dst>")
|
||||
return
|
||||
|
||||
src = Path(args[0]).resolve()
|
||||
dst = confdata_path(args[1])
|
||||
|
||||
if not is_in_confdata(dst):
|
||||
print("Access denied: destination must be inside confdata")
|
||||
return
|
||||
if dst.is_dir():
|
||||
dst = dst / src.name
|
||||
if dst.exists():
|
||||
print(f"Destination already exists: {dst.name}")
|
||||
return
|
||||
if not src.exists():
|
||||
print(f"Source not found: {args[0]}")
|
||||
return
|
||||
if src.is_dir():
|
||||
print("Copying directories is not supported")
|
||||
return
|
||||
|
||||
shutil.copy2(src, dst)
|
||||
log_action(login, f"COPY {src} -> {dst}")
|
||||
print("Done.")
|
||||
|
||||
|
||||
def cmd_remove(args: list[str], login: str, perms: str) -> None:
|
||||
if "d" not in perms:
|
||||
print("Permission denied (requires: d)")
|
||||
return
|
||||
if len(args) != 1:
|
||||
print("Usage: remove <file>")
|
||||
return
|
||||
path = confdata_path(args[0])
|
||||
if not is_in_confdata(path):
|
||||
print("Access denied: file must be inside confdata")
|
||||
return
|
||||
if not path.exists():
|
||||
print(f"File not found: {path.name}")
|
||||
return
|
||||
path.unlink()
|
||||
log_action(login, f"REMOVE {path}")
|
||||
print("Done.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
signal.signal(signal.SIGINT, lambda _s, _f: (print("\nBye."), sys.exit(0)))
|
||||
|
||||
login, user = authenticate()
|
||||
perms = user["permissions"]
|
||||
full_name = user["full_name"]
|
||||
|
||||
log_action(login, "LOGIN")
|
||||
print(f"\nПривет, {full_name}")
|
||||
print(HELP_TEXT)
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = input(f"\n{login}> ").strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
log_action(login, "EXIT")
|
||||
print("\nBye.")
|
||||
break
|
||||
|
||||
if not line:
|
||||
continue
|
||||
|
||||
parts = line.split()
|
||||
command, args = parts[0], parts[1:]
|
||||
|
||||
if command == "exit":
|
||||
log_action(login, "EXIT")
|
||||
print("Bye.")
|
||||
break
|
||||
elif command == "help":
|
||||
print(HELP_TEXT)
|
||||
elif command == "create":
|
||||
cmd_create(args, login, perms)
|
||||
elif command == "read":
|
||||
cmd_read(args, login, perms)
|
||||
elif command == "append":
|
||||
cmd_append(args, login, perms)
|
||||
elif command == "copy":
|
||||
cmd_copy(args, login, perms)
|
||||
elif command == "remove":
|
||||
cmd_remove(args, login, perms)
|
||||
else:
|
||||
print(f"Unknown command: {command!r}. Type 'help' for available commands.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
106
lab2/bruteforce.py
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import hashlib
|
||||
import itertools
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from config import PASSWD_FILE
|
||||
|
||||
CHARSET = string.ascii_letters + string.digits + "!@#$%^&*()"
|
||||
FIRST_CHARS = string.ascii_letters
|
||||
|
||||
MAX_HOURS = 8
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
return hashlib.sha256(password.encode("ascii")).hexdigest()
|
||||
|
||||
|
||||
def get_target_hash(login: str) -> str | None:
|
||||
if not PASSWD_FILE.exists():
|
||||
return None
|
||||
with open(PASSWD_FILE) as f:
|
||||
for line in f:
|
||||
parts = line.strip().split(":", 4)
|
||||
if len(parts) == 5 and parts[0] == login:
|
||||
return parts[1]
|
||||
return None
|
||||
|
||||
|
||||
def max_combinations(length: int) -> int:
|
||||
if length == 1:
|
||||
return len(FIRST_CHARS)
|
||||
return len(FIRST_CHARS) * (len(CHARSET) ** (length - 1))
|
||||
|
||||
|
||||
def brute_force_length(target_hash: str, length: int) -> tuple[str, int, float] | None:
|
||||
count = 0
|
||||
start = time.perf_counter()
|
||||
|
||||
if length == 1:
|
||||
for first in FIRST_CHARS:
|
||||
count += 1
|
||||
if hash_password(first) == target_hash:
|
||||
return first, count, time.perf_counter() - start
|
||||
return None
|
||||
|
||||
for first in FIRST_CHARS:
|
||||
for rest in itertools.product(CHARSET, repeat=length - 1):
|
||||
if time.perf_counter() - start > MAX_HOURS * 3600:
|
||||
return None
|
||||
count += 1
|
||||
password = first + "".join(rest)
|
||||
if hash_password(password) == target_hash:
|
||||
return password, count, time.perf_counter() - start
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Brute force password cracker (SHA-256)")
|
||||
parser.add_argument("login", help="Target username")
|
||||
parser.add_argument(
|
||||
"--max-length",
|
||||
type=int,
|
||||
default=6,
|
||||
help="Maximum password length to try (default: 6)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
target_hash = get_target_hash(args.login)
|
||||
if not target_hash:
|
||||
print(f"User '{args.login}' not found in passwd file.")
|
||||
return
|
||||
|
||||
print(f"Target: {args.login}")
|
||||
print(f"Hash: {target_hash}")
|
||||
print(f"Charset size: {len(CHARSET)} ({len(FIRST_CHARS)} valid for first char)")
|
||||
print(f"Algorithm: SHA-256")
|
||||
print()
|
||||
|
||||
for length in range(1, args.max_length + 1):
|
||||
total = max_combinations(length)
|
||||
print(f"Length {length}: max {total:>15,} combinations")
|
||||
|
||||
result = brute_force_length(target_hash, length)
|
||||
|
||||
if result is not None:
|
||||
password, count, elapsed = result
|
||||
print(f" >>> FOUND: '{password}'")
|
||||
print(f" Iterations: {count:,}")
|
||||
print(f" Time: {elapsed:.4f}s")
|
||||
print(f" Speed: {count / elapsed:,.0f} hashes/s")
|
||||
return
|
||||
else:
|
||||
print(f" Not found at length {length} (timeout or exhausted)")
|
||||
|
||||
print(f"\nPassword not found within length {args.max_length}.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
11
lab2/config.py
Normal file
@@ -0,0 +1,11 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(os.environ.get("PRACTICE2_DIR", "/usr/local/practice2"))
|
||||
|
||||
ETC_DIR = BASE_DIR / "etc"
|
||||
CONFDATA_DIR = BASE_DIR / "confdata"
|
||||
BIN_DIR = BASE_DIR / "bin"
|
||||
LOG_DIR = BASE_DIR / "log"
|
||||
|
||||
PASSWD_FILE = ETC_DIR / "passwd"
|
||||
277
lab2/lab2.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Практическая работа №2
|
||||
|
||||
по дисциплине «Защита информации»
|
||||
|
||||
**Тема работы:** «Разработка и исследование системы аутентификации и авторизации»
|
||||
**Преподаватель:** Силиненко А.В.
|
||||
**Email:** [a_silinenko@mail.ru](mailto:a_silinenko@mail.ru)
|
||||
|
||||
---
|
||||
|
||||
## 1. Цели работы
|
||||
|
||||
* Разработать систему доступа пользователей к конфиденциальным данным;
|
||||
* Исследовать стойкость паролей к атаке методом грубой силы.
|
||||
|
||||
---
|
||||
|
||||
## 2. Задачи работы
|
||||
|
||||
### 2.1.
|
||||
|
||||
При необходимости установить на компьютер целевую ОС (Linux или MacOS), в которой производится разработка и использование системы.
|
||||
|
||||
### 2.2.
|
||||
|
||||
Разработать систему доступа пользователей к конфиденциальным данным, включающую:
|
||||
|
||||
* Выделенный каталог для хранения всех файлов системы;
|
||||
* Утилиту для работы с данными аутентификации и авторизации (паролями и правами доступа);
|
||||
* Утилиту доступа к конфиденциальным данным, обеспечивающую аутентификацию и авторизацию пользователя.
|
||||
|
||||
### 2.3.
|
||||
|
||||
Разработать программу взлома паролей методом грубой силы и исследовать стойкость паролей в зависимости от длины пароля и алгоритма хэширования.
|
||||
|
||||
---
|
||||
|
||||
## 3. Требования к работе
|
||||
|
||||
### 3.1. ОС
|
||||
|
||||
Работа выполняется в ОС **Linux** или **MacOS**.
|
||||
|
||||
Необходим доступ с правами суперпользователя.
|
||||
Если доступа нет — установить гипервизор **VirtualBox** ([https://www.virtualbox.org/](https://www.virtualbox.org/)) и развернуть гостевую ОС.
|
||||
|
||||
Допускается установка ОС как второй системы без VirtualBox.
|
||||
|
||||
---
|
||||
|
||||
### 3.2. Структура каталогов
|
||||
|
||||
Необходимо создать дерево каталогов:
|
||||
|
||||
```
|
||||
/usr/local/practice2/
|
||||
├── etc # хранение данных аутентификации и авторизации
|
||||
├── confdata # хранение конфиденциальных файлов
|
||||
├── bin # разработанные утилиты
|
||||
└── log # файлы регистрации
|
||||
```
|
||||
|
||||
**Права доступа:** чтение, запись и выполнение только для пользователя `root`.
|
||||
|
||||
---
|
||||
|
||||
### 3.3. Требования к утилите управления пользователями
|
||||
|
||||
#### Файл хранения данных
|
||||
|
||||
Файл:
|
||||
|
||||
```
|
||||
/usr/local/practice2/etc/passwd
|
||||
```
|
||||
|
||||
Структура записи:
|
||||
|
||||
```
|
||||
<логин>:<хэш_пароля>:<идентификатор>:<права>:<ФИО>
|
||||
```
|
||||
|
||||
Одна строка — один пользователь.
|
||||
|
||||
**Права на файл:** чтение и запись только для `root`.
|
||||
|
||||
#### Права доступа
|
||||
|
||||
* `r` — чтение
|
||||
* `w` — запись
|
||||
* `d` — удаление
|
||||
|
||||
#### Требования к функционалу
|
||||
|
||||
Утилита должна обеспечивать:
|
||||
|
||||
* Добавление нового пользователя:
|
||||
|
||||
* логин
|
||||
* ФИО
|
||||
* права доступа
|
||||
* пароль + подтверждение
|
||||
* Проверку корректности данных
|
||||
* Хранение пароля в виде **хэш-значения**
|
||||
* Использование алгоритма хэширования согласно индивидуальному заданию
|
||||
* Редактирование существующего пользователя
|
||||
* Изменение пароля
|
||||
* Удаление пользователя
|
||||
* Регистрацию всех действий с файлом `passwd`
|
||||
|
||||
#### Требования к паролю
|
||||
|
||||
* Кодировка: ASCII
|
||||
* Разрешены:
|
||||
|
||||
* A–Z
|
||||
* a–z
|
||||
* 0–9
|
||||
* `!@#$%^&*()`
|
||||
* Первый символ не может быть цифрой или спецсимволом
|
||||
|
||||
---
|
||||
|
||||
### 3.4. Требования к утилите доступа к конфиденциальным данным
|
||||
|
||||
#### Авторизация
|
||||
|
||||
При запуске:
|
||||
|
||||
1. Запрос логина
|
||||
2. Запрос пароля
|
||||
3. Проверка корректности
|
||||
|
||||
Если данные корректны:
|
||||
|
||||
* Вывод: `Привет, <ФИО>`
|
||||
* Краткая справка
|
||||
* Приглашение к вводу команд
|
||||
|
||||
Если данные некорректны:
|
||||
|
||||
* Повторный запрос
|
||||
|
||||
Остановка — по сигналу **SIGINT (Ctrl+C)**.
|
||||
|
||||
---
|
||||
|
||||
### Поддерживаемые команды
|
||||
|
||||
| Команда | Описание | Требуемые права |
|
||||
| -------- | ------------------------ | --------------- |
|
||||
| `read` | Вывод содержимого файла | r |
|
||||
| `append` | Добавление данных в файл | w |
|
||||
| `copy` | Копирование файла | r + w |
|
||||
| `remove` | Удаление файла | d |
|
||||
| `exit` | Выход | — |
|
||||
| `help` | Справка | — |
|
||||
|
||||
#### Ограничения copy
|
||||
|
||||
* Разрешено копирование:
|
||||
|
||||
* в каталог `confdata`
|
||||
* внутри `confdata`
|
||||
* Запрещено:
|
||||
|
||||
* из `confdata` в другие каталоги
|
||||
* перезапись существующих файлов внутри `confdata`
|
||||
|
||||
#### Дополнительные требования
|
||||
|
||||
* Утилита доступна для запуска всем пользователям ОС
|
||||
* Все действия с конфиденциальными данными логируются
|
||||
|
||||
---
|
||||
|
||||
### 3.5. Программа взлома паролей
|
||||
|
||||
Должна:
|
||||
|
||||
* Запускать утилиту доступа
|
||||
* Перебирать пароли методом brute force
|
||||
* Учитывать используемый алгоритм хэширования
|
||||
* Фиксировать:
|
||||
|
||||
* время перебора
|
||||
* количество итераций до взлома
|
||||
|
||||
---
|
||||
|
||||
### 3.6. Общие требования
|
||||
|
||||
* Язык программирования — любой
|
||||
* Код должен быть снабжен комментариями
|
||||
|
||||
---
|
||||
|
||||
### 3.7. Требования к исследованию
|
||||
|
||||
Провести исследование для длин паролей:
|
||||
|
||||
```
|
||||
3, 4, 5, 6, 7, 8 символов
|
||||
```
|
||||
|
||||
Необходимо:
|
||||
|
||||
* Рассчитать максимальное количество итераций
|
||||
* По экспериментам (3–4 символа) оценить время для 5–8
|
||||
* Проверить теорию экспериментально
|
||||
* Прервать эксперимент, если длительность > 8 часов
|
||||
* Проводить тестирование без активных задач на ПК
|
||||
|
||||
---
|
||||
|
||||
## 4. Требования к отчету
|
||||
|
||||
В отчете необходимо указать:
|
||||
|
||||
* Актуальность темы
|
||||
|
||||
* Цель и задачи
|
||||
|
||||
* Требования к системе
|
||||
|
||||
* Характеристики ПК (процессор, память)
|
||||
|
||||
* ОС и среду разработки
|
||||
|
||||
* Используемый язык
|
||||
|
||||
* Алгоритм хэширования
|
||||
|
||||
* Примеры сборки (если применимо)
|
||||
|
||||
* Примеры работы утилит
|
||||
|
||||
* Пример расчета количества итераций и времени взлома
|
||||
|
||||
* Таблицу или график с результатами:
|
||||
|
||||
* рассчитанное количество итераций и время
|
||||
* полученные экспериментальные данные
|
||||
|
||||
* Выводы
|
||||
|
||||
---
|
||||
|
||||
## 5. Справочная информация
|
||||
|
||||
### Поддержка алгоритмов хэширования
|
||||
|
||||
**Linux:**
|
||||
|
||||
* `md5sum`
|
||||
* `sha1sum`
|
||||
* `sha256sum`
|
||||
* `sha512sum`
|
||||
* `b2sum`
|
||||
* библиотека `openssl`
|
||||
|
||||
**C++:**
|
||||
|
||||
* `std::hash`
|
||||
|
||||
**Python:**
|
||||
|
||||
* модуль `hashlib`
|
||||
|
||||
**Java:**
|
||||
|
||||
* `java.security.MessageDigest`
|
||||
* Spring Security
|
||||
|
||||
**Go:**
|
||||
|
||||
* пакет `hash`
|
||||
19
lab2/pyproject.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[project]
|
||||
name = "lab2"
|
||||
version = "0.1.0"
|
||||
description = "Authentication, authorization and brute force research system"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.14"
|
||||
dependencies = []
|
||||
|
||||
[project.scripts]
|
||||
usermgr = "usermgr:main"
|
||||
access = "access:main"
|
||||
bruteforce = "bruteforce:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
include = ["*.py"]
|
||||
38
lab2/setup.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BASE_DIR="${PRACTICE2_DIR:-/usr/local/practice2}"
|
||||
|
||||
echo "Setting up directory structure at: $BASE_DIR"
|
||||
|
||||
mkdir -p "$BASE_DIR/etc" "$BASE_DIR/confdata" "$BASE_DIR/bin" "$BASE_DIR/log"
|
||||
|
||||
touch "$BASE_DIR/etc/passwd"
|
||||
|
||||
cp "$SCRIPT_DIR/config.py" "$BASE_DIR/bin/config.py"
|
||||
cp "$SCRIPT_DIR/usermgr.py" "$BASE_DIR/bin/usermgr"
|
||||
cp "$SCRIPT_DIR/access.py" "$BASE_DIR/bin/confaccess"
|
||||
cp "$SCRIPT_DIR/bruteforce.py" "$BASE_DIR/bin/bruteforce"
|
||||
|
||||
chmod +x "$BASE_DIR/bin/usermgr" "$BASE_DIR/bin/confaccess" "$BASE_DIR/bin/bruteforce"
|
||||
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
chmod 700 "$BASE_DIR" "$BASE_DIR/etc" "$BASE_DIR/confdata" "$BASE_DIR/bin" "$BASE_DIR/log"
|
||||
chmod 600 "$BASE_DIR/etc/passwd"
|
||||
echo "Permissions set (root-only)."
|
||||
else
|
||||
echo "Warning: not running as root; skipping permission hardening."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done. Directory layout:"
|
||||
ls -la "$BASE_DIR"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " $BASE_DIR/bin/usermgr add <login>"
|
||||
echo " $BASE_DIR/bin/confaccess"
|
||||
echo " $BASE_DIR/bin/bruteforce <login>"
|
||||
echo ""
|
||||
echo "To add bin to PATH temporarily:"
|
||||
echo " export PATH=\"$BASE_DIR/bin:\$PATH\""
|
||||
246
lab2/usermgr.py
Normal file
@@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import getpass
|
||||
import hashlib
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from config import LOG_DIR, PASSWD_FILE
|
||||
|
||||
ALLOWED_CHARS = set(
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()"
|
||||
)
|
||||
FIRST_CHAR_ALLOWED = set("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
||||
VALID_PERM_CHARS = set("rwd")
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
return hashlib.sha256(password.encode("ascii")).hexdigest()
|
||||
|
||||
|
||||
def validate_password(password: str) -> str | None:
|
||||
if not password:
|
||||
return "password cannot be empty"
|
||||
if password[0] not in FIRST_CHAR_ALLOWED:
|
||||
return "first character must be a letter (A-Z, a-z)"
|
||||
invalid = [ch for ch in password if ch not in ALLOWED_CHARS]
|
||||
if invalid:
|
||||
return f"invalid characters: {''.join(set(invalid))!r}"
|
||||
return None
|
||||
|
||||
|
||||
def validate_permissions(perms: str) -> str | None:
|
||||
if not perms:
|
||||
return "permissions cannot be empty"
|
||||
for ch in perms:
|
||||
if ch not in VALID_PERM_CHARS:
|
||||
return f"invalid permission {ch!r}; allowed: r, w, d"
|
||||
if len(set(perms)) != len(perms):
|
||||
return "duplicate permissions"
|
||||
return None
|
||||
|
||||
|
||||
def log_action(action: str) -> None:
|
||||
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
timestamp = datetime.now().isoformat(sep=" ", timespec="seconds")
|
||||
with open(LOG_DIR / "usermgr.log", "a") as f:
|
||||
f.write(f"{timestamp} {action}\n")
|
||||
|
||||
|
||||
def read_users() -> list[dict]:
|
||||
if not PASSWD_FILE.exists():
|
||||
return []
|
||||
users = []
|
||||
with open(PASSWD_FILE) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split(":", 4)
|
||||
if len(parts) != 5:
|
||||
continue
|
||||
users.append(
|
||||
{
|
||||
"login": parts[0],
|
||||
"password_hash": parts[1],
|
||||
"id": parts[2],
|
||||
"permissions": parts[3],
|
||||
"full_name": parts[4],
|
||||
}
|
||||
)
|
||||
return users
|
||||
|
||||
|
||||
def write_users(users: list[dict]) -> None:
|
||||
PASSWD_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(PASSWD_FILE, "w") as f:
|
||||
for u in users:
|
||||
f.write(
|
||||
f"{u['login']}:{u['password_hash']}:{u['id']}:{u['permissions']}:{u['full_name']}\n"
|
||||
)
|
||||
|
||||
|
||||
def find_user(users: list[dict], login: str) -> dict | None:
|
||||
return next((u for u in users if u["login"] == login), None)
|
||||
|
||||
|
||||
def next_uid(users: list[dict]) -> str:
|
||||
if not users:
|
||||
return "1"
|
||||
return str(max(int(u["id"]) for u in users) + 1)
|
||||
|
||||
|
||||
def prompt_password() -> str:
|
||||
while True:
|
||||
password = getpass.getpass("Password: ")
|
||||
err = validate_password(password)
|
||||
if err:
|
||||
print(f"Invalid password: {err}")
|
||||
continue
|
||||
confirm = getpass.getpass("Confirm password: ")
|
||||
if password != confirm:
|
||||
print("Passwords do not match.")
|
||||
continue
|
||||
return password
|
||||
|
||||
|
||||
def cmd_add(args: argparse.Namespace) -> None:
|
||||
users = read_users()
|
||||
login = args.login
|
||||
|
||||
if find_user(users, login):
|
||||
print(f"User '{login}' already exists.")
|
||||
sys.exit(1)
|
||||
|
||||
full_name = input("Full name: ").strip()
|
||||
if not full_name:
|
||||
print("Full name cannot be empty.")
|
||||
sys.exit(1)
|
||||
|
||||
perms = input("Permissions (any combination of r, w, d): ").strip()
|
||||
err = validate_permissions(perms)
|
||||
if err:
|
||||
print(f"Invalid permissions: {err}")
|
||||
sys.exit(1)
|
||||
|
||||
password = prompt_password()
|
||||
|
||||
uid = next_uid(users)
|
||||
users.append(
|
||||
{
|
||||
"login": login,
|
||||
"password_hash": hash_password(password),
|
||||
"id": uid,
|
||||
"permissions": perms,
|
||||
"full_name": full_name,
|
||||
}
|
||||
)
|
||||
write_users(users)
|
||||
log_action(f"ADD login={login} id={uid} permissions={perms} full_name='{full_name}'")
|
||||
print(f"User '{login}' added (id={uid}).")
|
||||
|
||||
|
||||
def cmd_edit(args: argparse.Namespace) -> None:
|
||||
users = read_users()
|
||||
user = find_user(users, args.login)
|
||||
if not user:
|
||||
print(f"User '{args.login}' not found.")
|
||||
sys.exit(1)
|
||||
|
||||
changed = []
|
||||
|
||||
if args.full_name is not None:
|
||||
if not args.full_name:
|
||||
print("Full name cannot be empty.")
|
||||
sys.exit(1)
|
||||
user["full_name"] = args.full_name
|
||||
changed.append("full_name")
|
||||
|
||||
if args.permissions is not None:
|
||||
err = validate_permissions(args.permissions)
|
||||
if err:
|
||||
print(f"Invalid permissions: {err}")
|
||||
sys.exit(1)
|
||||
user["permissions"] = args.permissions
|
||||
changed.append("permissions")
|
||||
|
||||
if not changed:
|
||||
print("Nothing to change. Use --full-name and/or --permissions.")
|
||||
sys.exit(0)
|
||||
|
||||
write_users(users)
|
||||
log_action(f"EDIT login={args.login} changed={','.join(changed)}")
|
||||
print(f"User '{args.login}' updated: {', '.join(changed)}.")
|
||||
|
||||
|
||||
def cmd_passwd(args: argparse.Namespace) -> None:
|
||||
users = read_users()
|
||||
user = find_user(users, args.login)
|
||||
if not user:
|
||||
print(f"User '{args.login}' not found.")
|
||||
sys.exit(1)
|
||||
|
||||
password = prompt_password()
|
||||
user["password_hash"] = hash_password(password)
|
||||
write_users(users)
|
||||
log_action(f"PASSWD login={args.login}")
|
||||
print(f"Password for '{args.login}' updated.")
|
||||
|
||||
|
||||
def cmd_delete(args: argparse.Namespace) -> None:
|
||||
users = read_users()
|
||||
if not find_user(users, args.login):
|
||||
print(f"User '{args.login}' not found.")
|
||||
sys.exit(1)
|
||||
|
||||
users = [u for u in users if u["login"] != args.login]
|
||||
write_users(users)
|
||||
log_action(f"DELETE login={args.login}")
|
||||
print(f"User '{args.login}' deleted.")
|
||||
|
||||
|
||||
def cmd_list(_args: argparse.Namespace) -> None:
|
||||
users = read_users()
|
||||
if not users:
|
||||
print("No users.")
|
||||
return
|
||||
print(f"{'Login':<16} {'ID':<4} {'Perms':<6} Full Name")
|
||||
print("-" * 52)
|
||||
for u in users:
|
||||
print(f"{u['login']:<16} {u['id']:<4} {u['permissions']:<6} {u['full_name']}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="User management utility for practice2")
|
||||
sub = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
p_add = sub.add_parser("add", help="Add a new user")
|
||||
p_add.add_argument("login", help="Username (login)")
|
||||
p_add.set_defaults(func=cmd_add)
|
||||
|
||||
p_edit = sub.add_parser("edit", help="Edit an existing user")
|
||||
p_edit.add_argument("login", help="Username to edit")
|
||||
p_edit.add_argument("--full-name", dest="full_name", help="New full name")
|
||||
p_edit.add_argument("--permissions", help="New permissions (e.g. rwd)")
|
||||
p_edit.set_defaults(func=cmd_edit)
|
||||
|
||||
p_passwd = sub.add_parser("passwd", help="Change user password")
|
||||
p_passwd.add_argument("login", help="Username")
|
||||
p_passwd.set_defaults(func=cmd_passwd)
|
||||
|
||||
p_del = sub.add_parser("delete", help="Delete a user")
|
||||
p_del.add_argument("login", help="Username to delete")
|
||||
p_del.set_defaults(func=cmd_delete)
|
||||
|
||||
p_list = sub.add_parser("list", help="List all users")
|
||||
p_list.set_defaults(func=cmd_list)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
8
lab2/uv.lock
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.14"
|
||||
|
||||
[[package]]
|
||||
name = "lab2"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
0
lab1/.gitignore → report/.gitignore
vendored
BIN
report/img/lab2-access-log.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
report/img/lab2-bruteforce.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
report/img/lab2-confaccess-auth.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
report/img/lab2-confaccess-commands.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
report/img/lab2-passwd.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
report/img/lab2-setup.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
report/img/lab2-usermgr-add.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
report/img/lab2-usermgr-list.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@@ -119,14 +119,6 @@
|
||||
% убрать отступ у subsection
|
||||
\setlength{\cftsubsecindent}{0pt}
|
||||
|
||||
% subsubsection курсивом
|
||||
\usepackage{titlesec}
|
||||
|
||||
\titleformat{\subsubsection}
|
||||
{\normalfont\large\itshape} % стиль: обычный + курсив
|
||||
{\thesubsubsection} % номер (убери если не нужен)
|
||||
{1em}
|
||||
{}
|
||||
|
||||
|
||||
\begin{document}
|
||||
@@ -135,7 +127,8 @@
|
||||
\hfill \break
|
||||
\hfill \break
|
||||
\normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
|
||||
федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
|
||||
федеральное государственное автономное образовательное учреждение высшего образования \\
|
||||
САНКТ-ПЕТЕРБУРГСКИЙ ПОЛИТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ ПЕТРА ВЕЛИКОГО\\[10pt]}
|
||||
\normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt]
|
||||
\normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt]
|
||||
\normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
|
||||
@@ -182,10 +175,29 @@
|
||||
В отчёте представлены результаты следующих практических работ:
|
||||
\begin{enumerate}
|
||||
\item Практическая работа №1. Анализ уязвимостей программного обеспечения. Данная практическая работа посвящена анализу уязвимостей программного обеспечения с использованием Банка данных угроз безопасности информации ФСТЭК России~\cite{fstec-bdu}. В ходе выполнения работы предусмотрено изучение структуры разделов «Угрозы» и «Уязвимости», а также поиск уязвимостей по заданным критериям. Особое внимание уделяется выявлению уязвимостей, соответствующих используемым версиям операционных систем личных устройств, и рассмотрению возможных мер по их устранению.
|
||||
\item Практическая работа №2. Разработка и исследование системы аутентификации и авторизации. Данная практическая работа посвящена разработке системы доступа пользователей к конфиденциальным данным и исследованию стойкости паролей к атаке методом грубой силы. В ходе выполнения работы реализованы утилита управления пользователями, утилита доступа к конфиденциальным данным и программа перебора паролей, а также проведено экспериментальное исследование зависимости времени взлома от длины пароля.
|
||||
\end{enumerate}
|
||||
|
||||
\newpage
|
||||
\section{Практическая работа №1. Анализ уязвимостей программного обеспечения}
|
||||
\section{Анализ уязвимостей программного обеспечения}
|
||||
\subsection{Цели и задачи работы}
|
||||
|
||||
Практическая работа №1 по дисциплине «Защита информации» посвящена анализу уязвимостей программного обеспечения с использованием Банка данных угроз безопасности информации ФСТЭК Российской Федерации.
|
||||
|
||||
Цель работы: получение практических навыков работы с Банком данных угроз ФСТЭК России~\cite{fstec-bdu}, содержащим сведения об угрозах безопасности информации и уязвимостях программного обеспечения.
|
||||
|
||||
Для достижения поставленной цели в ходе выполнения работы были определены следующие задачи:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Ознакомление со структурой раздела <<Угрозы>> Банка данных угроз безопасности информации ФСТЭК России.
|
||||
\item Ознакомление со структурой раздела <<Уязвимости>> Банка данных угроз.
|
||||
\item Поиск информации в разделе <<Уязвимости>> по заданным критериям.
|
||||
\item Определение версий операционных систем, используемых на личном смартфоне и персональном компьютере.
|
||||
\item Поиск уязвимостей с уровнем опасности <<Критический>> и <<Высокий>>, соответствующих используемым версиям операционных систем, а также анализ и применение рекомендованных мер по их устранению.
|
||||
\end{enumerate}
|
||||
|
||||
Выполнение данной работы позволяет сформировать практические навыки анализа актуальных угроз и уязвимостей, а также повысить уровень понимания механизмов обеспечения информационной безопасности современных информационных систем.
|
||||
|
||||
\subsection{Знакомство с разделом <<Угрозы>> Банка угроз}
|
||||
Раздел <<Угрозы>> Банка данных угроз безопасности информации ФСТЭК России предназначен для систематизированного представления сведений об актуальных угрозах безопасности информации. Данный раздел содержит структурированную информацию, позволяющую оценить характер угроз, возможные последствия их реализации и способы противодействия.
|
||||
|
||||
@@ -209,7 +221,7 @@
|
||||
|
||||
Список уязвимостей представляет собой структурированный перечень выявленных уязвимостей в программном обеспечении. Для каждой уязвимости указывается идентификатор, наименование, описание, затронутое программное обеспечение и его версии, уровень опасности, а также оценка по шкале CVSS. Уязвимости классифицируются по уровню критичности (критический, высокий, средний и др.), что позволяет определить приоритетность их устранения. Также приводятся рекомендации по устранению или минимизации последствий эксплуатации уязвимости.
|
||||
|
||||
Подраздел <<Наиболее опасные уязвимости>> содержит перечень уязвимостей с наивысшим уровнем опасности. Как правило, к ним относятся уязвимости, позволяющие выполнить удалённое выполнение кода, повысить привилегии, обойти механизмы аутентификации или получить несанкционированный доступ к информации. Данный раздел позволяет оперативно определить наиболее критичные риски для информационных систем.
|
||||
Подраздел <<Наиболее опасные уязвимости>> содержит перечень уязвимостей с наивысшим уровнем опасности. Как правило, к ним относятся уязвимости, позволяющие выполнить удалённое выполнение кода, повысить привилегии, обойти механизмы аутентификации или получить несанкционированный доступ к информации. Данный раздел позволяет оперативно определить наиболее критичные риски для информационных систем (см. рис.~\ref{fig:top_vuln}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -218,7 +230,7 @@
|
||||
\label{fig:top_vuln}
|
||||
\end{figure}
|
||||
|
||||
Подраздел <<Инфографика>> предназначен для наглядного представления статистических данных по уязвимостям. В нём отображается распределение уязвимостей по уровням опасности, типам ошибок, видам программного обеспечения и производителям. Инфографика позволяет визуально оценить текущее состояние защищённости программных продуктов и выявить наиболее проблемные направления.
|
||||
Подраздел <<Инфографика>> предназначен для наглядного представления статистических данных по уязвимостям. В нём отображается распределение уязвимостей по уровням опасности, типам ошибок, видам программного обеспечения и производителям. Инфографика позволяет визуально оценить текущее состояние защищённости программных продуктов и выявить наиболее проблемные направления (см. рис.~\ref{fig:stats_vuln}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -230,7 +242,7 @@
|
||||
\newpage
|
||||
\subsection{Версии ОС, используемые в личных устройствах}
|
||||
|
||||
На личном компьютере используется операционная система Ubuntu 25.10 (см. Рис.~\ref{fig:laptop-os-version}).
|
||||
На личном компьютере используется операционная система Ubuntu 25.10 (см. рис.~\ref{fig:laptop-os-version}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -239,7 +251,7 @@
|
||||
\label{fig:laptop-os-version}
|
||||
\end{figure}
|
||||
|
||||
На личном мобильном устройстве используется операционная система Android 11 с графической оболочкой MIUI Global 12.5.14, сборка 12.5.14.0 RKURUXM (см. Рис.~\ref{fig:mobile-os-version}).
|
||||
На личном мобильном устройстве используется операционная система Android 11 с графической оболочкой MIUI Global 12.5.14, сборка 12.5.14.0 RKURUXM (см. рис.~\ref{fig:mobile-os-version}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -257,7 +269,7 @@
|
||||
\end{enumerate}
|
||||
Использовался уровень опасности <<высокий>>, так как критических уязвимостей для данной ОС не найдено.
|
||||
|
||||
По заданным критериям было найдено 10 уязвимостей (см. Рис.~\ref{fig:laptop-vulnerabilities}).
|
||||
По заданным критериям было найдено 10 уязвимостей (см. рис.~\ref{fig:laptop-vulnerabilities}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -300,7 +312,7 @@ A: C (Availability Impact: Complete) — полное нарушение дос
|
||||
\item Уровень опасности: критический.
|
||||
\end{enumerate}
|
||||
|
||||
По заданным критериям была найдена 1 уязвимость (см. Рис.~\ref{fig:mobile-vulnerabilities}).
|
||||
По заданным критериям была найдена 1 уязвимость (см. рис.~\ref{fig:mobile-vulnerabilities}).
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
@@ -332,12 +344,218 @@ A: C (Availability Impact: Complete) — полное нарушение дос
|
||||
\label{fig:mobile-vulnerabilities-details}
|
||||
\end{figure}
|
||||
|
||||
\newpage
|
||||
\phantom{}
|
||||
\newpage
|
||||
\section{Разработка и исследование системы аутентификации и авторизации}
|
||||
|
||||
\subsection{Цели и задачи работы}
|
||||
|
||||
Практическая работа №2 по дисциплине <<Защита информации>> посвящена разработке системы доступа пользователей к конфиденциальным данным и исследованию стойкости паролей к атаке методом грубой силы.
|
||||
|
||||
Цель работы: разработать систему аутентификации и авторизации пользователей и исследовать стойкость паролей к атаке методом грубой силы.
|
||||
|
||||
Для достижения поставленной цели в ходе выполнения работы были определены следующие задачи:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Разработать систему доступа пользователей к конфиденциальным данным, включающую утилиту управления пользователями и утилиту доступа к конфиденциальным данным.
|
||||
\item Разработать программу взлома паролей методом грубой силы.
|
||||
\item Исследовать стойкость паролей в зависимости от их длины при использовании алгоритма хэширования SHA-256.
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Требования к системе}
|
||||
|
||||
Для работы необходимо создать следующие каталоги:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{/usr/local/practice2/etc} — каталог для хранения файла с данными аутентификации и авторизации;
|
||||
\item \texttt{/usr/local/practice2/confdata} — каталог для хранения файлов с конфиденциальной информацией;
|
||||
\item \texttt{/usr/local/practice2/bin} — каталог для хранения разработанных утилит;
|
||||
\item \texttt{/usr/local/practice2/log} — каталог для хранения файлов регистрации.
|
||||
\end{itemize}
|
||||
|
||||
Права доступа на чтение, запись и выполнение всех каталогов и файлов системы предоставляются только пользователю root.
|
||||
|
||||
Данные аутентификации хранятся в файле \texttt{/usr/local/practice2/etc/passwd}. Каждая строка соответствует одному пользователю и имеет структуру:
|
||||
|
||||
\begin{center}
|
||||
\texttt{<логин>:<хэш\_пароля>:<идентификатор>:<права>:<ФИО>}
|
||||
\end{center}
|
||||
|
||||
Поддерживаются следующие права доступа: \texttt{r} — чтение, \texttt{w} — запись, \texttt{d} — удаление. Права на файл паролей предоставляются только пользователю root.
|
||||
|
||||
Пароль должен содержать только символы в кодировке ASCII: буквы A–Z и a–z, цифры 0–9, специальные символы \texttt{!@\#\$\%\^{}\&*()}. Первый символ пароля должен быть буквой.
|
||||
|
||||
\subsection{Характеристики ПК и среда разработки}
|
||||
|
||||
Работа выполнялась на персональном компьютере со следующими характеристиками:
|
||||
|
||||
\begin{itemize}
|
||||
\item Процессор: AMD Ryzen 5 5500U
|
||||
\item Оперативная память: 16 ГБ
|
||||
\item Операционная система: Ubuntu 25.10
|
||||
\end{itemize}
|
||||
|
||||
В качестве среды разработки использовался редактор Cursor. Язык программирования — Python 3.14. Алгоритм хэширования — SHA-256, реализованный с использованием модуля \texttt{hashlib} стандартной библиотеки Python.
|
||||
|
||||
\subsection{Описание реализации}
|
||||
|
||||
Утилита управления пользователями (usermgr) предоставляет следующие подкоманды: \texttt{add} — добавление пользователя с интерактивным вводом ФИО, прав доступа, пароля и его подтверждения; \texttt{edit} — редактирование ФИО и прав доступа существующего пользователя; \texttt{passwd} — изменение пароля; \texttt{delete} — удаление пользователя; \texttt{list} — вывод списка всех пользователей. Пароль хранится в виде SHA-256-хэша. Все операции с файлом паролей регистрируются в журнале \texttt{log/usermgr.log}.
|
||||
|
||||
Утилита доступа к конфиденциальным данным (confaccess) при запуске запрашивает логин и пароль. При успешной аутентификации выводится приветствие <<Привет, <ФИО>>> и справка по доступным командам. При вводе неверных данных запрос повторяется. Завершение работы происходит по команде \texttt{exit} или сигналу SIGINT (Ctrl+C). Все попытки входа и действия с конфиденциальными данными регистрируются в журнале \texttt{log/access.log}.
|
||||
|
||||
Поддерживаемые команды приведены в таблице~\ref{tab:commands}.
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\caption{Команды утилиты confaccess}
|
||||
\label{tab:commands}
|
||||
\begin{tabularx}{\textwidth}{llX}
|
||||
\toprule
|
||||
Команда & Требуемые права & Описание \\
|
||||
\midrule
|
||||
\texttt{create <file>} & w & Создание нового пустого файла в confdata \\
|
||||
\texttt{read <file>} & r & Вывод содержимого файла \\
|
||||
\texttt{append <file> <text>} & w & Добавление строки в конец файла \\
|
||||
\texttt{copy <src> <dst>} & r, w & Копирование файла в confdata \\
|
||||
\texttt{remove <file>} & d & Удаление файла из confdata \\
|
||||
\texttt{help} & — & Вывод справки \\
|
||||
\texttt{exit} & — & Выход из программы \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
Копирование разрешено только в каталог \texttt{confdata} или внутри него. Копирование из \texttt{confdata} в другие каталоги и перезапись существующих файлов запрещены.
|
||||
|
||||
Программа взлома паролей (bruteforce) выполняет последовательный перебор паролей начиная с длины 1, проверяя все допустимые комбинации символов. Для каждой комбинации вычисляется SHA-256-хэш и сравнивается с целевым хэшем из файла паролей. При нахождении совпадения фиксируются найденный пароль, количество итераций и затраченное время. Перебор прекращается при обнаружении пароля, достижении заданной максимальной длины или истечении восьмичасового лимита.
|
||||
|
||||
\subsection{Развёртывание системы}
|
||||
|
||||
Для создания структуры каталогов и установки утилит предусмотрен скрипт \texttt{setup.sh}. При запуске с правами суперпользователя скрипт также выставляет ограничительные права доступа. Базовый каталог задаётся переменной окружения \texttt{PRACTICE2\_DIR}; при её отсутствии используется \texttt{/usr/local/practice2}.
|
||||
|
||||
Установка системы выполняется следующим образом:
|
||||
|
||||
\begin{verbatim}
|
||||
chmod +x setup.sh
|
||||
sudo ./setup.sh
|
||||
\end{verbatim}
|
||||
|
||||
Результат выполнения скрипта представлен на рисунке~\ref{fig:lab2-setup}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.6\linewidth]{img/lab2-setup.png}
|
||||
\caption{Результат выполнения скрипта setup.sh}
|
||||
\label{fig:lab2-setup}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Примеры работы утилит}
|
||||
|
||||
На рисунке~\ref{fig:lab2-usermgr-add} показан процесс добавления нового пользователя. На рисунке~\ref{fig:lab2-usermgr-list} — список пользователей после добавления нескольких учётных записей.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/lab2-usermgr-add.png}
|
||||
\caption{Добавление нового пользователя}
|
||||
\label{fig:lab2-usermgr-add}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/lab2-usermgr-list.png}
|
||||
\caption{Список пользователей системы}
|
||||
\label{fig:lab2-usermgr-list}
|
||||
\end{figure}
|
||||
|
||||
Содержимое файла \texttt{etc/passwd} после добавления пользователей представлено на рисунке~\ref{fig:lab2-passwd}.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{img/lab2-passwd.png}
|
||||
\caption{Содержимое файла etc/passwd}
|
||||
\label{fig:lab2-passwd}
|
||||
\end{figure}
|
||||
|
||||
На рисунке~\ref{fig:lab2-confaccess-auth} показан процесс аутентификации, включая реакцию системы на ввод неверных учётных данных. На рисунке~\ref{fig:lab2-confaccess-commands} — пример выполнения команд с проверкой прав доступа.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.65\linewidth]{img/lab2-confaccess-auth.png}
|
||||
\caption{Аутентификация в утилите confaccess}
|
||||
\label{fig:lab2-confaccess-auth}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.5\linewidth]{img/lab2-confaccess-commands.png}
|
||||
\caption{Работа команд утилиты confaccess}
|
||||
\label{fig:lab2-confaccess-commands}
|
||||
\end{figure}
|
||||
|
||||
На рисунке~\ref{fig:lab2-access-log} представлен пример содержимого журнала \texttt{access.log} с записями об успешных и неуспешных попытках входа и выполненных операциях.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.85\linewidth]{img/lab2-access-log.png}
|
||||
\caption{Содержимое файла журнала access.log}
|
||||
\label{fig:lab2-access-log}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Исследование стойкости паролей}
|
||||
|
||||
Алфавит допустимых символов включает 72 символа: 52 буквы (A–Z, a–z), 10 цифр (0–9) и 10 специальных символов (\texttt{!@\#\$\%\^{}\&*()}). Поскольку первый символ пароля должен быть буквой, для него допустимы только 52 варианта.
|
||||
|
||||
Максимальное количество итераций для пароля длиной $n$ вычисляется по формуле:
|
||||
|
||||
\[
|
||||
N(n) = 52 \cdot 72^{n-1}
|
||||
\]
|
||||
|
||||
Расчётное максимальное время взлома определяется как $t_{\max}(n) = N(n)\,/\,v$, где $v$ — скорость хэширования, измеренная экспериментально. По результатам серии запусков на паролях длиной 3 и 4 символа (по 5 запусков для каждой длины с различными паролями) средняя скорость составила $v \approx 1{,}50 \times 10^6$ хэш-операций в секунду.
|
||||
|
||||
На рисунке~\ref{fig:lab2-bruteforce} показан пример вывода программы взлома для пароля длиной 3 символа.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\centering
|
||||
\includegraphics[width=0.7\linewidth]{img/lab2-bruteforce.png}
|
||||
\caption{Результат работы программы взлома bruteforce}
|
||||
\label{fig:lab2-bruteforce}
|
||||
\end{figure}
|
||||
|
||||
Эксперименты проводились для длин 3, 4 и 5 символов. Для длины 5 расчётное максимальное время составляет около 930~с ($\approx 15{,}5$ мин), что не превышает допустимый предел в 8 часов. Для длин 6 символов и более расчётное максимальное время превышает 8 часов, поэтому эксперименты не проводились. Результаты приведены в таблице~\ref{tab:bruteforce}.
|
||||
|
||||
\begin{table}[h!]
|
||||
\centering
|
||||
\caption{Результаты исследования стойкости паролей (SHA-256, $v = 1{,}50 \times 10^6$ Х/с)}
|
||||
\label{tab:bruteforce}
|
||||
\begin{tabularx}{\textwidth}{crrcc}
|
||||
\toprule
|
||||
Длина & $N$ & $t_{\max}$, с & Эксп. итераций & Эксп. время, с \\
|
||||
\midrule
|
||||
3 & $269\,568$ & $0{,}18$ & $163\,000$ & $0{,}22$ \\
|
||||
4 & $19\,408\,896$ & $12{,}9$ & $14\,160\,000$ & $9{,}5$ \\
|
||||
5 & $1\,397\,440\,512$ & $930$ & $698\,000\,000$ & $464$ \\
|
||||
6 & $1{,}01 \times 10^{11}$ & $66\,954$ & \multicolumn{2}{c}{не проводился} \\
|
||||
7 & $7{,}24 \times 10^{12}$ & ${\approx}4{,}8 \times 10^{6}$ & \multicolumn{2}{c}{не проводился} \\
|
||||
8 & $5{,}22 \times 10^{14}$ & ${\approx}3{,}5 \times 10^{8}$ & \multicolumn{2}{c}{не проводился} \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
\end{table}
|
||||
|
||||
\subsection{Выводы}
|
||||
|
||||
В ходе практической работы была разработана система доступа пользователей к конфиденциальным данным, включающая утилиту управления пользователями, утилиту доступа и программу взлома паролей методом грубой силы. Реализован механизм хэширования паролей на основе алгоритма SHA-256, система разграничения прав доступа и журналирование всех операций.
|
||||
|
||||
Теоретический анализ показал, что количество итераций, необходимых для полного перебора паролей, экспоненциально возрастает с увеличением их длины. Экспериментальное исследование позволило оценить реальную скорость перебора и подтвердить теоретические оценки. Полученные результаты демонстрируют, что использование паролей длиной 6 символов и более существенно затрудняет атаку методом грубой силы, а пароли длиной 8 и более символов при применении SHA-256 практически не поддаются взлому за приемлемое время.
|
||||
|
||||
\newpage
|
||||
\section*{Заключение}
|
||||
\addcontentsline{toc}{section}{Заключение}
|
||||
|
||||
В ходе выполнения практической работы №1 был проведён анализ уязвимостей программного обеспечения с использованием Банка данных угроз ФСТЭК России. Были изучены структура разделов <<Угрозы>> и <<Уязвимости>>, определены версии операционных систем личного компьютера и смартфона, а также выполнен поиск уязвимостей с уровнем опасности <<Критический>> и <<Высокий>>. Проведён анализ наиболее актуальных уязвимостей, рассмотрены их характеристики и векторы CVSS, а также рекомендации по устранению. В процессе выполнения работы были получены практические навыки поиска, анализа и оценки уязвимостей информационных систем.
|
||||
|
||||
В ходе выполнения практической работы №2 была разработана система доступа пользователей к конфиденциальным данным. Реализованы утилита управления пользователями с хэшированием паролей по алгоритму SHA-256, утилита доступа с разграничением прав и журналированием операций, а также программа взлома паролей методом грубой силы. Проведено исследование стойкости паролей в зависимости от их длины, результаты которого подтвердили экспоненциальную зависимость числа итераций от длины пароля и продемонстрировали практическую устойчивость достаточно длинных паролей к атаке полного перебора.
|
||||
|
||||
\newpage
|
||||
\printbibliography[heading=bibintoc]
|
||||
|
||||