PHP项目数据脱敏与掩码

wen PHP项目 3

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');

最佳实践建议

  1. 分级脱敏:根据不同权限级别展示不同脱敏程度
  2. 动态配置:将脱敏规则存储在数据库或配置文件中
  3. 性能优化:对频繁查询的字段建立缓存
  4. 安全考虑:在ORM层面实现脱敏,避免遗漏
  5. 测试覆盖:为每种脱敏类型编写单元测试

这个方案提供了完整的PHP数据脱敏实现,涵盖了常见的数据类型和多种使用场景。

PHP项目数据脱敏与掩码

抱歉,评论功能暂时关闭!