PHP 怎么数据静态脱敏

wen PHP项目 2

本文目录导读:

PHP 怎么数据静态脱敏

  1. 基础字符串函数脱敏(最简单)
  2. 使用正则表达式脱敏
  3. 数组批量脱敏
  4. 结合数据库查询的脱敏
  5. 使用第三方库(推荐)
  6. 完整的脱敏工具类
  7. 总结建议

在 PHP 中进行数据静态脱敏,主要有以下几种常用方法,我会从简单到复杂依次介绍:

基础字符串函数脱敏(最简单)

使用 substr_replacestr_repeat 等函数实现基本脱敏:

<?php
// 手机号脱敏:保留前3后4
function maskMobile($mobile) {
    return substr_replace($mobile, '****', 3, 4);
}
// 身份证号脱敏:保留前6后4
function maskIdCard($idCard) {
    return substr_replace($idCard, '********', 6, 8);
}
// 邮箱脱敏
function maskEmail($email) {
    $parts = explode('@', $email);
    $name = $parts[0];
    $maskedName = substr($name, 0, 2) . str_repeat('*', strlen($name) - 2);
    return $maskedName . '@' . $parts[1];
}
// 测试
echo maskMobile('13812345678'); // 138****5678
echo "\n";
echo maskIdCard('110101199001011234'); // 110101********1234
echo "\n";
echo maskEmail('zhangsan@example.com'); // zh*****@example.com

使用正则表达式脱敏

适合更复杂的脱敏规则:

<?php
function maskWithRegex($data, $type) {
    switch ($type) {
        case 'bank_card': // 银行卡号
            return preg_replace('/(\d{4})\d{8}(\d{4})/', '$1********$2', $data);
        case 'name': // 姓名:保留姓
            return preg_replace('/([\x{4e00}-\x{9fa5}])./u', '$1*', $data);
        case 'address': // 地址:保留前6个字符
            return mb_substr($data, 0, 6) . str_repeat('*', mb_strlen($data) - 6);
    }
    return $data;
}
echo maskWithRegex('6222020200012345678', 'bank_card'); // 6222********4567
echo "\n";
echo maskWithRegex('张三', 'name'); // 张*
echo "\n";
echo maskWithRegex('北京市朝阳区某某街道', 'address'); // 北京市朝阳*****

数组批量脱敏

当需要处理多条记录时:

<?php
class DataMasker {
    // 脱敏规则映射
    private static $rules = [
        'mobile'    => ['type' => 'fixed', 'start' => 3, 'length' => 4],
        'id_card'   => ['type' => 'fixed', 'start' => 6, 'length' => 8],
        'real_name' => ['type' => 'name'],
        'email'     => ['type' => 'email'],
        'bank_card' => ['type' => 'regex', 'pattern' => '/(\d{4})\d{8}(\d{4})/', 'replacement' => '$1********$2'],
    ];
    // 批量脱敏
    public static function maskArray(array $data, array $fields) {
        foreach ($data as &$row) {
            foreach ($fields as $field => $ruleName) {
                if (isset($row[$field])) {
                    $row[$field] = self::mask($row[$field], $ruleName);
                }
            }
        }
        return $data;
    }
    // 单个字段脱敏
    public static function mask($value, $ruleName) {
        if (empty($value)) return $value;
        $rule = self::$rules[$ruleName] ?? null;
        if (!$rule) return $value;
        switch ($ruleName) {
            case 'mobile':
            case 'id_card':
                return substr_replace(
                    $value, 
                    str_repeat('*', $rule['length']), 
                    $rule['start'], 
                    $rule['length']
                );
            case 'real_name':
                return mb_substr($value, 0, 1) . str_repeat('*', mb_strlen($value) - 1);
            case 'email':
                $parts = explode('@', $value);
                return substr($parts[0], 0, 2) . '***@' . $parts[1];
            case 'bank_card':
                return preg_replace($rule['pattern'], $rule['replacement'], $value);
        }
        return $value;
    }
}
// 使用示例
$users = [
    [
        'id' => 1,
        'name' => '张三',
        'mobile' => '13812345678',
        'email' => 'zhangsan@example.com',
        'id_card' => '110101199001011234'
    ],
    [
        'id' => 2,
        'name' => '李四',
        'mobile' => '13987654321',
        'email' => 'lisi@example.com',
        'id_card' => '110101199202022345'
    ]
];
$masked = DataMasker::maskArray($users, [
    'name' => 'real_name',
    'mobile' => 'mobile',
    'email' => 'email',
    'id_card' => 'id_card'
]);
print_r($masked);

