安全 认证 加密 IoT安全

安全最佳实践

平台安全配置指南,包括认证授权、数据安全、网络安全、IoT安全等

安全最佳实践

本文档提供统一企业数字平台的安全配置建议,帮助您构建安全可靠的企业应用。

安全架构

┌─────────────────────────────────────────────────────────────────┐
│                        安全防护体系                              │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐           │
│  │ WAF     │  │ DDoS    │  │ 入侵    │  │ 漏洞    │           │
│  │ 防护    │  │ 防护    │  │ 检测    │  │ 扫描    │           │
│  └─────────┘  └─────────┘  └─────────┘  └─────────┘           │
├─────────────────────────────────────────────────────────────────┤
│                        应用安全层                                │
│  ┌───────────────────────────────────────────────────────────┐ │
│  │  身份认证 │ 访问控制 │ 数据加密 │ 审计日志 │ 安全审计   │ │
│  └───────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│                        网络安全层                                │
│  ┌───────────────────────────────────────────────────────────┐ │
│  │  防火墙 │ VPN │ 网络隔离 │ TLS加密 │ 证书管理            │ │
│  └───────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│                        数据安全层                                │
│  ┌───────────────────────────────────────────────────────────┐ │
│  │  数据加密 │ 密钥管理 │ 数据脱敏 │ 备份恢复 │ 数据销毁   │ │
│  └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

身份认证

密码策略

# 密码策略配置
password_policy:
  min_length: 12                    # 最小长度
  require_uppercase: true           # 需要大写字母
  require_lowercase: true           # 需要小写字母
  require_numbers: true             # 需要数字
  require_special_chars: true       # 需要特殊字符
  max_age_days: 90                  # 密码有效期
  history_count: 5                  # 不能重复最近5个密码
  lockout_threshold: 5              # 5次失败后锁定
  lockout_duration_minutes: 30      # 锁定30分钟

多因素认证 (MFA)

// MFA 配置
interface MFAConfig {
  enabled: boolean;
  methods: ('totp' | 'sms' | 'email' | 'hardware_key')[];
  required_for: ('login' | 'sensitive_operations' | 'admin_access')[];
  remember_device_days: number;
}

const mfaConfig: MFAConfig = {
  enabled: true,
  methods: ['totp', 'sms'],
  required_for: ['login', 'sensitive_operations', 'admin_access'],
  remember_device_days: 30,
};

// TOTP 验证实现
import { authenticator } from 'otplib';

class TOTPService {
  generateSecret(): string {
    return authenticator.generateSecret();
  }

  generateQRCode(secret: string, user: string): string {
    return authenticator.keyuri(user, 'Enterprise Platform', secret);
  }

  verify(token: string, secret: string): boolean {
    return authenticator.verify({ token, secret });
  }
}

单点登录 (SSO)

支持多种 SSO 协议:

# SAML 配置
saml:
  enabled: true
  entity_id: "https://platform.example.com/saml/metadata"
  acs_url: "https://platform.example.com/saml/acs"
  slo_url: "https://platform.example.com/saml/slo"
  certificate: "/path/to/certificate.pem"
  private_key: "/path/to/private_key.pem"
  
  # IdP 配置
  idp:
    entity_id: "https://idp.example.com"
    sso_url: "https://idp.example.com/sso"
    slo_url: "https://idp.example.com/slo"
    certificate: "/path/to/idp_certificate.pem"

# OIDC 配置
oidc:
  enabled: true
  issuer: "https://idp.example.com"
  client_id: "your-client-id"
  client_secret: "your-client-secret"
  redirect_uri: "https://platform.example.com/auth/callback"
  scopes: ["openid", "profile", "email"]

会话管理

// 会话配置
const sessionConfig = {
  // 会话超时
  idle_timeout: 30 * 60 * 1000,      // 30分钟无操作超时
  absolute_timeout: 8 * 60 * 60 * 1000, // 8小时绝对超时
  
  // 会话安全
  cookie: {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    domain: '.example.com',
  },
  
  // 并发控制
  max_concurrent_sessions: 3,
  
  // 会话固定防护
  regenerate_on_login: true,
  regenerate_on_privilege_change: true,
};

