first commit

This commit is contained in:
brusnitsyn
2026-06-24 17:20:43 +09:00
commit 43499acf1c
165 changed files with 25929 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
<?php
namespace App\Models\Concerns;
use App\Services\Crypto\PdnCipher;
use Illuminate\Support\Facades\Log;
use Throwable;
/**
* Прозрачное шифрование полей персональных данных на уровне приложения.
*
* Мера ФСТЭК: ЗНИ (защита носителей), ОЦЛ.2 (контроль целостности данных).
*
* Использование в модели:
*
* use HasPdnEncryption;
* protected array $encrypted = ['last_name', 'passport', 'snils'];
*
* Шифрование выполняется выбранным драйвером (config/security.php encryption),
* что позволяет заменить AES на сертифицированное СКЗИ (ГОСТ) без правки моделей.
*/
trait HasPdnEncryption
{
public function setAttribute($key, $value)
{
if ($this->isPdnEncrypted($key) && $value !== null) {
$value = $this->pdnCipher()->encrypt((string) $value);
}
return parent::setAttribute($key, $value);
}
public function getAttribute($key)
{
$value = parent::getAttribute($key);
if ($this->isPdnEncrypted($key) && $value !== null && is_string($value)) {
try {
return $this->pdnCipher()->decrypt($value);
} catch (Throwable $e) {
// Нарушение целостности/невозможность расшифровки — инцидент (ИНЦ.1).
Log::channel(config('audit.siem.channel', 'stack'))->error('pdn.decrypt_failed', [
'model' => static::class,
'attribute' => $key,
'id' => $this->getKey(),
]);
return null;
}
}
return $value;
}
/**
* Список зашифрованных атрибутов модели.
*
* @return array<int, string>
*/
public function encryptedAttributes(): array
{
return $this->encrypted;
}
protected function isPdnEncrypted(string $key): bool
{
return in_array($key, $this->encryptedAttributes(), true);
}
protected function pdnCipher(): PdnCipher
{
return app(PdnCipher::class);
}
}