PHP项目数据脱敏与掩码完整方案
数据脱敏核心类
<?php
namespace App\Utils;
class DataMasker
{
// 脱敏规则配置
private static $rules = [
'phone' => [
'pattern' => '/^(\d{3})\d{4}(\d{4})$/',
'replacement' => '$1****$2'
],
'email' => [
'pattern' => '/^(\w{3})\w*@(\w+\.\w+)$/',
'replacement' => '$1***@$2'
],
'id_card' => [
'pattern' => '/^(\d{6})\d{8}(\d{4})$/',
'replacement' => '$1********$2'
],
'bank_card' => [
'pattern' => '/^(\d{4})\d{8,12}(\d{4})$/',
'replacement' => '$1**** ****$2'
],
'name' => [
'pattern' => '/^(.)(.*)$/u',
'replacement' => '$1**'
],
'address' => [
'pattern' => '/^(.{6})(.*)$/u',
'replacement' => '$1****'
],
'password' => [
'pattern' => '/^.*$/',
'replacement' => '******'
]
];
/**
* 通用脱敏方法
* @param string $value 原始值
* @param string $type 数据类型
* @param array $customRule 自定义规则
* @return string
*/
public static function mask($value, $type = 'phone', $customRule = [])
{
if (empty($value)) {
return $value;
}
$rule = !empty($customRule) ? $customRule : (self::$rules[$type] ?? null);
if (!$rule) {
return $value;
}
return preg_replace($rule['pattern'], $rule['replacement'], $value);
}
/**
* 批量脱敏
* @param array $data 数据数组
* @param array $fieldRules 字段规则 ['field_name' => 'type']
* @return array
*/
public static function batchMask(array $data, array $fieldRules)
{
foreach ($data as $key => &$value) {
if (is_array($value)) {
$value = self::batchMask($value, $fieldRules);
} elseif (isset($fieldRules[$key])) {
$value = self::mask($value, $fieldRules[$key]);
}
}
return $data;
}
/**
* 自定义脱敏规则
* @param string $name 规则名称
* @param string $pattern 正则模式
* @param string $replacement 替换格式
*/
public static function addRule($name, $pattern, $replacement)
{
self::$rules[$name] = [
'pattern' => $pattern,
'replacement' => $replacement
];
}
}
数据脱敏注解实现(高级用法)
<?php
namespace App\Annotations;
/**
* @Annotation
* @Target({"PROPERTY"})
*/
class DataMask
{
public $type = 'phone'; // 脱敏类型
public $condition = null; // 脱敏条件
}
// 使用示例
class User
{
/** @DataMask(type="phone") */
public $phone;
/** @DataMask(type="email") */
public $email;
/** @DataMask(type="name") */
public $name;
}
// 注解处理器
class DataMaskProcessor
{
public static function process($object)
{
$reflection = new \ReflectionClass($object);
$properties = $reflection->getProperties();
foreach ($properties as $property) {
$annotations = self::getAnnotations($property);
if (isset($annotations['DataMask'])) {
$type = $annotations['DataMask']['type'];
$property->setAccessible(true);
$value = $property->getValue($object);
$maskedValue = DataMasker::mask($value, $type);
$property->setValue($object, $maskedValue);
}
}
return $object;
}
private static function getAnnotations(\ReflectionProperty $property)
{
$docBlock = $property->getDocComment();
preg_match_all('/@(\w+)\(([^)]*)\)/', $docBlock, $matches);
$annotations = [];
foreach ($matches[1] as $index => $name) {
$params = $matches[2][$index];
parse_str(str_replace(['"', "'", '='], ['', '', ':'], $params), $parsed);
$annotations[$name] = $parsed;
}
return $annotations;
}
}
中间件实现(请求/响应脱敏)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class DataMaskingMiddleware
{
private $maskRules = [
'phone' => ['pattern' => '/1[3-9]\d{9}/', 'type' => 'phone'],
'email' => ['pattern' => '/\w+@\w+\.\w+/', 'type' => 'email'],
'id_card' => ['pattern' => '/\d{17}[\dXx]/', 'type' => 'id_card']
];
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 响应脱敏
if ($response->headers->get('content-type') === 'application/json') {
$content = $response->getContent();
$data = json_decode($content, true);
if (is_array($data)) {
$maskedData = $this->maskResponseData($data);
$response->setContent(json_encode($maskedData));
}
}
return $response;
}
private function maskResponseData($data)
{
if (is_array($data)) {
foreach ($data as $key => $value) {
if (is_array($value)) {
$data[$key] = $this->maskResponseData($value);
} elseif (is_string($value)) {
$data[$key] = $this->maskSensitiveData($value);
}
}
}
return $data;
}
private function maskSensitiveData($value)
{
foreach ($this->maskRules as $rule) {
if (preg_match($rule['pattern'], $value)) {
return DataMasker::mask($value, $rule['type']);
}
}
return $value;
}
}
数据库查询脱敏
<?php
namespace App\Models\Traits;
trait DataMaskingTrait
{
protected $maskable = [];
public function toArray()
{
$data = parent::toArray();
foreach ($this->maskable as $field => $type) {
if (isset($data[$field])) {
$data[$field] = DataMasker::mask($data[$field], $type);
}
}
return $data;
}
}
// 使用示例
class User extends Model
{
use DataMaskingTrait;
protected $maskable = [
'phone' => 'phone',
'email' => 'email',
'name' => 'name',
'id_card' => 'id_card'
];
}
日志脱敏处理器
<?php
namespace App\Logging;
use Monolog\Handler\AbstractProcessingHandler;
class MaskedLogHandler extends AbstractProcessingHandler
{
private $sensitiveKeys = [
'password', 'token', 'secret', 'credit_card',
'phone', 'email', 'id_card'
];
protected function write(array $record): void
{
$record['formatted'] = $this->maskSensitiveData($record['formatted']);
// 写入日志
}
private function maskSensitiveData($data)
{
if (is_array($data)) {
foreach ($data as $key => $value) {
if (in_array($key, $this->sensitiveKeys)) {
$data[$key] = '***MASKED***';
} elseif (is_array($value)) {
$data[$key] = $this->maskSensitiveData($value);
}
}
}
return $data;
}
}
使用示例
// 基本使用
$phone = DataMasker::mask('13812345678', 'phone'); // 138****5678
$email = DataMasker::mask('username@example.com', 'email'); // use***@example.com
// 批量脱敏
$userData = [
'name' => '张三',
'phone' => '13812345678',
'email' => 'user@example.com'
];
$masked = DataMasker::batchMask($userData, [
'name' => 'name',
'phone' => 'phone',
'email' => 'email'
]);
// 自定义规则
DataMasker::addRule(
'custom_phone',
'/^(\d{2})\d{7}(\d{2})$/',
'$1*****$2'
);
echo DataMasker::mask('02112345678', 'custom_phone');
最佳实践建议
- 分级脱敏:根据不同权限级别展示不同脱敏程度
- 动态配置:将脱敏规则存储在数据库或配置文件中
- 性能优化:对频繁查询的字段建立缓存
- 安全考虑:在ORM层面实现脱敏,避免遗漏
- 测试覆盖:为每种脱敏类型编写单元测试
这个方案提供了完整的PHP数据脱敏实现,涵盖了常见的数据类型和多种使用场景。