// 会话管理实现
class SessionManager {
  async createSession(userId: string, deviceInfo: DeviceInfo): Promise<Session> {
    // 检查并发会话数
    const activeSessions = await this.getActiveSessions(userId);
    if (activeSessions.length >= sessionConfig.max_concurrent_sessions) {
      // 踢出最早的会话
      await this.terminateSession(activeSessions[0].id);
    }
    
    const session = {
      id: generateSecureId(),
      userId,
      deviceInfo,
      createdAt: new Date(),
      lastActivityAt: new Date(),
      expiresAt: new Date(Date.now() + sessionConfig.absolute_timeout),
    };
    
    await this.saveSession(session);
    return session;
  }

  async validateSession(sessionId: string): Promise<boolean> {
    const session = await this.getSession(sessionId);
    if (!session) return false;
    
    const now = Date.now();
    
    // 检查绝对超时
    if (now > session.expiresAt.getTime()) {
      await this.terminateSession(sessionId);
      return false;
    }
    
    // 检查空闲超时
    if (now - session.lastActivityAt.getTime() > sessionConfig.idle_timeout) {
      await this.terminateSession(sessionId);
      return false;
    }
    
    // 更新最后活动时间
    await this.updateLastActivity(sessionId);
    return true;
  }
}

访问控制

RBAC 权限模型

// 角色定义
interface Role {
  id: string;
  name: string;
  permissions: Permission[];
  inherits?: string[];  // 继承的角色
}

// 权限定义
interface Permission {
  resource: string;     // 资源类型
  action: string;       // 操作类型
  conditions?: Condition[];  // 条件限制
}

// 示例角色配置
const roles: Role[] = [
  {
    id: 'admin',
    name: '系统管理员',
    permissions: [
      { resource: '*', action: '*' }  // 所有权限
    ]
  },
  {
    id: 'device_manager',
    name: '设备管理员',
    permissions: [
      { resource: 'device', action: 'read' },
      { resource: 'device', action: 'write' },
      { resource: 'device', action: 'delete' },
      { resource: 'product', action: 'read' },
      { resource: 'rule', action: '*' },
    ]
  },
  {
    id: 'device_operator',
    name: '设备操作员',
    permissions: [
      { resource: 'device', action: 'read' },
      { resource: 'device', action: 'control' },
      { 
        resource: 'device', 
        action: 'write',
        conditions: [{ field: 'groupId', operator: 'in', value: '${user.groups}' }]
      },
    ]
  },
  {
    id: 'viewer',
    name: '只读用户',
    permissions: [
      { resource: 'device', action: 'read' },
      { resource: 'dashboard', action: 'read' },
    ]
  }
];

权限检查中间件

// 权限检查装饰器
function RequirePermission(resource: string, action: string) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      const user = getCurrentUser();
      
      if (!await checkPermission(user, resource, action)) {
        throw new ForbiddenError(`无权限执行 ${resource}:${action}`);
      }
      
      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

// 使用示例
class DeviceController {
  @RequirePermission('device', 'read')
  async getDevices() {
    return await deviceService.list();
  }

  @RequirePermission('device', 'write')
  async updateDevice(id: string, data: any) {
    return await deviceService.update(id, data);
  }

  @RequirePermission('device', 'delete')
  async deleteDevice(id: string) {
    return await deviceService.delete(id);
  }
}

数据权限

// 数据权限过滤
class DataPermissionFilter {
  async applyFilter(user: User, query: any, resource: string): Promise<any> {
    const dataScope = await this.getDataScope(user, resource);
    
    switch (dataScope.type) {
      case 'all':
        // 所有数据
        return query;
        
      case 'department':
        // 本部门数据
        return { ...query, departmentId: user.departmentId };
        
      case 'department_and_below':
        // 本部门及下级部门
        const deptIds = await this.getDepartmentAndChildren(user.departmentId);
        return { ...query, departmentId: { $in: deptIds } };
        
      case 'self':
        // 仅本人数据
        return { ...query, ownerId: user.id };
        
      case 'custom':
        // 自定义数据范围
        return { ...query, ...dataScope.conditions };
        
      default:
        throw new Error('Unknown data scope');
    }
  }
}

数据安全

数据加密

传输加密

# TLS 配置
tls:
  min_version: "TLSv1.2"
  cipher_suites:
    - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  
  # HSTS 配置
  hsts:
    enabled: true
    max_age: 31536000
    include_subdomains: true
    preload: true

