智能配电系统用户敏感数据脱敏详细设计:从静态遮盖到动态策略

1. 脱敏设计的核心挑战与设计原则

在智能配电系统中,敏感数据贯穿于用户用电行为、设备运行状态、能源交易信息、系统操作日志等各个层面。传统的数据脱敏方案往往采用"一刀切"的静态规则,无法适应配电系统多角色、多场景的精细化数据访问需求。

1.1 配电系统敏感数据分类矩阵

数据类别 敏感级别 数据类型 示例数据 泄露风险
用户身份信息 P3(最高) 结构化 姓名、身份证号、手机号、住址 个人隐私泄露、精准诈骗
用电行为数据 P2(高) 时序数据 每小时用电量、用电曲线、负荷特征 用户行为分析、家庭状态推断
设备运行数据 P2(高) 结构化+时序 设备ID、地理位置、运行参数 关键基础设施暴露、物理安全威胁
能源交易信息 P2(高) 交易记录 电价、交易量、账户余额 商业机密、市场价格操纵
系统操作日志 P1(中) 日志数据 操作命令、IP地址、时间戳 系统漏洞分析、攻击路径还原

1.2 分层脱敏设计原则

执行层面 策略驱动 静态数据脱敏
ETL/批处理 动态数据脱敏
API网关 实时脱敏
根据访问上下文 数据安全策略引擎 访问控制策略 原始敏感数据 数据分类与标记 存储层脱敏 传输层脱敏 展示层脱敏 脱敏后存储 安全传输 安全展示

四层脱敏架构的核心设计思想:

  • 策略与执行分离:脱敏规则集中管理,执行点分布式部署
  • 上下文感知:根据访问者角色、场景、时间动态调整脱敏强度
  • 可逆与不可逆结合:平衡数据可用性与安全性
  • 全链路审计:记录脱敏操作,满足合规要求

2. 脱敏引擎核心架构设计

2.1 统一脱敏服务架构

c 复制代码
// 脱敏引擎核心数据结构
typedef struct {
    char data_type[32];          // 数据类型:phone, id_card, address等
    char algorithm[32];          // 脱敏算法:mask, hash, encrypt等
    char *original_value;        // 原始值
    char *masked_value;          // 脱敏后值
    int sensitivity_level;       // 敏感等级:1-5
    time_t expire_time;          // 脱敏有效期
    char context_hash[64];       // 访问上下文哈希
} data_masking_request;

typedef struct {
    char rule_id[64];            // 规则ID
    char data_pattern[128];      // 数据匹配模式(正则或字段名)
    char algorithm_name[32];     // 算法名称
    char *algorithm_params;      // JSON格式算法参数
    char applicable_roles[256];  // 适用角色(逗号分隔)
    char applicable_scenes[256]; // 适用场景
    time_t valid_from;           // 生效时间
    time_t valid_to;             // 失效时间
    int priority;                // 规则优先级
} masking_rule;

// 脱敏引擎核心接口
typedef struct {
    // 初始化引擎
    int (*init)(const char *config_path);
    
    // 注册脱敏算法
    int (*register_algorithm)(const char *name, 
                              masking_algorithm_func func);
    
    // 执行脱敏
    int (*mask_data)(data_masking_request *request,
                     const char *user_role,
                     const char *access_context);
    
    // 批量脱敏
    int (*batch_mask)(data_masking_request requests[],
                      int count,
                      const char *user_role);
    
    // 规则管理
    int (*add_rule)(masking_rule *rule);
    int (*update_rule)(const char *rule_id, masking_rule *rule);
    int (*delete_rule)(const char *rule_id);
    
    // 审计日志
    int (*query_audit_log)(const char *filter, 
                           audit_log_entry **logs,
                           int *count);
} data_masking_engine;

2.2 脱敏算法库详细设计

2.2.1 基础脱敏算法实现
c 复制代码
// 算法参数结构
typedef struct {
    int keep_prefix_len;      // 保留前缀长度
    int keep_suffix_len;      // 保留后缀长度
    char mask_char;           // 遮盖字符
    int hash_salt_len;        // 哈希盐长度
    char *hash_salt;          // 哈希盐值
    char *crypto_key_id;      // 加密密钥ID
} masking_algorithm_params;

// 遮盖算法(适用于显示场景)
char* mask_algorithm(const char *original, 
                     masking_algorithm_params *params) {
    if (original == NULL || params == NULL) return NULL;
    
    int orig_len = strlen(original);
    int total_keep = params->keep_prefix_len + params->keep_suffix_len;
    
    // 检查参数有效性
    if (total_keep >= orig_len) {
        // 保留部分超过长度,返回全遮盖
        char *result = malloc(orig_len + 1);
        memset(result, params->mask_char, orig_len);
        result[orig_len] = '\0';
        return result;
    }
    
    char *result = malloc(orig_len + 1);
    
    // 保留前缀
    if (params->keep_prefix_len > 0) {
        strncpy(result, original, params->keep_prefix_len);
    }
    
    // 中间部分遮盖
    int mask_len = orig_len - total_keep;
    memset(result + params->keep_prefix_len, 
           params->mask_char, 
           mask_len);
    
    // 保留后缀
    if (params->keep_suffix_len > 0) {
        strncpy(result + params->keep_prefix_len + mask_len,
                original + orig_len - params->keep_suffix_len,
                params->keep_suffix_len);
    }
    
    result[orig_len] = '\0';
    return result;
}

