PD Guard: модуль безопасности персональных данных
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package ru.pdguard.config;
|
||||
|
||||
import ru.pdguard.mask.MaskMode;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Правила обработки для одной системы-потребителя.
|
||||
*
|
||||
* @param name имя системы; им же разделяется хранилище соответствий,
|
||||
* чтобы одна система не могла достать данные другой
|
||||
* @param enabled разрешено ли системе обращаться в модуль
|
||||
* @param demask выполняется ли для системы обратное преобразование
|
||||
* @param maskMode вид замены: звёздочки, токен или синтетическое значение
|
||||
* @param types типы ПД к маскированию; {@code "*"} — все известные
|
||||
* @param key общий секрет системы; задан — заголовок {@code X-System-Key} обязан
|
||||
* совпасть, иначе имя системы можно было бы просто назвать.
|
||||
* Только знаки ASCII: заголовки HTTP передаются в Latin-1,
|
||||
* и кириллица в ключе до сервиса доедет искажённой
|
||||
* @param requireCompanion типы, которые маскируются только вместе с ПД другого типа:
|
||||
* пин-код сам по себе безвреден, пин-код рядом с номером
|
||||
* карты — уже нет; то же для даты без якорного слова, места
|
||||
* рождения («Нижний Новгород» в рассказе о городе — не адрес
|
||||
* клиента) и страны («цены выросли в Казахстане» — не гражданство).
|
||||
* Сюда же банковские реквизиты — счёт, БИК, ОГРН, ОГРНИП, КПП:
|
||||
* сами по себе они опознают организацию или счёт, а не человека,
|
||||
* и в перечне типов из задания их нет. Рядом с именем клиента
|
||||
* они становятся его данными и маскируются.
|
||||
* Сюда же доход и биометрия. Сумма заработка без человека —
|
||||
* статистика («доход домохозяйств вырос до 74 500 руб»), а не
|
||||
* персональные данные. Биометрия же в тексте не встречается
|
||||
* вовсе: это шаблон в базе, и правило маскирует лишь само
|
||||
* упоминание, то есть слово, а не данные. Чувствителен здесь
|
||||
* факт, что биометрию сдал названный человек, — а он и
|
||||
* существует только при имени рядом
|
||||
*/
|
||||
public record SystemPolicy(String name, boolean enabled, boolean demask, MaskMode maskMode,
|
||||
Set<String> types, Set<String> requireCompanion, String key) {
|
||||
|
||||
public static final String ALL = "*";
|
||||
|
||||
/** Имя политики по умолчанию; оно же разделяет хранилище для запросов без заголовка. */
|
||||
public static final String DEFAULT_NAME = "default";
|
||||
|
||||
/** Политика по умолчанию: маскируем всё, что умеем, обратное преобразование включено. */
|
||||
public static final SystemPolicy DEFAULT = new SystemPolicy(
|
||||
DEFAULT_NAME, true, true, MaskMode.MASK, Set.of(ALL),
|
||||
Set.of("CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY",
|
||||
"ACCOUNT_NUMBER", "BIK", "OGRN", "OGRNIP", "KPP",
|
||||
"INCOME", "BIOMETRIC"), null);
|
||||
|
||||
public SystemPolicy {
|
||||
types = Set.copyOf(types);
|
||||
requireCompanion = Set.copyOf(requireCompanion);
|
||||
}
|
||||
|
||||
/** Политика только для перечисленных типов, с остальными настройками по умолчанию. */
|
||||
public static SystemPolicy forTypes(String... types) {
|
||||
return new SystemPolicy(DEFAULT_NAME, true, true, MaskMode.MASK,
|
||||
Set.of(types), DEFAULT.requireCompanion(), null);
|
||||
}
|
||||
|
||||
/** Совпадает ли предъявленный ключ. Ключ не задан — проверка не применяется. */
|
||||
public boolean accepts(String presentedKey) {
|
||||
if (key == null || key.isBlank()) {
|
||||
return true;
|
||||
}
|
||||
return java.security.MessageDigest.isEqual(
|
||||
key.getBytes(java.nio.charset.StandardCharsets.UTF_8),
|
||||
(presentedKey == null ? "" : presentedKey).getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public boolean allows(String type) {
|
||||
return types.contains(ALL) || types.contains(type);
|
||||
}
|
||||
|
||||
public boolean needsCompanion(String type) {
|
||||
return requireCompanion.contains(type);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user