结合数据库查询的脱敏

处理从数据库查询出来的数据:

<?php
class UserService {
    private $db;
    public function __construct(PDO $db) {
        $this->db = $db;
    }
    // 获取用户列表(已脱敏)
    public function getUsersWithMask(): array {
        $stmt = $this->db->query("SELECT id, name, mobile, email FROM users");
        $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // 应用脱敏规则
        foreach ($users as &$user) {
            $user['name'] = $this->maskName($user['name']);
            $user['mobile'] = $this->maskMobile($user['mobile']);
            $user['email'] = $this->maskEmail($user['email']);
        }
        return $users;
    }
    // 获取敏感数据(不脱敏,内部使用)
    public function getRawUser($id): ?array {
        $stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
        $stmt->execute([$id]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
}

使用第三方库(推荐)

可以使用 fakerphp/faker 或自定义的脱敏类库:

composer require fakerphp/faker
<?php
require_once 'vendor/autoload.php';
use Faker\Factory;
class DataDesensitizer {
    private $faker;
    public function __construct() {
        $this->faker = Factory::create('zh_CN');
    }
    // 生成脱敏数据
    public function generateMaskedData() {
        return [
            'name' => $this->maskName(),
            'mobile' => $this->maskMobile(),
            'id_card' => $this->maskIdCard(),
        ];
    }
    private function maskName() {
        // 生成近似人名
        $lastNames = ['张', '李', '王', '刘', '陈'];
        return $lastNames[array_rand($lastNames)] . '某';
    }
    private function maskMobile() {
        return '138****' . $this->faker->numerify('####');
    }
    private function maskIdCard() {
        return $this->faker->numerify('########') . '********' . $this->faker->numerify('####');
    }
}

完整的脱敏工具类

<?php
class SensitiveDataMasker {
    // 支持的脱敏类型
    const MASK_FIXED = 'fixed';       // 固定位置脱敏
    const MASK_REGEX = 'regex';       // 正则脱敏
    const MASK_NAME = 'name';         // 姓名脱敏
    const MASK_EMAIL = 'email';       // 邮箱脱敏
    const MASK_ADDRESS = 'address';   // 地址脱敏
    // 通用脱敏方法
    public static function mask($value, $type, $options = []) {
        switch ($type) {
            case self::MASK_FIXED:
                return self::maskFixed($value, $options['start'] ?? 3, 
                                      $options['length'] ?? 4, 
                                      $options['char'] ?? '*');
            case self::MASK_REGEX:
                return self::maskRegex($value, $options['pattern'], 
                                      $options['replacement']);
            case self::MASK_NAME:
                return self::maskName($value);
            case self::MASK_EMAIL:
                return self::maskEmail($value);
            case self::MASK_ADDRESS:
                $keep = $options['keep'] ?? 6;
                return self::maskAddress($value, $keep);
            default:
                return $value;
        }
    }
    private static function maskFixed($value, $start, $length, $char) {
        return substr_replace($value, str_repeat($char, $length), $start, $length);
    }
    private static function maskRegex($value, $pattern, $replacement) {
        return preg_replace($pattern, $replacement, $value);
    }
    private static function maskName($value) {
        return mb_substr($value, 0, 1) . str_repeat('*', mb_strlen($value) - 1);
    }
    private static function maskEmail($value) {
        $parts = explode('@', $value);
        $len = strlen($parts[0]);
        $keep = max(2, $len - 3);
        return substr($parts[0], 0, $keep) . str_repeat('*', $len - $keep) . '@' . $parts[1];
    }
    private static function maskAddress($value, $keep) {
        return mb_substr($value, 0, $keep) . str_repeat('*', mb_strlen($value) - $keep);
    }
}
// 使用示例
echo SensitiveDataMasker::mask('13812345678', 'fixed', ['start' => 3, 'length' => 4]);
// 138****5678

总结建议

  1. 生产环境:建议使用专门的脱敏类库或框架
  2. 性能考虑:大批量数据时使用 SQL 层脱敏(如 CONCATSUBSTRING
  3. 安全性:脱敏不能完全替代权限控制
  4. 测试环境:可以使用 faker 生成真实度高的测试数据

选择哪种方式取决于你的具体需求、数据量和性能要求。

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