// 一致性哈希脱敏(适用于关联分析)
char* consistent_hash_algorithm(const char *original,
                                masking_algorithm_params *params) {
    // 使用带盐哈希,保证相同原始值始终得到相同脱敏值
    char salted_input[1024];
    snprintf(salted_input, sizeof(salted_input), "%s%s",
             params->hash_salt, original);
    
    uint8_t hash[32];
    sm3_hash((uint8_t*)salted_input, strlen(salted_input), hash);
    
    // 转换为十六进制字符串(或Base64)
    char *result = malloc(65);  // 32字节hex字符串
    for (int i = 0; i < 16; i++) {  // 取前16字节,缩短长度
        sprintf(result + i*2, "%02x", hash[i]);
    }
    result[32] = '\0';
    
    return result;
}

// 可逆加密脱敏(需要时可通过授权解密)
char* reversible_encrypt_algorithm(const char *original,
                                   masking_algorithm_params *params) {
    // 使用国密SM4加密
    uint8_t iv[16];
    generate_random_iv(iv, sizeof(iv));
    
    // 获取加密密钥
    uint8_t *key = get_key_from_hsm(params->crypto_key_id);
    if (key == NULL) return NULL;
    
    // 计算填充后长度
    size_t orig_len = strlen(original);
    size_t padded_len = ((orig_len + 15) / 16) * 16;
    
    uint8_t *padded_data = malloc(padded_len);
    memcpy(padded_data, original, orig_len);
    memset(padded_data + orig_len, 0, padded_len - orig_len);
    
    // 加密
    uint8_t *ciphertext = malloc(padded_len);
    sm4_cbc_encrypt(padded_data, padded_len, key, iv, ciphertext);
    
    // 组合:IV + 密文 + 原始长度
    char *result = malloc(16 + padded_len + 8 + 1);
    memcpy(result, iv, 16);
    memcpy(result + 16, ciphertext, padded_len);
    memcpy(result + 16 + padded_len, &orig_len, sizeof(size_t));
    
    // 转换为Base64便于存储
    char *base64_result = base64_encode((uint8_t*)result, 
                                        16 + padded_len + 8);
    
    free(padded_data);
    free(ciphertext);
    free(result);
    
    return base64_result;
}
2.2.2 配电场景专用算法
c 复制代码
// 用电曲线数据脱敏(保持统计特征,隐藏细节)
typedef struct {
    float *original_load_curve;  // 原始负荷曲线(96点/天)
    float *masked_load_curve;    // 脱敏后曲线
    int points_per_day;          // 每天数据点数
    int days;                    // 天数
    float noise_level;           // 噪声水平(0-1)
    int aggregation_level;       // 聚合级别(1:小时, 2:4小时, 3:天)
} load_curve_masking_request;

float* mask_load_curve(load_curve_masking_request *request) {
    float *result = malloc(request->points_per_day * request->days * sizeof(float));
    
    for (int d = 0; d < request->days; d++) {
        for (int p = 0; p < request->points_per_day; p++) {
            int idx = d * request->points_per_day + p;
            float original = request->original_load_curve[idx];
            
            // 根据聚合级别调整脱敏策略
            switch (request->aggregation_level) {
                case 1:  // 小时级:添加随机噪声
                    result[idx] = original * (1.0 + 
                        ((float)rand()/RAND_MAX - 0.5) * request->noise_level);
                    break;
                    
                case 2:  // 4小时级:同时间段平均值
                    {
                        int block_start = (p / 4) * 4;
                        float sum = 0;
                        for (int i = 0; i < 4; i++) {
                            if (block_start + i < request->points_per_day) {
                                sum += request->original_load_curve[d * request->points_per_day + block_start + i];
                            }
                        }
                        result[idx] = sum / 4.0;
                    }
                    break;
                    
                case 3:  // 天级:日总量不变,时间分布标准化
                    {
                        // 计算日总量
                        float daily_total = 0;
                        for (int i = 0; i < request->points_per_day; i++) {
                            daily_total += request->original_load_curve[d * request->points_per_day + i];
                        }
                        
                        // 使用标准居民负荷曲线比例分配
                        result[idx] = daily_total * standard_residential_pattern[p];
                    }
                    break;
                    
                default:
                    result[idx] = original;
                    break;
            }
            
            // 确保非负(用电量不能为负)
            if (result[idx] < 0) result[idx] = 0;
        }
    }
    
    return result;
}

// 地理位置模糊算法
typedef struct {
    double latitude;
    double longitude;
    int precision_level;  // 1: 精确(10米), 2: 模糊(100米), 3: 区域(1公里)
} geo_location;