存储加密

import crypto from 'crypto';

class EncryptionService {
  private algorithm = 'aes-256-gcm';
  private keyLength = 32;
  private ivLength = 16;
  private tagLength = 16;

  constructor(private masterKey: Buffer) {}

  encrypt(plaintext: string): string {
    const iv = crypto.randomBytes(this.ivLength);
    const cipher = crypto.createCipheriv(this.algorithm, this.masterKey, iv);
    
    let encrypted = cipher.update(plaintext, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const tag = cipher.getAuthTag();
    
    // 格式: iv:tag:encrypted
    return `${iv.toString('hex')}:${tag.toString('hex')}:${encrypted}`;
  }

  decrypt(ciphertext: string): string {
    const [ivHex, tagHex, encrypted] = ciphertext.split(':');
    
    const iv = Buffer.from(ivHex, 'hex');
    const tag = Buffer.from(tagHex, 'hex');
    
    const decipher = crypto.createDecipheriv(this.algorithm, this.masterKey, iv);
    decipher.setAuthTag(tag);
    
    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
  }
}

// 敏感字段加密
@Entity()
class Customer {
  @Column()
  name: string;

  @Column({ transformer: new EncryptedTransformer() })
  idCard: string;  // 身份证号加密存储

  @Column({ transformer: new EncryptedTransformer() })
  bankAccount: string;  // 银行账号加密存储
}

数据脱敏

// 数据脱敏工具
class DataMasking {
  // 手机号脱敏: 138****8000
  static maskPhone(phone: string): string {
    return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
  }

  // 身份证脱敏: 110***********1234
  static maskIdCard(idCard: string): string {
    return idCard.replace(/(\d{3})\d{11}(\d{4})/, '$1***********$2');
  }

  // 邮箱脱敏: u***@example.com
  static maskEmail(email: string): string {
    const [local, domain] = email.split('@');
    return `${local[0]}***@${domain}`;
  }

  // 银行卡脱敏: **** **** **** 1234
  static maskBankCard(card: string): string {
    return `**** **** **** ${card.slice(-4)}`;
  }

  // 姓名脱敏: 张*明
  static maskName(name: string): string {
    if (name.length === 2) {
      return name[0] + '*';
    }
    return name[0] + '*'.repeat(name.length - 2) + name[name.length - 1];
  }
}

// 响应数据脱敏中间件
function dataMaskingMiddleware(schema: MaskingSchema) {
  return (req: Request, res: Response, next: NextFunction) => {
    const originalJson = res.json.bind(res);
    
    res.json = (data: any) => {
      const maskedData = applyMasking(data, schema, req.user);
      return originalJson(maskedData);
    };
    
    next();
  };
}

密钥管理

// 使用 HashiCorp Vault 管理密钥
import Vault from 'node-vault';

class KeyManagementService {
  private vault: Vault.client;

  constructor() {
    this.vault = Vault({
      endpoint: process.env.VAULT_ADDR,
      token: process.env.VAULT_TOKEN,
    });
  }

  async getEncryptionKey(keyName: string): Promise<Buffer> {
    const result = await this.vault.read(`secret/data/encryption/${keyName}`);
    return Buffer.from(result.data.data.key, 'base64');
  }

  async rotateKey(keyName: string): Promise<void> {
    // 生成新密钥
    const newKey = crypto.randomBytes(32);
    
    // 获取旧密钥
    const oldKey = await this.getEncryptionKey(keyName);
    
    // 保存新密钥
    await this.vault.write(`secret/data/encryption/${keyName}`, {
      data: {
        key: newKey.toString('base64'),
        previousKey: oldKey.toString('base64'),
        rotatedAt: new Date().toISOString(),
      }
    });
    
    // 触发数据重新加密任务
    await this.scheduleReEncryption(keyName);
  }
}

IoT 安全

设备认证

// 设备证书认证
class DeviceCertAuth {
  async authenticate(cert: X509Certificate, deviceId: string): Promise<boolean> {
    // 验证证书有效性
    if (!this.verifyCertificate(cert)) {
      return false;
    }
    
    // 验证证书与设备绑定
    const expectedCN = `device:${deviceId}`;
    if (cert.subject.CN !== expectedCN) {
      return false;
    }
    
    // 检查证书是否被吊销
    if (await this.isRevoked(cert.serialNumber)) {
      return false;
    }
    
    return true;
  }

