rubric/modules/infosecmodules/infosec_authn.go
2026-07-21 14:52:55 +03:00

325 lines
14 KiB
Go
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package infosecmodules
import (
"rubric/utils"
)
var AuthnModule = Module{
Key: "authn",
Aliases: []string{"auth", "mfa", "jwt", "oauth"},
Title: "Аутентификация и авторизация",
Description: "MFA, JWT, OAuth2, OIDC, RBAC, LDAP/AD, управление доступом",
Run: showAuthn,
}
func showAuthn() {
utils.Header("InfoSec — Аутентификация и авторизация")
utils.Section("Основные понятия")
utils.Code(`Authentication (AuthN) — кто ты? Проверка идентичности.
Authorization (AuthZ) — что тебе можно? Проверка прав.
Факторы аутентификации:
Something you know — пароль, PIN, секретный вопрос
Something you have — токен, телефон, смарт-карта
Something you are — биометрика (отпечаток, лицо)
Somewhere you are — геолокация, IP (дополнительный фактор)
MFA (Multi-Factor Authentication) — два и более фактора.
2FA — два фактора (подмножество MFA).
Типы MFA (от слабого к сильному):
SMS OTP — уязвим к SIM-swap, SS7 атакам
TOTP — RFC 6238, Google Authenticator (лучше SMS)
HOTP — счётчик, менее популярен
Push Auth — Duo, Microsoft Authenticator
FIDO2/WebAuthn — аппаратные ключи, phishing-resistant (лучший вариант)`)
utils.Section("Пароли и хранение")
utils.Code(`Правила паролей (NIST SP 800-63B — актуальные рекомендации):
- Минимальная длина: 8+ символов (лучше 12-14+)
- НЕ требовать обязательную смену (только при компрометации)
- НЕ требовать сложность (но и не запрещать)
- Проверять по словарю скомпрометированных паролей
- Разрешать пробелы и Unicode
- Показывать пароль при вводе (опционально)
- Поддерживать менеджеры паролей (не ограничивать длину)
Хранение паролей:
❌ Открытый текст
❌ Обратимое шифрование
❌ MD5 / SHA-1 без соли
✓ bcrypt (cost ≥ 12), Argon2id, scrypt
Проверка скомпрометированных паролей:
Have I Been Pwned API — проверить hashed пароль
k-Anonymity: отправить первые 5 символов хэша
https://api.pwnedpasswords.com/range/{hash_prefix}
Account Lockout:
Блокировать после N неудачных попыток (3-5)
Экспоненциальная задержка между попытками
Уведомлять пользователя о неудачных попытках`)
utils.Section("TOTP — Time-based One-Time Password")
utils.Code(`RFC 6238. Основан на HMAC-SHA1 + временном счётчике.
Как работает:
1. Общий секрет (seed) обменивается один раз (QR-код)
2. Counter = floor(UnixTime / 30) # меняется каждые 30 сек
3. OTP = HOTP(secret, counter) — 6-8 цифр
4. Сервер проверяет OTP ± 1 временной шаг
Уязвимости:
- Фишинг в реальном времени (attacker-in-the-middle)
- Не защищает от SIM-swap (только SMS)
- Потеря устройства = потеря доступа
# Python реализация:
import pyotp
secret = pyotp.random_base32()
totp = pyotp.TOTP(secret)
print(totp.now()) # текущий OTP
print(totp.verify("123456")) # проверить
# Provisioning URI для QR-кода:
uri = totp.provisioning_uri("alice@example.com", issuer_name="MyApp")`)
utils.Section("FIDO2 / WebAuthn — беспарольная аутентификация")
utils.Code(`FIDO2 = WebAuthn (W3C) + CTAP (Client-to-Authenticator Protocol)
Phishing-resistant: ключ привязан к origin (домену).
Аутентификаторы:
Platform — встроенный в устройство (Touch ID, Windows Hello)
Roaming — внешний ключ (YubiKey, Titan Key)
Как работает:
Регистрация:
1. Сервер отправляет challenge
2. Аутентификатор создаёт пару ключей для этого origin
3. Публичный ключ сохраняется на сервере
4. Приватный ключ НИКОГДА не покидает устройство
Аутентификация:
1. Сервер отправляет challenge
2. Аутентификатор подписывает challenge приватным ключом
3. Сервер верифицирует подпись публичным ключом
Атака фишингом НЕВОЗМОЖНА: ключ не работает на evil.com
Паспорты (Passkeys):
= FIDO2 ключи, синхронизируемые через облако (iCloud, Google, 1Password)
Удобство ключей + синхронизация устройств`)
utils.Section("JWT — JSON Web Token")
utils.Code(`JWT — компактный токен для передачи claims между сторонами.
Используется для stateless аутентификации (API, микросервисы).
Структура: header.payload.signature
# Header (base64url):
{"alg": "HS256", "typ": "JWT"}
# Payload (base64url):
{
"sub": "1234567890", # subject (user ID)
"name": "Alice Smith",
"iat": 1516239022, # issued at
"exp": 1516242622, # expiration
"jti": "uuid", # JWT ID (для revocation)
"iss": "https://auth.example.com", # issuer
"aud": "https://api.example.com" # audience
}
# Signature:
HMAC-SHA256(base64(header) + "." + base64(payload), secret)
Типы алгоритмов:
Симметричные (один ключ):
HS256, HS384, HS512 — HMAC-SHA
Асимметричные (ключевая пара):
RS256 — RSA + SHA256 (рекомендуется для распределённых систем)
ES256 — ECDSA + SHA256 (компактнее)
EdDSA — Ed25519 (современный)
Уязвимости JWT:
alg=none — КРИТИЧНО, проверяй алгоритм!
Атака: изменить alg на "none", удалить подпись
RS256→HS256 подмена
Атака: использовать публичный RSA ключ как HMAC секрет
Слабый секрет — поддаётся брутфорсу
Отсутствие exp — токен не истекает
Хранение в localStorage — доступен XSS атаке
Лучшие практики:
- Короткий TTL (access token: 15 мин, refresh: 7 дней)
- Использовать RS256 или ES256 (не HS256 в распределённых системах)
- Хранить в HttpOnly cookie (не localStorage!)
- Проверять iss, aud, exp
- Реализовать token revocation (Redis blacklist или jti)`)
utils.Section("OAuth2 и OpenID Connect")
utils.Code(`OAuth2 — фреймворк делегированной авторизации (не аутентификации!).
OIDC (OpenID Connect) — слой аутентификации поверх OAuth2.
OAuth2 Roles:
Resource Owner — пользователь
Client — приложение (запрашивает доступ)
Resource Server — API с защищёнными данными
Authorization Server — выдаёт токены
OAuth2 Grant Types (потоки):
Authorization Code — для веб-приложений (РЕКОМЕНДУЕТСЯ)
1. Redirect → Auth Server с code_challenge (PKCE)
2. Пользователь авторизуется
3. Redirect обратно с code
4. Обмен code на tokens (server-side)
Client Credentials — сервис-к-сервису (без пользователя)
Device Authorization — умные устройства, CLI
❌ Implicit Flow — устаревший, уязвим (токен в URL)
❌ Resource Owner Password — только для доверенных приложений
PKCE (Proof Key for Code Exchange):
Защита от перехвата authorization code.
Обязателен для публичных клиентов (SPA, мобильные).
code_verifier = random()
code_challenge = base64url(sha256(code_verifier))
Токены OAuth2:
Access Token — краткоживущий, доступ к API
Refresh Token — долгоживущий, получение нового access token
ID Token — OIDC, JWT с данными пользователя
OIDC Scopes:
openid — обязательный для OIDC, возвращает ID Token
profile — имя, аватар, locale
email — email и верификация
address — адрес
phone — телефон`)
utils.Section("RBAC, ABAC и управление доступом")
utils.Code(`DAC (Discretionary Access Control):
Владелец решает кто имеет доступ (Linux chmod, ACL)
MAC (Mandatory Access Control):
Системная политика (SELinux, AppArmor)
RBAC (Role-Based Access Control):
Права назначаются ролям, роли — пользователям.
Пример: admin, editor, viewer
Kubernetes RBAC: Role → RoleBinding → ServiceAccount/User
ABAC (Attribute-Based Access Control):
Решения на основе атрибутов (пользователь, ресурс, контекст)
Пример: OPA (Open Policy Agent), AWS IAM
ReBAC (Relationship-Based Access Control):
Права через отношения между объектами
Пример: Google Zanzibar, SpiceDB, OpenFGA
Принцип наименьших привилегий:
Давать только необходимые права
Just-in-Time (JIT) доступ — временное повышение привилегий
Privileged Access Management (PAM):
CyberArk, HashiCorp Vault, AWS IAM Identity Center
# OPA — Open Policy Agent (пример Rego политики):
package authz
default allow = false
allow if {
input.method == "GET"
input.path == ["api", "public"]
}
allow if {
input.user.role == "admin"
}`)
utils.Section("LDAP и Active Directory")
utils.Code(`LDAP (Lightweight Directory Access Protocol):
Протокол доступа к каталогам (пользователи, группы, ресурсы).
Active Directory — Microsoft LDAP + Kerberos + DNS.
LDAP структура:
DC=example,DC=com — домен
OU=Users — организационная единица
CN=Alice Smith — объект (пользователь)
Полный DN: CN=Alice Smith,OU=Users,DC=example,DC=com
# ldapsearch — поиск в LDAP:
ldapsearch -H ldap://dc.example.com \
-D "CN=binduser,DC=example,DC=com" \
-w password \
-b "DC=example,DC=com" \
"(sAMAccountName=alice)"
# Атаки на LDAP/AD:
Pass-the-Hash — использование хэша NTLM без пароля
Pass-the-Ticket — использование Kerberos билета
Kerberoasting — получение хэшей service tickets
AS-REP Roasting — атака на аккаунты без Kerberos preauth
DCSync — имитация контроллера домена (миллионы хэшей)
LDAP Injection — инъекция в LDAP-запросы
# Защита:
- Kerberos AES вместо NTLM/RC4
- Protected Users группа (не кэшировать credentials)
- Отключить NTLMv1, ограничить NTLMv2
- LAPS (Local Admin Password Solution)
- Tiered Administration Model`)
utils.Section("Secrets Management")
utils.Code(`НЕ хранить секреты в:
❌ Git / исходном коде
❌ Docker image / Dockerfile
❌ Environment variables (видны в /proc)
❌ Логах
Инструменты управления секретами:
HashiCorp Vault — де-факто стандарт:
Динамические секреты (генерация по запросу)
Lease и автообновление
Аудит всех обращений
AWS Secrets Manager / Parameter Store
GCP Secret Manager
Azure Key Vault
Kubernetes Secrets (base64, не зашифровано по умолчанию!)
+ Sealed Secrets (Bitnami) — зашифрованные секреты в Git
+ External Secrets Operator — синхронизация с Vault/AWS
# HashiCorp Vault — основные операции:
vault server -config=vault.hcl
vault operator init # инициализация (ключи разуплотнения)
vault operator unseal # разуплотнение
vault login
vault secrets enable kv-v2
vault kv put secret/myapp db_password=s3cret api_key=abc123
vault kv get secret/myapp
vault kv get -field=db_password secret/myapp
# Динамические секреты PostgreSQL:
vault secrets enable database
vault write database/config/mydb \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db:5432/mydb" \
allowed_roles="my-role"
vault write database/roles/my-role \
db_name=mydb \
creation_statements="CREATE ROLE..." \
default_ttl="1h" max_ttl="24h"
# Получить временные credentials:
vault read database/creds/my-role
# → username: v-root-xyz123, password: A1B2C3..., lease_duration: 1h`)
utils.Footer()
}