geo_location mask_geo_location(geo_location original, int precision_level) {
    geo_location masked;
    
    switch (precision_level) {
        case 1:  // 精确级别,基本不模糊
            masked.latitude = original.latitude;
            masked.longitude = original.longitude;
            break;
            
        case 2:  // 模糊到百米级
            // 添加随机偏移(约100米范围内)
            masked.latitude = original.latitude + 
                ((double)rand()/RAND_MAX - 0.5) * 0.0009;  // ~100米
            masked.longitude = original.longitude + 
                ((double)rand()/RAND_MAX - 0.5) * 0.0009;
            break;
            
        case 3:  // 区域级别,模糊到公里级
            // 取网格中心点(1km网格)
            double grid_size = 0.009;  // 约1km
            masked.latitude = floor(original.latitude / grid_size) * grid_size + grid_size/2;
            masked.longitude = floor(original.longitude / grid_size) * grid_size + grid_size/2;
            break;
            
        default:
            // 使用地理哈希(geohash)模糊
            char geohash[13];
            encode_geohash(original.latitude, original.longitude, geohash, 6);  // 6位约1.2km精度
            decode_geohash(geohash, &masked.latitude, &masked.longitude);
            break;
    }
    
    return masked;
}

3. 上下文感知的动态脱敏策略

3.1 多维度访问上下文模型

c 复制代码
// 访问上下文数据结构
typedef struct {
    // 用户身份信息
    char user_id[64];
    char user_role[32];          // admin, operator, auditor, analyst
    char department[64];         // 所属部门
    int security_clearance;      // 安全等级 1-5
    
    // 访问场景
    char access_scene[32];       // realtime_monitor, report_gen, data_export, debug
    char client_ip[46];          // 客户端IP
    char client_type[32];        // web, mobile, api, internal
    
    // 时间上下文
    time_t access_time;
    int is_business_hour;        // 是否工作时间
    int is_emergency;            // 是否紧急情况
    
    // 数据访问模式
    int query_frequency;         // 查询频率(次/分钟)
    int data_volume;             // 本次访问数据量
    char query_purpose[128];     // 查询目的
    
    // 环境安全状态
    int system_security_level;   // 系统当前安全等级
    int is_vpn_connection;       // 是否VPN连接
    int location_risk_level;     // 地理位置风险等级
} access_context;

// 动态脱敏策略决策引擎
typedef struct {
    access_context context;
    char data_field[64];         // 数据字段名
    char data_type[32];          // 数据类型
    int default_sensitivity;     // 默认敏感等级
    
    // 决策输出
    char selected_algorithm[32];
    masking_algorithm_params params;
    int is_access_granted;       // 是否允许访问
    char reason[256];            // 决策理由
} masking_decision;

// 策略决策函数
masking_decision make_masking_decision(access_context *ctx, 
                                       const char *data_field,
                                       const char *original_value) {
    masking_decision decision;
    memset(&decision, 0, sizeof(decision));
    
    // 初始化决策
    memcpy(&decision.context, ctx, sizeof(access_context));
    strncpy(decision.data_field, data_field, sizeof(decision.data_field)-1);
    
    // 决策矩阵
    if (strcmp(data_field, "user_phone") == 0) {
        // 电话号码脱敏策略
        if (strcmp(ctx->user_role, "admin") == 0 && 
            ctx->security_clearance >= 4) {
            // 高级管理员,可查看完整信息
            strcpy(decision.selected_algorithm, "none");
            decision.is_access_granted = 1;
        } else if (strcmp(ctx->access_scene, "emergency_repair") == 0 &&
                   ctx->is_emergency == 1) {
            // 紧急抢修场景
            strcpy(decision.selected_algorithm, "mask");
            decision.params.keep_prefix_len = 3;
            decision.params.keep_suffix_len = 4;
            decision.params.mask_char = '*';
            decision.is_access_granted = 1;
        } else if (strcmp(ctx->user_role, "analyst") == 0 &&
                   strcmp(ctx->access_scene, "data_analysis") == 0) {
            // 数据分析场景,使用一致性哈希保持关联性
            strcpy(decision.selected_algorithm, "consistent_hash");
            decision.params.hash_salt = get_system_salt();
            decision.params.hash_salt_len = strlen(decision.params.hash_salt);
            decision.is_access_granted = 1;
        } else {
            // 默认情况:严格脱敏
            strcpy(decision.selected_algorithm, "mask");
            decision.params.keep_prefix_len = 0;
            decision.params.keep_suffix_len = 0;
            decision.params.mask_char = '*';
            decision.is_access_granted = 1;
        }
    } else if (strncmp(data_field, "power_consumption", 17) == 0) {
        // 用电数据脱敏策略
        if (strcmp(ctx->user_role, "admin") == 0 ||
            (strcmp(ctx->user_role, "operator") == 0 && 
             ctx->is_business_hour == 1)) {
            strcpy(decision.selected_algorithm, "none");
            decision.is_access_granted = 1;
        } else if (strcmp(ctx->access_scene, "report_generation") == 0) {
            // 报告生成:聚合脱敏
            strcpy(decision.selected_algorithm, "aggregate_mask");
            decision.is_access_granted = 1;
        } else {
            // 其他角色:噪声脱敏
            strcpy(decision.selected_algorithm, "noise_mask");
            decision.is_access_granted = 1;
        }
    }
    
    // 记录决策审计信息
    log_masking_decision(&decision, original_value);
    
    return decision;
}

