Хеширование паролей для OneScript: PBKDF2-HMAC-SHA256, строка хеша в формате PHC со всеми параметрами внутри, проверка пароля, контролируемый апгрейд параметров (перехеширование) и настраиваемая политика сложности.
Публичный интерфейс — один модуль-фасад ХешированиеПаролей. Примитивы
(SHA-256/HMAC, PBKDF2, генератор соли, разбор формата PHC, сравнение хешей) и
класс политики лежат в src/internal и меняются без оглядки на обратную
совместимость.
Зависимостей времени выполнения нет — только стандартная библиотека OneScript.
opm install pwhash#Использовать pwhash
// Регистрация: получить строку для хранения в БД
СтрокаХеша = ХешированиеПаролей.Захешировать(Пароль);
// -> "$pbkdf2-sha256$i=80000$3q2+7w...$k9jL2m..."
// Вход: проверить пароль
Если ХешированиеПаролей.Проверить(Пароль, СтрокаХеша) Тогда
// при устаревших параметрах — обновить хеш и сохранить новую строку
Если ХешированиеПаролей.ТребуетПерехеширования(СтрокаХеша) Тогда
НоваяСтрока = ХешированиеПаролей.Захешировать(Пароль);
КонецЕсли;
КонецЕсли;$pbkdf2-sha256$i=<итерации>$<соль-base64>$<хеш-base64>
Соглашение PHC (Password Hashing Competition string format). base64 —
стандартный алфавит без дополнения = (при разборе дополнение допускается).
Все параметры проверки лежат в самой строке, поэтому смена значений по
умолчанию не ломает ранее созданные хеши: чужие строки с другим числом
итераций, солью 1–1024 байта и ключом 1–128 байт разбираются и проверяются.
| Функция | Возвращает | Назначение |
|---|---|---|
Захешировать(Пароль, Итерации = 0, ДлинаСоли = 0) |
Строка | Хеш в формате PHC. 0 — значения по умолчанию (80000 итераций, соль 16 байт). Итерации — от 1000, соль — 8–64 байта. |
Проверить(Пароль, СтрокаХеша) |
Булево | Проверка пароля. Fail-closed: битая или чужая строка, неверный тип аргумента, ошибка разбора — Ложь, исключения наружу не выходят. |
ТребуетПерехеширования(СтрокаХеша) |
Булево | Истина, если итераций меньше текущего значения по умолчанию (или строка нечитаема). Вызывать после успешной проверки. |
ПолитикаСложности(МинДлина, МаксДлина, ТребоватьСтрочную, ТребоватьПрописную, ТребоватьЦифру, ТребоватьСпецсимвол) |
ПолитикаСложностиПаролей | Требования к паролю; все параметры необязательны (см. ниже). |
ИтерацииПоУмолчанию() / УстановитьИтерацииПоУмолчанию(Итерации) |
Число / — | Политика итераций процесса. Старые хеши продолжают проверяться. |
ДлинаСолиПоУмолчанию() |
Число | 16. |
МинимальныеИтерации() |
Число | 1000 — нижняя граница для новых хешей. |
ЛимитИтерацийПроверки() / УстановитьЛимитИтерацийПроверки(Лимит) |
Число / — | Анти-DoS-граница итераций при разборе чужих строк, по умолчанию 2 000 000. |
СгенерироватьСоль(ДлинаБайт = 0) |
ДвоичныеДанные | Криптостойкая соль 8–64 байта. |
PBKDF2SHA256(Пароль, Соль, Итерации, ДлинаКлюча = 32) |
ДвоичныеДанные | «Сырой» PBKDF2-HMAC-SHA256 (RFC 8018). |
HMACSHA256(Ключ, Данные) |
ДвоичныеДанные | HMAC-SHA256 (RFC 2104), 32 байта. |
РазобратьСтрокуХеша(СтрокаХеша) |
Структура/Неопределено | Поля Алгоритм, Итерации, Соль, Хеш; Неопределено при любом отклонении от формата. |
Снимок требований приложения: значения задаются при создании и не меняются.
Политика = ХешированиеПаролей.ПолитикаСложности(); // 8..256, классы не требуются
Строгая = ХешированиеПаролей.ПолитикаСложности(12, 128, // мин, макс
Истина, Истина, Истина, Истина); // строчная, прописная, цифра, спецсимвол
Если Политика.ПревышаетМаксимум(Пароль) Тогда ... // дешёвый отсев ДО передачи в KDF
Строгая.Проверить(Пароль); // исключение с текстом для человека
Если Политика.Допустим(Пароль) Тогда ... КонецЕсли; // то же без исключения (UI)
Политика.Требования(); // массив строк для подсказок на формах
Политика.ОписаниеОднойСтрокой(); // «длина от 8 до 256 символов; цифра»
Политика.МинДлина(); Политика.МаксДлина();Буквы распознаются юникод-устойчиво (через сравнение регистров), цифры — ASCII 0–9, спецсимвол — всё, что не буква и не цифра. Кривая конфигурация деградирует к дефолтам; максимум ниже минимума поднимается до минимума.
ПревышаетМаксимум держите на пути входа: гигантский пароль не должен доходить
до Захешировать — он занял бы PBKDF2 на секунды.
80000 итераций ≈ 330 мс на эталонной машине проекта (Apple Silicon, oscript
2.0.2) — середина целевого коридора 200–500 мс. Рекомендованные OWASP 600000 на
чистом OneScript означали бы ~2,5 с на вход. Компромисс сознательный: подберите
значение под свою машину, повышайте при апгрейде железа —
ТребуетПерехеширования обеспечит плавную миграцию при входе пользователей, а
разницу с нативным PBKDF2 компенсируйте требованиями к паролям и ограничением
частоты попыток входа.
Соль 16 байт, уникальная для каждого хеша. Источник энтропии —
УникальныйИдентификатор (системный CSPRNG через Guid.NewGuid в .NET) с
отбеливанием SHA-256. Встроенный ГенераторСлучайныхЧисел не используется:
это обёртка над System.Random, криптостойкой она не является.
- Юникод-нормализация не выполняется. «é» одним символом и «e» + акут дают разные байты UTF-8 и разные хеши. Приводите пароль к единой форме (обычно NFC) и при хешировании, и при проверке — это ответственность auth-слоя.
- Сравнение хешей — без досрочного выхода, но строгих гарантий константного времени интерпретатор дать не может (JIT, кеши, GC). На фоне стоимости самого PBKDF2 остаточная утечка пренебрежима.
- Анти-DoS по итерациям: строка со злонамеренно большим числом итераций
отвергается, а не считается (
ЛимитИтерацийПроверки). - Частоту попыток входа библиотека не ограничивает — rate limiting и блокировки на стороне приложения.
- Память под пароль не затирается: очистка строк средствами OneScript недоступна.
src/Модули/ХешированиеПаролей.os публичный фасад (#Использовать "../internal")
src/internal/Модули/ ПримитивыSHA256, ВыводКлючаPBKDF2,
ГенераторСоли, ФорматPHC, СравнениеХешей
src/internal/Классы/ ПолитикаСложностиПаролей
ПримитивыSHA256— приведение к буферам, SHA-256, HMAC-SHA256 по RFC 2104 (встроенного HMAC в OneScript 2.0 нет); ключевые блоки ipad/opad вынесены наружу, чтобы PBKDF2 собирал их один раз на весь вывод ключа.ВыводКлючаPBKDF2— вывод ключа по RFC 8018, многоблочный.ГенераторСоли— соль из системного CSPRNG.ФорматPHC— сборка строки и строгий fail-closed разбор (свой цикл по цифрам вместоЧисло(), строгий base64, границы длин и итераций).СравнениеХешей— сравнение буферов без досрочного выхода.
oneunit execute53 теста: официальные векторы HMAC (RFC 4231) и PBKDF2 (RFC 7914 §11 + канонический набор), круговые сценарии, битые и чужие строки (fail-closed), защита от DoS по итерациям, уникальность солей, перехеширование при смене политики итераций, политика сложности.
MIT