安全最佳实践
本文档提供统一企业数字平台的安全配置建议,帮助您构建安全可靠的企业应用。
安全架构
┌─────────────────────────────────────────────────────────────────┐
│ 安全防护体系 │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 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 证书已正确配置
- 防火墙规则已配置
- 数据库访问已限制
- 敏感配置已加密
- 日志记录已启用
- 备份策略已配置
定期检查
- 安全补丁已更新
- 证书有效期检查
- 访问权限审计
- 异常登录检查
- 漏洞扫描
- 渗透测试