3.2 基于角色的脱敏策略配置

yaml 复制代码
# masking_policies.yaml
version: "2.0"
policies:
  - policy_id: "POL-USER-PHONE-001"
    name: "用户手机号脱敏策略"
    description: "根据不同角色动态脱敏手机号"
    data_pattern: "user_phone"
    rules:
      - rule_id: "RULE-ADMIN-FULL"
        roles: ["system_admin", "security_admin"]
        conditions:
          - field: "security_clearance"
            operator: ">="
            value: 4
        algorithm: "none"
        priority: 100
        
      - rule_id: "RULE-OPERATOR-PARTIAL"
        roles: ["operation_admin", "maintenance_engineer"]
        conditions:
          - field: "access_scene"
            operator: "=="
            value: "emergency_repair"
          - field: "is_emergency"
            operator: "=="
            value: true
        algorithm: "mask"
        parameters:
          keep_prefix_len: 3
          keep_suffix_len: 4
          mask_char: "*"
        priority: 90
        
      - rule_id: "RULE-ANALYST-HASH"
        roles: ["data_analyst", "business_analyst"]
        conditions:
          - field: "access_scene"
            operator: "in"
            value: ["data_analysis", "report_generation"]
        algorithm: "consistent_hash"
        parameters:
          hash_algorithm: "sm3"
          salt_source: "system_salt_v1"
        priority: 80
        
      - rule_id: "RULE-DEFAULT-STRICT"
        roles: ["*"]  # 所有角色
        algorithm: "mask"
        parameters:
          keep_prefix_len: 0
          keep_suffix_len: 0
          mask_char: "*"
        priority: 10
  
  - policy_id: "POL-POWER-DATA-002"
    name: "用电数据脱敏策略"
    description: "用电曲线和负荷数据脱敏"
    data_pattern: "power_consumption_*"
    rules:
      - rule_id: "RULE-REALTIME-MONITOR"
        roles: ["system_admin", "operation_admin"]
        conditions:
          - field: "access_scene"
            operator: "=="
            value: "realtime_monitor"
        algorithm: "none"
        priority: 100
        
      - rule_id: "RULE-ANALYSIS-AGGREGATE"
        roles: ["data_analyst", "researcher"]
        algorithm: "load_curve_aggregate"
        parameters:
          aggregation_level: 2  # 4小时聚合
          noise_level: 0.1
        priority: 70
        
      - rule_id: "RULE-PUBLIC-REPORT"
        roles: ["public_user", "partner"]
        algorithm: "load_curve_standardized"
        parameters:
          standard_pattern: "residential_standard_2023"
        priority: 50

4. 数据脱敏实施架构

4.1 静态数据脱敏(ETL流程)

sql 复制代码
-- 数据库脱敏函数(PostgreSQL示例)
-- 1. 创建脱敏函数
CREATE OR REPLACE FUNCTION mask_phone_number(
    phone TEXT,
    keep_prefix INT DEFAULT 3,
    keep_suffix INT DEFAULT 4
) RETURNS TEXT AS $$
DECLARE
    phone_len INT;
    result TEXT;
BEGIN
    phone_len = LENGTH(phone);
    
    IF phone_len <= keep_prefix + keep_suffix THEN
        -- 长度不足,返回全遮盖
        RETURN REPEAT('*', phone_len);
    END IF;
    
    -- 构造脱敏结果
    result = LEFT(phone, keep_prefix) || 
             REPEAT('*', phone_len - keep_prefix - keep_suffix) ||
             RIGHT(phone, keep_suffix);
    
    RETURN result;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 2. 创建视图实现动态脱敏
CREATE VIEW v_masked_customer_info AS
SELECT 
    customer_id,
    mask_phone_number(phone_number, 3, 4) AS phone_masked,
    CASE 
        WHEN current_user IN ('admin', 'auditor') THEN full_name
        ELSE CONCAT(LEFT(full_name, 1), '**')
    END AS name_masked,
    mask_id_number(id_number, 6, 4) AS id_masked,
    -- 用电数据脱敏
    CASE 
        WHEN current_user = 'admin' THEN daily_consumption
        ELSE ROUND(daily_consumption, -1) -- 四舍五入到十位
    END AS consumption_masked
FROM customer_power_info;

-- 3. 数据脱敏ETL作业配置
{
  "etl_job": {
    "name": "daily_data_masking",
    "source": {
      "type": "postgresql",
      "table": "raw_customer_data"
    },
    "transformations": [
      {
        "field": "phone_number",
        "algorithm": "phone_mask",
        "parameters": {"keep_prefix": 3, "keep_suffix": 4}
      },
      {
        "field": "id_card",
        "algorithm": "id_card_mask",
        "parameters": {"show_first": 6, "show_last": 4}
      },
      {
        "field": "load_curve",
        "algorithm": "noise_addition",
        "parameters": {"noise_level": 0.05, "seed": "daily_salt"}
      }
    ],
    "destination": {
      "type": "postgresql",
      "table": "masked_customer_data"
    },
    "schedule": "0 2 * * *",  -- 每天凌晨2点
    "audit_logging": true
  }
}