  private verifyCertificate(cert: X509Certificate): boolean {
    // 验证证书链
    const caChain = this.loadCAChain();
    return cert.verify(caChain);
  }

  private async isRevoked(serialNumber: string): Promise<boolean> {
    // 检查 CRL 或 OCSP
    return await this.checkCRL(serialNumber);
  }
}

// 设备 Token 认证
class DeviceTokenAuth {
  async generateToken(deviceId: string, deviceSecret: string): Promise<string> {
    const timestamp = Date.now();
    const nonce = crypto.randomBytes(16).toString('hex');
    
    const signContent = `${deviceId}\n${timestamp}\n${nonce}`;
    const signature = crypto
      .createHmac('sha256', deviceSecret)
      .update(signContent)
      .digest('hex');
    
    return Buffer.from(JSON.stringify({
      deviceId,
      timestamp,
      nonce,
      signature,
    })).toString('base64');
  }

  async verifyToken(token: string): Promise<DeviceInfo | null> {
    const decoded = JSON.parse(Buffer.from(token, 'base64').toString());
    const { deviceId, timestamp, nonce, signature } = decoded;
    
    // 检查时间戳(5分钟有效期)
    if (Date.now() - timestamp > 5 * 60 * 1000) {
      return null;
    }
    
    // 检查 nonce 防重放
    if (await this.isNonceUsed(nonce)) {
      return null;
    }
    
    // 获取设备密钥并验证签名
    const device = await this.getDevice(deviceId);
    const expectedSignature = crypto
      .createHmac('sha256', device.secret)
      .update(`${deviceId}\n${timestamp}\n${nonce}`)
      .digest('hex');
    
    if (signature !== expectedSignature) {
      return null;
    }
    
    // 标记 nonce 已使用
    await this.markNonceUsed(nonce);
    
    return device;
  }
}

通信安全

# MQTT 安全配置
mqtt:
  # TLS 配置
  tls:
    enabled: true
    cert_file: /etc/mqtt/certs/server.crt
    key_file: /etc/mqtt/certs/server.key
    ca_file: /etc/mqtt/certs/ca.crt
    require_client_cert: true
    
  # 认证配置
  auth:
    type: jwt  # password, jwt, certificate
    jwt:
      secret: ${JWT_SECRET}
      algorithm: RS256
      
  # ACL 配置
  acl:
    - pattern: "device/+/telemetry"
      access: publish
      users: ["device:*"]
      
    - pattern: "device/+/command"
      access: subscribe
      users: ["device:*"]
      
    - pattern: "device/#"
      access: all
      users: ["admin"]

固件安全

// 固件签名验证
class FirmwareVerifier {
  async verify(firmware: Buffer, signature: Buffer): Promise<boolean> {
    const publicKey = await this.getSigningPublicKey();
    
    const verify = crypto.createVerify('SHA256');
    verify.update(firmware);
    
    return verify.verify(publicKey, signature);
  }

  async verifyAndExtract(signedFirmware: Buffer): Promise<Buffer> {
    // 签名格式: [4字节签名长度][签名][固件内容]
    const signatureLength = signedFirmware.readUInt32BE(0);
    const signature = signedFirmware.slice(4, 4 + signatureLength);
    const firmware = signedFirmware.slice(4 + signatureLength);
    
    if (!await this.verify(firmware, signature)) {
      throw new Error('固件签名验证失败');
    }
    
    return firmware;
  }
}

// 安全 OTA 升级
class SecureOTA {
  async upgrade(deviceId: string, firmwareId: string): Promise<void> {
    const firmware = await this.getFirmware(firmwareId);
    
    // 验证固件签名
    await this.firmwareVerifier.verifyAndExtract(firmware.data);
    
    // 生成一次性下载 URL
    const downloadUrl = await this.generateSecureDownloadUrl(firmwareId, deviceId);
    
    // 发送升级指令
    await this.sendUpgradeCommand(deviceId, {
      url: downloadUrl,
      version: firmware.version,
      checksum: firmware.checksum,
      checksumType: 'sha256',
    });
  }

