64 lines
2.0 KiB
PHP
64 lines
2.0 KiB
PHP
<?php
|
||
|
||
namespace App\Services\Pdn;
|
||
|
||
use RuntimeException;
|
||
|
||
/**
|
||
* Псевдонимизация и маскирование персональных данных.
|
||
*
|
||
* Мера ФСТЭК / 152-ФЗ ст. 3 п.9: псевдонимизация для аналитики и логов,
|
||
* чтобы исключить прямые идентификаторы (раздел 5.3 гайда).
|
||
*/
|
||
class PdnAnonymizer
|
||
{
|
||
/**
|
||
* Детерминированный псевдоним субъекта (HMAC-SHA256).
|
||
* Один и тот же вход даёт один и тот же псевдоним — пригодно для join'ов
|
||
* в аналитике без раскрытия исходного идентификатора.
|
||
*/
|
||
public function pseudonym(string $value): string
|
||
{
|
||
$key = (string) config('security.encryption.pseudonym_key');
|
||
|
||
if ($key === '') {
|
||
throw new RuntimeException(
|
||
'Не задан SECURITY_PSEUDONYM_KEY для псевдонимизации ПДн.'
|
||
);
|
||
}
|
||
|
||
return hash_hmac('sha256', $value, $this->normalizeKey($key));
|
||
}
|
||
|
||
/**
|
||
* Маскирование значения для отображения (например в журналах/UI оператора).
|
||
*
|
||
* Пример: "Иванов" -> "И****", "ivan@mail.ru" -> "iv***@mail.ru".
|
||
*/
|
||
public function mask(string $value, int $visible = 2): string
|
||
{
|
||
if (str_contains($value, '@')) {
|
||
[$local, $domain] = explode('@', $value, 2);
|
||
|
||
return $this->mask($local, $visible).'@'.$domain;
|
||
}
|
||
|
||
$length = mb_strlen($value);
|
||
|
||
if ($length <= $visible) {
|
||
return str_repeat('*', max($length, 1));
|
||
}
|
||
|
||
return mb_substr($value, 0, $visible).str_repeat('*', $length - $visible);
|
||
}
|
||
|
||
private function normalizeKey(string $key): string
|
||
{
|
||
if (str_starts_with($key, 'base64:')) {
|
||
return (string) base64_decode(substr($key, 7), true);
|
||
}
|
||
|
||
return $key;
|
||
}
|
||
}
|