4.2 API网关动态脱敏

python 复制代码
# API网关脱敏中间件
from flask import Flask, request, jsonify, g
import json
import hashlib
import re

app = Flask(__name__)

class APIMaskingMiddleware:
    def __init__(self, app):
        self.app = app
        self.masking_rules = self.load_masking_rules()
        
    def load_masking_rules(self):
        """加载脱敏规则配置"""
        with open('api_masking_rules.json', 'r') as f:
            return json.load(f)
    
    def get_user_context(self):
        """获取用户访问上下文"""
        return {
            'user_id': g.get('user_id', 'anonymous'),
            'user_role': g.get('user_role', 'guest'),
            'access_scene': request.endpoint,
            'client_ip': request.remote_addr,
            'access_time': request.headers.get('X-Request-Time'),
            'security_level': g.get('security_level', 1)
        }
    
    def should_mask_field(self, field_path, context):
        """判断字段是否需要脱敏"""
        for rule in self.masking_rules:
            # 检查字段匹配
            if re.match(rule['field_pattern'], field_path):
                # 检查角色匹配
                if '*' in rule['allowed_roles'] or context['user_role'] in rule['allowed_roles']:
                    # 检查条件匹配
                    if self.check_conditions(rule.get('conditions', []), context):
                        return rule
        return None
    
    def mask_value(self, value, rule, context):
        """执行脱敏"""
        algorithm = rule['algorithm']
        
        if algorithm == 'mask':
            keep_prefix = rule.get('keep_prefix', 0)
            keep_suffix = rule.get('keep_suffix', 0)
            return self.mask_string(value, keep_prefix, keep_suffix)
            
        elif algorithm == 'hash':
            salt = rule.get('salt', 'default_salt')
            return self.consistent_hash(value, salt)
            
        elif algorithm == 'round':
            decimals = rule.get('decimals', 0)
            return round(float(value), decimals)
            
        else:
            return value
    
    def mask_response_data(self, data, context):
        """递归脱敏响应数据"""
        if isinstance(data, dict):
            masked_dict = {}
            for key, value in data.items():
                field_path = f"{request.endpoint}.{key}"
                rule = self.should_mask_field(field_path, context)
                if rule:
                    masked_dict[key] = self.mask_value(value, rule, context)
                else:
                    masked_dict[key] = self.mask_response_data(value, context)
            return masked_dict
        elif isinstance(data, list):
            return [self.mask_response_data(item, context) for item in data]
        else:
            return data
    
    def __call__(self, environ, start_response):
        # 处理请求
        response = self.app(environ, start_response)
        
        # 如果是JSON响应,进行脱敏处理
        if response.headers.get('Content-Type', '').startswith('application/json'):
            # 获取原始响应数据
            original_data = json.loads(response.get_data())
            
            # 获取用户上下文
            context = self.get_user_context()
            
            # 执行脱敏
            masked_data = self.mask_response_data(original_data, context)
            
            # 更新响应
            response.set_data(json.dumps(masked_data))
            
            # 添加脱敏审计头
            response.headers['X-Data-Masking'] = 'applied'
            response.headers['X-Masking-Context'] = json.dumps(context)
        
        return response

# 使用示例
app.wsgi_app = APIMaskingMiddleware(app.wsgi_app)

@app.route('/api/power/customer/<customer_id>')
def get_customer_power_info(customer_id):
    # 模拟数据库查询
    customer_data = {
        'customer_id': customer_id,
        'name': '张三',
        'phone': '13800138000',
        'address': '北京市朝阳区某街道',
        'power_consumption': {
            'daily': 25.6,  # kWh
            'monthly': 768.2,
            'load_curve': [0.5, 0.3, 0.2, ...]  # 96点数据
        },
        'devices': [
            {'id': 'device_001', 'type': 'smart_meter', 'status': 'active'},
            {'id': 'device_002', 'type': 'inverter', 'status': 'active'}
        ]
    }
    return jsonify(customer_data)

if __name__ == '__main__':
    app.run(debug=True)

5. 脱敏审计与合规性保障

5.1 全链路审计日志

c 复制代码
// 脱敏审计日志系统
typedef struct {
    char log_id[64];            // 日志ID(UUID)
    time_t timestamp;           // 时间戳
    char user_id[64];           // 用户ID
    char user_role[32];         // 用户角色
    char operation_type[32];    // 操作类型:query, export, api_call
    char data_source[128];      // 数据源
    char data_field[64];        // 数据字段
    
    // 脱敏前内容(加密存储)
    char original_value_hash[64];  // 原始值哈希(SM3)
    uint8_t *encrypted_original;   // 加密的原始值(仅审计员可解密)
    size_t encrypted_len;
    
    // 脱敏后内容
    char masked_value[256];     // 脱敏后的值
    char algorithm_used[32];    // 使用的算法
    char algorithm_params[256]; // 算法参数(JSON格式)
    
    // 访问上下文
    char client_ip[46];
    char client_device[64];
    char access_reason[256];
    
    // 签名(防篡改)
    uint8_t signature[64];      // 对整个日志的SM2签名
} masking_audit_log;