  private async generateSecureDownloadUrl(firmwareId: string, deviceId: string): Promise<string> {
    const token = jwt.sign(
      { firmwareId, deviceId, exp: Date.now() + 3600000 },
      process.env.DOWNLOAD_SECRET
    );
    return `https://ota.example.com/download/${firmwareId}?token=${token}`;
  }
}

审计日志

日志记录

// 审计日志结构
interface AuditLog {
  id: string;
  timestamp: Date;
  
  // 操作者信息
  actor: {
    type: 'user' | 'device' | 'system';
    id: string;
    name: string;
    ip: string;
    userAgent?: string;
  };
  
  // 操作信息
  action: string;
  resource: {
    type: string;
    id: string;
    name?: string;
  };
  
  // 操作详情
  details: {
    before?: any;
    after?: any;
    params?: any;
  };
  
  // 结果
  result: 'success' | 'failure';
  errorMessage?: string;
}

// 审计日志服务
class AuditLogService {
  async log(event: Partial<AuditLog>): Promise<void> {
    const log: AuditLog = {
      id: generateId(),
      timestamp: new Date(),
      ...event,
    } as AuditLog;
    
    // 写入数据库
    await this.repository.save(log);
    
    // 发送到日志分析系统
    await this.sendToSIEM(log);
  }

  // 审计日志装饰器
  static Audit(action: string, resourceType: string) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
      const originalMethod = descriptor.value;

      descriptor.value = async function (...args: any[]) {
        const startTime = Date.now();
        let result: 'success' | 'failure' = 'success';
        let errorMessage: string | undefined;
        let returnValue: any;

        try {
          returnValue = await originalMethod.apply(this, args);
        } catch (error) {
          result = 'failure';
          errorMessage = error.message;
          throw error;
        } finally {
          await auditLogService.log({
            action,
            resource: { type: resourceType, id: args[0] },
            actor: getCurrentActor(),
            result,
            errorMessage,
            details: { params: args, duration: Date.now() - startTime },
          });
        }

        return returnValue;
      };

      return descriptor;
    };
  }
}

// 使用示例
class DeviceService {
  @AuditLogService.Audit('device.delete', 'device')
  async deleteDevice(deviceId: string): Promise<void> {
    await this.repository.delete(deviceId);
  }
}

安全告警

// 安全事件检测
class SecurityEventDetector {
  private rules: SecurityRule[] = [
    {
      name: '暴力破解检测',
      condition: (events) => {
        const loginFailures = events.filter(e => 
          e.action === 'login' && e.result === 'failure'
        );
        return loginFailures.length >= 5;
      },
      window: 5 * 60 * 1000,  // 5分钟窗口
      severity: 'high',
    },
    {
      name: '异常登录位置',
      condition: async (events, context) => {
        const loginEvent = events[0];
        const lastLocation = await this.getLastLoginLocation(loginEvent.actor.id);
        const currentLocation = await this.getLocationByIP(loginEvent.actor.ip);
        
        // 检查是否在短时间内从不同地理位置登录
        return this.isImpossibleTravel(lastLocation, currentLocation);
      },
      severity: 'medium',
    },
    {
      name: '敏感操作检测',
      condition: (events) => {
        const sensitiveActions = ['user.delete', 'role.modify', 'config.change'];
        return events.some(e => sensitiveActions.includes(e.action));
      },
      severity: 'info',
    },
  ];

  async detect(event: AuditLog): Promise<SecurityAlert[]> {
    const alerts: SecurityAlert[] = [];
    
    for (const rule of this.rules) {
      const windowEvents = await this.getEventsInWindow(
        event.actor.id,
        rule.window
      );
      
      if (await rule.condition(windowEvents, { currentEvent: event })) {
        alerts.push({
          rule: rule.name,
          severity: rule.severity,
          event,
          timestamp: new Date(),
        });
      }
    }
    
    return alerts;
  }
}

安全检查清单

部署前检查

  • 所有默认密码已修改
  • TLS 证书已正确配置
  • 防火墙规则已配置
  • 数据库访问已限制
  • 敏感配置已加密
  • 日志记录已启用
  • 备份策略已配置

定期检查

  • 安全补丁已更新
  • 证书有效期检查
  • 访问权限审计
  • 异常登录检查
  • 漏洞扫描
  • 渗透测试

下一步