PHP 怎么PHP 选择性披露

wen PHP项目 3

本文目录导读:

PHP 怎么PHP 选择性披露

  1. 使用字符串函数直接脱敏(最常用)
  2. 基于用户权限的动态选择性披露
  3. 使用模型层的访问器(Laravel 风格)
  4. 使用中间件进行全局过滤(API 场景)
  5. 数据库级别的视图(只读场景)
  6. 处理 JSON 数据中的选择性披露
  7. 最佳实践建议

在 PHP 中实现“选择性披露”通常意味着:只向特定用户(或特定权限等级)显示部分敏感数据,而对其他用户隐藏或脱敏这些数据

常见的应用场景包括:

  • 用户手机号:显示 138****1234
  • 用户邮箱:显示 a***@example.com
  • 银行卡号:显示 尾号 1234
  • 系统密码:显示
  • 用户真实姓名:只显示姓氏,如 张*

以下是几种常见的实现方案,按复杂度从低到高排列:


使用字符串函数直接脱敏(最常用)

这是最直接、性能最好的方式,适用于固定格式的数据。

function maskPhone($phone) {
    return substr($phone, 0, 3) . '****' . substr($phone, -4);
}
function maskEmail($email) {
    $parts = explode('@', $email);
    $name = $parts[0];
    $domain = $parts[1];
    // 隐藏用户名的一部分
    $maskedName = substr($name, 0, 2) . str_repeat('*', max(0, strlen($name) - 4)) . substr($name, -1);
    return $maskedName . '@' . $domain;
}
function maskName($name) {
    if (mb_strlen($name) <= 1) {
        return $name;
    }
    // 保留第一个字符,其余隐藏
    return mb_substr($name, 0, 1) . str_repeat('*', mb_strlen($name) - 1);
}
// 使用示例
echo maskPhone('13812345678'); // 138****5678
echo maskEmail('example@test.com'); // ex****e@test.com
echo maskName('张三'); // 张*

基于用户权限的动态选择性披露

如果希望根据用户的角色(如普通用户、管理员、客服)来决定是否真正显示原数据:

class SelectiveDisclosure {
    public static function maskSensitiveData($data, $type, $userRole = 'user') {
        // 管理员/客服可以看到完整数据
        if (in_array($userRole, ['admin', 'support'])) {
            return $data;
        }
        switch ($type) {
            case 'phone':
                return substr($data, 0, 3) . '****' . substr($data, -4);
            case 'email':
                $parts = explode('@', $data);
                $name = $parts[0];
                $maskedName = substr($name, 0, 2) . '***';
                return $maskedName . '@' . $parts[1];
            case 'id_card':
                return substr($data, 0, 3) . '********' . substr($data, -4);
            default:
                return '***';
        }
    }
}
// 使用
$userRole = getUserRole(); // 'admin' 或 'user'
echo SelectiveDisclosure::maskSensitiveData('13812345678', 'phone', $userRole);

使用模型层的访问器(Laravel 风格)

如果你使用 MVC 框架(如 Laravel),可以在模型层定义访问器,根据上下文自动处理。

// User.php
class User extends Model {
    // 获取手机号时自动脱敏
    public function getPhoneAttribute($value) {
        // 检查当前用户是否允许查看原数据
        if (auth()->user() && auth()->user()->can('view_sensitive_phone')) {
            return $value;
        }
        return substr($value, 0, 3) . '****' . substr($value, -4);
    }
    // 获取邮箱时自动脱敏
    public function getEmailAttribute($value) {
        if (auth()->user() && auth()->user()->can('view_sensitive_email')) {
            return $value;
        }
        $parts = explode('@', $value);
        return substr($parts[0], 0, 2) . '***@' . $parts[1];
    }
}

使用中间件进行全局过滤(API 场景)

对于 RESTful API,可以编写中间件在响应输出前替换敏感字段:

// SelectiveDisclosureMiddleware.php
class SelectiveDisclosureMiddleware {
    public function handle($request, Closure $next) {
        $response = $next($request);
        // 只在响应中处理
        if (method_exists($response, 'getData')) {
            $data = $response->getData(true);
            $data = $this->maskSensitiveFields($data, $request->user());
            $response->setData($data);
        }
        return $response;
    }
    private function maskSensitiveFields($data, $user) {
        // 深度遍历数组/对象,对特定字段进行脱敏
        array_walk_recursive($data, function(&$value, $key) use ($user) {
            if (in_array($key, ['phone', 'email', 'id_card'])) {
                if (!$user || !$user->can('view_' . $key)) {
                    $value = maskField($key, $value);
                }
            }
        });
        return $data;
    }
}

数据库级别的视图(只读场景)

如果希望直接在数据库层面做选择性披露,可以创建数据库视图:

-- 普通用户视图
CREATE VIEW user_safe_view AS 
SELECT 
    id,
    CONCAT(LEFT(phone, 3), '****', RIGHT(phone, 4)) as phone,
    CONCAT(LEFT(email, 2), '***@', SUBSTRING_INDEX(email, '@', -1)) as email,
    name
FROM users;

然后在 PHP 中根据用户角色决定查询哪个表或视图。


处理 JSON 数据中的选择性披露

如果数据以 JSON 存储(如 API 响应中的嵌套数组):

function maskJsonSensitiveData(array $data, string $userRole): array {
    foreach ($data as $key => &$value) {
        if ($key === 'phone' && is_string($value)) {
            // 普通用户脱敏,管理员保留
            $value = ($userRole === 'admin') ? $value : substr($value, 0, 3) . '****' . substr($value, -4);
        } elseif (is_array($value)) {
            $value = maskJsonSensitiveData($value, $userRole); // 递归处理嵌套
        }
    }
    return $data;
}

最佳实践建议

场景 推荐方案
简单的字段脱敏(固定格式) 字符串函数直接处理
需要根据用户权限动态披露 模型访问器 + 权限检查
API 接口统一处理 中间件过滤
多语言/多字符集(中文字符) 使用 mb_substr() 处理
性能敏感 在 SQL 查询中使用 CONCAT 直接脱敏
需要审计日志 在数据输出前记录原始值和脱敏后的值

安全提示

  • 永远不要在前端(JavaScript)做关键数据的选择性披露,因为前端代码可以被篡改。
  • 所有敏感数据的脱敏操作必须在服务端(PHP)完成
  • 如果你需要“选择性地向不同用户展示不同数据”,建议配合一个权限系统来处理。

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