// 审计日志记录函数
int log_masking_operation(masking_audit_log *log_entry) {
    // 1. 计算原始值哈希(用于验证,不存储明文)
    sm3_hash(log_entry->original_value, 
             strlen(log_entry->original_value),
             (uint8_t*)log_entry->original_value_hash);
    
    // 2. 加密原始值(使用审计专用密钥)
    uint8_t *audit_key = get_audit_encryption_key();
    log_entry->encrypted_len = encrypt_for_audit(
        log_entry->original_value, 
        strlen(log_entry->original_value),
        audit_key,
        &log_entry->encrypted_original
    );
    
    // 3. 计算日志条目的数字签名
    uint8_t log_hash[32];
    calculate_log_hash(log_entry, log_hash);
    
    // 使用审计私钥签名
    uint8_t *audit_private_key = get_audit_signing_key();
    sm2_sign(log_hash, sizeof(log_hash),
             audit_private_key,
             log_entry->signature);
    
    // 4. 写入安全审计存储(只追加,不可修改)
    return write_audit_log_secure(log_entry);
}

// 审计查询接口(仅授权人员可访问)
typedef struct {
    char query_id[64];
    char auditor_id[64];
    time_t start_time;
    time_t end_time;
    char filter_user_id[64];
    char filter_data_field[64];
    char filter_algorithm[32];
    int include_original_values;  // 是否包含原始值(需要特别授权)
    uint8_t query_signature[64];  // 查询请求签名
} audit_query_request;

// 安全审计查询
audit_log_result query_masking_audit_logs(audit_query_request *request) {
    // 1. 验证查询者权限
    if (!verify_auditor_permission(request->auditor_id, 
                                   request->include_original_values)) {
        return PERMISSION_DENIED;
    }
    
    // 2. 验证查询请求签名
    if (!verify_query_signature(request)) {
        return INVALID_SIGNATURE;
    }
    
    // 3. 执行查询
    audit_log_result result;
    result.logs = execute_audit_query(request);
    result.count = count_audit_logs(request);
    
    // 4. 如果请求包含原始值,需要解密
    if (request->include_original_values) {
        for (int i = 0; i < result.count; i++) {
            result.logs[i].original_value = 
                decrypt_audit_value(result.logs[i].encrypted_original,
                                   result.logs[i].encrypted_len);
        }
        
        // 记录原始值访问审计(二次审计)
        log_original_value_access(request->auditor_id, result.count);
    }
    
    return result;
}

5.2 合规性报告生成

python 复制代码
# 合规性报告生成器
import pandas as pd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib import colors

class ComplianceReportGenerator:
    def __init__(self, start_date, end_date):
        self.start_date = start_date
        self.end_date = end_date
        self.data = self.load_audit_data()
    
    def generate_gdpr_compliance_report(self):
        """生成GDPR合规报告"""
        report = {
            'summary': self.generate_summary(),
            'data_subject_rights': self.check_data_subject_rights(),
            'data_processing_logs': self.get_data_processing_logs(),
            'security_measures': self.evaluate_security_measures(),
            'breach_records': self.check_breach_records(),
            'recommendations': self.generate_recommendations()
        }
        
        # 生成PDF报告
        self.create_pdf_report(report)
        
        return report
    
    def generate_summary(self):
        """生成报告摘要"""
        total_operations = len(self.data)
        masking_operations = self.data[self.data['operation_type'] == 'mask'].shape[0]
        access_requests = self.data[self.data['operation_type'] == 'access'].shape[0]
        
        # 计算脱敏覆盖率
        sensitive_fields = ['phone', 'id_card', 'address', 'email', 'load_curve']
        coverage = {}
        
        for field in sensitive_fields:
            field_ops = self.data[self.data['data_field'].str.contains(field)]
            if not field_ops.empty:
                masked_ops = field_ops[field_ops['algorithm_used'] != 'none']
                coverage[field] = len(masked_ops) / len(field_ops) * 100
        
        return {
            'report_period': f"{self.start_date} to {self.end_date}",
            'total_operations': total_operations,
            'masking_operations': masking_operations,
            'access_requests': access_requests,
            'masking_coverage': coverage,
            'compliance_score': self.calculate_compliance_score()
        }
    
    def check_data_subject_rights(self):
        """检查数据主体权利保障情况"""
        rights_checks = {
            'right_to_access': self.check_right_to_access(),
            'right_to_rectification': self.check_right_to_rectification(),
            'right_to_erasure': self.check_right_to_erasure(),
            'right_to_restriction': self.check_right_to_restriction(),
            'right_to_data_portability': self.check_right_to_portability(),
            'right_to_object': self.check_right_to_object()
        }
        
        return rights_checks
    
    def create_pdf_report(self, report_data):
        """创建PDF格式合规报告"""
        filename = f"GDPR_Compliance_Report_{self.start_date}_{self.end_date}.pdf"
        doc = SimpleDocTemplate(filename, pagesize=letter)
        
        # 构建报告内容
        story = []
        styles = getSampleStyleSheet()
        
        # 标题
        title = Paragraph(f"智能配电系统数据保护合规报告", styles['Title'])
        story.append(title)
        
        # 报告期间
        period = Paragraph(f"报告期间: {report_data['summary']['report_period']}", 
                          styles['Normal'])
        story.append(period)
        
        # 摘要表格
        summary_data = [
            ['指标', '数值'],
            ['总操作数', report_data['summary']['total_operations']],
            ['脱敏操作数', report_data['summary']['masking_operations']],
            ['访问请求数', report_data['summary']['access_requests']],
            ['合规分数', f"{report_data['summary']['compliance_score']}/100"]
        ]
        
        summary_table = Table(summary_data)
        summary_table.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
            ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
            ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
            ('FONTSIZE', (0, 0), (-1, 0), 14),
            ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
            ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
            ('GRID', (0, 0), (-1, -1), 1, colors.black)
        ]))
        story.append(summary_table)
        
        # 生成PDF
        doc.build(story)
        
        return filename

6. 实施与验证

6.1 脱敏效果验证测试

python 复制代码
# 脱敏效果验证测试套件
import unittest
import re
from masking_engine import DataMaskingEngine

class TestDataMaskingEffectiveness(unittest.TestCase):
    
    def setUp(self):
        self.masking_engine = DataMaskingEngine('config/masking_rules.json')
        self.test_cases = self.load_test_cases()
    
    def test_phone_number_masking(self):
        """测试手机号脱敏效果"""
        test_phone = "13800138000"
        
        # 测试不同角色的脱敏结果
        test_scenarios = [
            {'role': 'admin', 'expected': '13800138000'},
            {'role': 'operator', 'expected': '138****8000'},
            {'role': 'analyst', 'expected': 'hash_abc123'},  # 哈希值
            {'role': 'guest', 'expected': '***********'}
        ]
        
        for scenario in test_scenarios:
            masked = self.masking_engine.mask_phone(
                test_phone, 
                scenario['role']
            )
            self.assertEqual(masked, scenario['expected'],
                           f"{scenario['role']}角色脱敏失败")
    
    def test_id_card_masking(self):
        """测试身份证号脱敏"""
        test_id = "110101199001011234"
        
        # 验证脱敏后格式
        masked = self.masking_engine.mask_id_card(test_id, 'operator')
        
        # 应该显示前6位和后4位
        self.assertTrue(masked.startswith('110101'))
        self.assertTrue(masked.endswith('1234'))
        self.assertIn('*', masked)  # 中间应该有遮盖字符
        
        # 总长度应该不变
        self.assertEqual(len(masked), len(test_id))
    
    def test_load_curve_privacy(self):
        """测试用电曲线隐私保护效果"""
        # 生成模拟用电曲线(96点,一天数据)
        original_curve = np.random.normal(1.0, 0.3, 96)
        
        # 脱敏处理
        masked_curve = self.masking_engine.mask_load_curve(
            original_curve, 
            'analyst',
            aggregation_level=2
        )
        
        # 验证统计特性保持
        orig_mean = np.mean(original_curve)
        masked_mean = np.mean(masked_curve)
        
        # 平均值应该接近(误差小于10%)
        self.assertAlmostEqual(orig_mean, masked_mean, delta=orig_mean*0.1)
        
        # 验证个体隐私保护:原始值与脱敏值的相关性应该降低
        correlation = np.corrcoef(original_curve, masked_curve)[0, 1]
        self.assertLess(correlation, 0.5,  # 相关性应该小于0.5
                       "用电曲线脱敏未能有效保护隐私")
    
    def test_consistency_for_analysis(self):
        """测试分析场景下的一致性脱敏"""
        # 相同的原始值在不同时间应该得到相同的脱敏值
        test_value = "13800138000"
        
        masked1 = self.masking_engine.mask_phone(test_value, 'analyst')
        masked2 = self.masking_engine.mask_phone(test_value, 'analyst')
        
        self.assertEqual(masked1, masked2,
                        "一致性脱敏失败:相同输入得到不同输出")
    
    def test_performance_benchmark(self):
        """测试脱敏性能"""
        import time
        
        # 准备测试数据
        test_data = [f"13800138{i:03d}" for i in range(10000)]
        
        start_time = time.time()
        
        # 批量脱敏
        masked_results = []
        for phone in test_data:
            masked = self.masking_engine.mask_phone(phone, 'operator')
            masked_results.append(masked)
        
        end_time = time.time()
        
        # 性能要求:每秒至少处理1000条记录
        processing_rate = len(test_data) / (end_time - start_time)
        self.assertGreater(processing_rate, 1000,
                          f"脱敏性能不足:{processing_rate:.0f}条/秒")
        
        print(f"脱敏性能:{processing_rate:.0f}条/秒")
    
    def test_audit_log_completeness(self):
        """测试审计日志完整性"""
        test_phone = "13800138000"
        user_role = "operator"
        
        # 执行脱敏操作
        masked = self.masking_engine.mask_phone(test_phone, user_role)
        
        # 验证审计日志被记录
        audit_logs = self.masking_engine.get_audit_logs(
            limit=1,
            order_by='timestamp desc'
        )
        
        self.assertGreater(len(audit_logs), 0,
                          "脱敏操作未记录审计日志")
        
        latest_log = audit_logs[0]
        
        # 验证日志内容
        self.assertEqual(latest_log['original_hash'], 
                        self.masking_engine.calculate_hash(test_phone))
        self.assertEqual(latest_log['user_role'], user_role)
        self.assertIsNotNone(latest_log['signature'],
                            "审计日志缺少数字签名")

if __name__ == '__main__':
    unittest.main()

6.2 部署与监控配置

yaml 复制代码
# Kubernetes部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: data-masking-service
  namespace: power-system
spec:
  replicas: 3
  selector:
    matchLabels:
      app: data-masking
  template:
    metadata:
      labels:
        app: data-masking
    spec:
      containers:
      - name: masking-service
        image: power-system/data-masking:2.1.0
        ports:
        - containerPort: 8080
        env:
        - name: MASKING_CONFIG_PATH
          value: "/app/config/masking_rules.yaml"
        - name: AUDIT_DB_URL
          valueFrom:
            secretKeyRef:
              name: masking-secrets
              key: audit-db-url
        - name: SECURITY_LEVEL
          value: "production"
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 15
        volumeMounts:
        - name: config-volume
          mountPath: /app/config
        - name: audit-volume
          mountPath: /var/log/audit
      volumes:
      - name: config-volume
        configMap:
          name: masking-config
      - name: audit-volume
        persistentVolumeClaim:
          claimName: audit-log-pvc
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
---
# 监控配置
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: data-masking-monitor
  namespace: power-system
spec:
  selector:
    matchLabels:
      app: data-masking
  endpoints:
  - port: http-metrics
    interval: 30s
    path: /metrics
  - port: http-metrics
    interval: 30s
    path: /masking-metrics
    params:
      type: ["performance", "compliance"]
---
# 自定义指标收集
apiVersion: v1
kind: ConfigMap
metadata:
  name: masking-metrics-config
data:
  metrics.yaml: |
    groups:
      - name: masking_performance
        rules:
        - record: masking_operations_total
          expr: sum by(algorithm, result) (masking_engine_operations_total)
          
        - record: masking_latency_seconds
          expr: histogram_quantile(0.95, 
                sum(rate(masking_engine_duration_seconds_bucket[5m])) 
                by (le, algorithm))
                
      - name: data_protection_compliance
        rules:
        - record: sensitive_data_exposure_rate
          expr: |
            masking_engine_exposed_records_total
            / 
            masking_engine_processed_records_total
            * 100
            
        - record: masking_policy_compliance
          expr: |
            masking_engine_compliant_operations_total
            /
            masking_engine_total_operations_total
            * 100

7. 总结:构建智能配电系统的数据隐私保护层

智能配电系统的数据脱敏设计不仅仅是技术实现,更是数据治理体系的重要组成部分。通过本方案的实施,可以达成以下目标:

7.1 关键技术成果

  1. 多粒度脱敏能力:从字段级到记录级,从静态到动态的完整脱敏体系
  2. 上下文感知策略:基于角色、场景、时间的智能化脱敏决策
  3. 平衡隐私与效用:在保护用户隐私的同时,最大限度保留数据价值
  4. 全链路可审计:满足GDPR、网络安全法等合规要求

7.2 配电系统特定收益

  • 用户信任建立:通过严格的数据保护增强用户信心
  • 合规风险降低:避免因数据泄露导致的法规处罚
  • 数据价值释放:在安全前提下支持精细化运营和增值服务
  • 系统韧性增强:减少敏感数据暴露面,降低安全攻击风险

通过本详细设计方案的实施,智能配电系统可以构建起符合国际标准、适应本土要求、具备行业特色的数据隐私保护能力,为能源数字化转型奠定坚实的安全基础。

相关推荐
陈鋆1 小时前
Langchain-Chatchat[四、RAG对话流程代码解析]
开发语言·python·langchain
ServBay1 小时前
Django 6.0 发布,新增原生任务队列与 CSP 支持
后端·python·django
β添砖java1 小时前
python第一阶段第九章异常、模块、包
开发语言·python
2501_941982051 小时前
企业微信Python SDK:高效群发消息实战
开发语言·python·企业微信
GoWjw1 小时前
内存管理【3】
linux·服务器·c++·ubuntu
用户12039112947262 小时前
AIGC 时代,数据库终于可以“听懂人话”了:从零打造自然语言操作 SQLite 的完整实战
python·sqlite·aigc
Q_Q5110082852 小时前
python+django/flask+vue农业电商服务系统
spring boot·python·pycharm·django·flask
帕巴啦2 小时前
Python计算累积频率——Origin绘制累积频率图
python·绘图·origin·累积频率·python计算累积频率·origin绘制累积频率图
Q_Q5110082852 小时前
python+django/flask+vue的基于疫情防控管理系统的数据可视化分析系统
spring boot·python·django·flask·node.js