企业信息化系统低代码平台安全性设计
引言
低代码平台通过可视化配置和快速开发能力,大幅提升企业应用交付效率。然而,由于平台开放了配置能力和脚本执行环境,安全性设计变得尤为重要。本文将探讨低代码平台的安全架构设计。
安全架构总览
低代码平台的安全体系分层设计:
| 安全层次 | 防护措施 | 关键技术 |
|---|---|---|
| 身份认证 | 多因素认证、单点登录 | OAuth2、SAML、JWT |
| 权限控制 | 细粒度权限、数据隔离 | RBAC、ABAC、行级权限 |
| 操作审计 | 行为记录、异常检测 | 审计日志、行为分析 |
| 代码安全 | 脚本审查、沙箱隔离 | AST分析、VM沙箱 |
细粒度权限控制
低代码平台需要支持多层次的权限控制:
// 权限控制引擎
class PermissionEngine {
constructor() {
this.policies = new Map();
this.roleHierarchy = new Map();
}
// 初始化角色层级
initRoleHierarchy(roles) {
for (const [role, parents] of Object.entries(roles)) {
this.roleHierarchy.set(role, parents || []);
}
}
// 检查权限
async checkPermission(context, resource, action) {
const user = context.user;
const roles = await this.getUserRoles(user.id);
// 检查直接权限
for (const role of roles) {
if (await this.hasDirectPermission(role, resource, action)) {
return true;
}
// 检查继承权限
if (await this.hasInheritedPermission(role, resource, action)) {
return true;
}
}
return false;
}
// 获取用户角色
async getUserRoles(userId) {
const userRoles = await db.query(`
SELECT r.id, r.code, r.level
FROM user_roles ur
JOIN roles r ON ur.role_id = r.id
WHERE ur.user_id = ?
`, [userId]);
return userRoles;
}
// 直接权限检查
async hasDirectPermission(role, resource, action) {
const policy = this.policies.get(`${role}:${resource}:${action}`);
return policy?.effect === 'allow';
}
// 继承权限检查
async hasInheritedPermission(role, resource, action) {
const parents = this.roleHierarchy.get(role.code) || [];
for (const parent of parents) {
if (await this.hasDirectPermission(parent, resource, action)) {
return true;
}
if (await this.hasInheritedPermission(parent, resource, action)) {
return true;
}
}
return false;
}
// 行级数据权限
async filterDataByPermission(userId, query, resource) {
const userRoles = await this.getUserRoles(userId);
const filters = [];
for (const role of userRoles) {
const dataPolicy = await this.getDataPolicy(role.id, resource);
if (dataPolicy) {
if (dataPolicy.type === 'own') {
// 仅看自己的数据
filters.push(`creator_id = ${userId}`);
} else if (dataPolicy.type === 'department') {
// 看部门数据
const deptId = await this.getUserDepartment(userId);
filters.push(`department_id = ${deptId}`);
} else if (dataPolicy.type === 'custom') {
// 自定义过滤条件
filters.push(dataPolicy.condition);
}
}
}
if (filters.length > 0) {
query.where = query.where
? `(${query.where}) AND (${filters.join(' OR ')})`
: filters.join(' OR ');
}
return query;
}
// 获取数据策略
async getDataPolicy(roleId, resource) {
return await db.query(`
SELECT * FROM data_policies
WHERE role_id = ? AND resource = ?
`, [roleId, resource]);
}
}
// 权限策略配置
const PermissionPolicies = {
// 应用级别权限
app: {
'app:create': { roles: ['admin'], actions: ['allow'] },
'app:edit': { roles: ['admin', 'developer'], actions: ['allow'] },
'app:view': { roles: ['admin', 'developer', 'operator'], actions: ['allow'] },
'app:delete': { roles: ['admin'], actions: ['allow'] }
},
// 数据表权限
table: {
'table:read': { roles: ['admin', 'developer', 'operator', 'viewer'], actions: ['allow'] },
'table:write': { roles: ['admin', 'developer', 'operator'], actions: ['allow'] },
'table:delete': { roles: ['admin', 'developer'], actions: ['allow'] }
},
// 字段级别权限
field: {
'field:sensitive': { roles: ['admin'], actions: ['allow'] },
'field:write': { roles: ['admin', 'developer'], actions: ['allow'] }
}
};
脚本执行沙箱
低代码平台的可视化脚本需要在隔离环境中执行:
// 沙箱执行环境
class SandboxExecutor {
constructor(config) {
this.timeout = config.timeout || 5000;
this.memoryLimit = config.memoryLimit || 128 * 1024 * 1024;
this.allowedAPIs = new Set(config.allowedAPIs || []);
this.blockedPatterns = new Set(config.blockedPatterns || []);
}
// 创建安全的执行上下文
createContext(userId, appId) {
return {
// 用户信息(只读)
user: {
id: userId,
roles: this.getUserRoles(userId),
props: this.getUserProps(userId)
},
// 应用上下文
app: {
id: appId,
name: this.getAppName(appId),
env: this.getAppEnv(appId)
},
// 安全的API白名单
api: this.createSafeAPI(userId, appId),
// 日志(受限)
console: {
log: (...args) => this.log('info', args),
warn: (...args) => this.log('warn', args),
error: (...args) => this.log('error', args)
},
// 禁止的全局对象
__proto__: null,
eval: undefined,
Function: undefined,
window: undefined,
document: undefined,
XMLHttpRequest: undefined,
fetch: undefined,
WebSocket: undefined
};
}
// 创建安全API封装
createSafeAPI(userId, appId) {
return {
// 数据库操作(仅限允许的表)
db: {
query: (table, conditions) => this.checkTableAccess(userId, appId, table, 'read')
? this.executeQuery(table, conditions)
: throw new Error('Access denied'),
insert: (table, data) => this.checkTableAccess(userId, appId, table, 'write')
? this.executeInsert(table, data)
: throw new Error('Access denied'),
update: (table, conditions, data) => this.checkTableAccess(userId, appId, table, 'write')
? this.executeUpdate(table, conditions, data)
: throw new Error('Access denied')
},
// HTTP请求(受限)
http: {
get: (url) => this.checkHTTPAllowed(url) ? this.safeGet(url) : throw new Error('URL not allowed'),
post: (url, data) => this.checkHTTPAllowed(url) ? this.safePost(url, data) : throw new Error('URL not allowed')
},
// 消息通知
notify: {
send: (type, message) => this.sendNotification(userId, type, message)
}
};
}
// 执行脚本
async execute(script, context) {
// 代码安全检查
this.validateScript(script);
// 创建VM上下文
const vmContext = vm.createContext(context);
try {
// 带超时和内存限制的执行
const result = await this.runWithLimits(script, vmContext);
return { success: true, result };
} catch (error) {
return { success: false, error: error.message };
}
}
// 代码安全检查
validateScript(script) {
// 危险模式检查
for (const pattern of this.blockedPatterns) {
if (pattern.test(script)) {
throw new Error(`Script contains blocked pattern: ${pattern}`);
}
}
// AST分析
const ast = acorn.parse(script, { ecmaVersion: 2020 });
this.analyzeAST(ast);
}
// AST安全分析
analyzeAST(node) {
// 递归检查AST节点
if (node.type === 'CallExpression') {
// 检查敏感函数调用
const dangerousCallees = ['eval', 'Function', 'setTimeout', 'setInterval'];
if (dangerousCallees.includes(node.callee.name)) {
throw new Error(`Dangerous function call: ${node.callee.name}`);
}
}
// 递归检查子节点
for (const key in node) {
if (node[key] && typeof node[key] === 'object') {
if (Array.isArray(node[key])) {
node[key].forEach(n => this.analyzeAST(n));
} else if (node[key].type) {
this.analyzeAST(node[key]);
}
}
}
}
// 带限制的执行
async runWithLimits(script, context) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Script execution timeout'));
}, this.timeout);
try {
const result = vm.runInContext(script, context, {
timeout: this.timeout,
displayErrors: true
});
clearTimeout(timer);
resolve(result);
} catch (error) {
clearTimeout(timer);
reject(error);
}
});
}
}
// API白名单配置
const APIWhitelist = {
// 允许的数据库操作
db: {
tables: ['customer', 'order', 'product', 'inventory', 'finance'],
operations: ['select', 'insert', 'update', 'delete']
},
// 允许的HTTP地址
http: {
allowedDomains: ['api.company.com', 'internal.company.com'],
blockedPaths: ['/admin', '/system', '/config']
},
// 允许的通知类型
notify: {
types: ['info', 'success', 'warning', 'error']
}
};
数据隔离策略
多租户环境下的数据隔离机制:
// 数据隔离管理器
class DataIsolation {
constructor(mode = 'schema') {
this.mode = mode; // schema | row | column
}
// 租户上下文
getTenantContext(request) {
return {
tenantId: request.headers['x-tenant-id'],
userId: request.user.id,
roles: request.user.roles
};
}
// 应用租户过滤
applyTenantFilter(query, tenantId) {
switch (this.mode) {
case 'schema':
// 租户schema隔离
return this.applySchemaIsolation(query, tenantId);
case 'row':
// 行级隔离
return this.applyRowIsolation(query, tenantId);
case 'column':
// 列级隔离
return this.applyColumnFilter(query, tenantId);
default:
throw new Error(`Unknown isolation mode: ${this.mode}`);
}
}
// Schema级别隔离
applySchemaIsolation(query, tenantId) {
// 切换到租户的schema
query.schema = `tenant_${tenantId}`;
return query;
}
// 行级隔离
applyRowIsolation(query, tenantId) {
// 自动添加租户ID条件
const tenantField = query.model === 'user' ? 'company_id' : 'tenant_id';
query.where = query.where
? `(${query.where}) AND ${tenantField} = '${tenantId}'`
: `${tenantField} = '${tenantId}'`;
return query;
}
// 列级数据脱敏
applyColumnFilter(query, tenantId, userRoles) {
const sensitiveFields = this.getSensitiveFields(query.model);
// 获取用户可见字段
const visibleFields = this.getVisibleFields(userRoles, query.model);
// 过滤敏感字段
query.fields = query.fields.map(field => ({
name: field.name,
visible: visibleFields.includes(field.name) && !sensitiveFields.includes(field.name),
masked: visibleFields.includes(field.name) && sensitiveFields.includes(field.name)
}));
return query;
}
// 获取敏感字段
getSensitiveFields(model) {
const sensitiveMap = {
user: ['password', 'salt', 'id_card', 'bank_account', 'phone'],
order: ['customer_phone', 'customer_address', 'payment_info'],
finance: ['account', 'balance', 'transaction_detail']
};
return sensitiveMap[model] || [];
}
// 跨租户数据查询保护
validateCrossTenantAccess(requester, targetTenantId) {
// 管理员可以跨租户访问
if (requester.roles.includes('admin')) {
return true;
}
// 租户间数据共享配置
return this.checkTenantShare(requester.tenantId, targetTenantId);
}
}
// 租户数据共享配置
const TenantSharing = {
// 允许跨租户查询的角色
crossTenantRoles: ['platform_admin', 'super_admin'],
// 租户间共享配置
sharingRules: [
{ from: 'tenant_A', to: 'tenant_B', allowed: true, scope: ['order'] },
{ from: 'tenant_C', to: 'tenant_D', allowed: false }
]
};
操作审计与监控
完整的安全审计体系:
// 安全审计日志
class SecurityAudit {
constructor() {
this.logger = new AuditLogger();
}
// 记录操作审计
async logOperation(context, operation, details) {
const auditRecord = {
timestamp: new Date().toISOString(),
userId: context.user.id,
tenantId: context.tenantId,
operation: operation.type,
resource: operation.resource,
action: operation.action,
details: details,
ip: context.ip,
userAgent: context.userAgent,
result: operation.success ? 'success' : 'failure',
// 风险评分
riskScore: this.calculateRiskScore(context, operation)
};
await this.logger.save(auditRecord);
// 高风险操作实时告警
if (auditRecord.riskScore > 80) {
await this.sendAlert(auditRecord);
}
}
// 计算风险评分
calculateRiskScore(context, operation) {
let score = 0;
// 异常时间访问
if (this.isAbnormalTime(context.timestamp)) {
score += 30;
}
// 敏感操作
const sensitiveOps = ['delete', 'export', 'admin', 'config'];
if (sensitiveOps.some(op => operation.action.includes(op))) {
score += 20;
}
// 异常 IP
if (this.isSuspiciousIP(context.ip)) {
score += 40;
}
// 大量数据操作
if (operation.dataSize > 1000) {
score += 10;
}
return Math.min(100, score);
}
// 异常行为检测
async detectAnomalies(userId, timeWindow = 3600000) {
const recentOps = await this.logger.query({
userId: userId,
since: Date.now() - timeWindow
});
const anomalies = [];
// 检测频繁失败
const failedCount = recentOps.filter(op => op.result === 'failure').length;
if (failedCount > 10) {
anomalies.push({
type: 'brute_force',
severity: 'high',
detail: `${failedCount} failed attempts in ${timeWindow / 1000}s`
});
}
// 检测异常时间操作
const nightOps = recentOps.filter(op => this.isNightTime(op.timestamp));
if (nightOps.length > 20) {
anomalies.push({
type: 'unusual_time',
severity: 'medium',
detail: `${nightOps.length} operations during night`
});
}
// 检测数据批量导出
const exports = recentOps.filter(op => op.operation === 'export');
if (exports.length > 5) {
anomalies.push({
type: 'data_exfiltration',
severity: 'high',
detail: `${exports.length} batch exports detected`
});
}
return anomalies;
}
// 安全报表生成
async generateSecurityReport(tenantId, period) {
const stats = {
totalOperations: await this.countOperations(tenantId, period),
failedOperations: await this.countOperations(tenantId, period, { result: 'failure' }),
sensitiveOperations: await this.countSensitiveOperations(tenantId, period),
userActivity: await this.getUserActivityStats(tenantId, period),
topRiskyUsers: await this.getTopRiskyUsers(tenantId, period),
anomalyAlerts: await this.getAnomalyCount(tenantId, period)
};
return stats;
}
}
最佳实践建议
- 默认安全:默认启用所有安全策略,减少配置错误
- 最小权限:遵循最小权限原则,限制过度授权
- 纵深防御:多层安全防护,单点突破不影响整体
- 持续监控:实时监控异常行为,快速响应安全事件
- 定期审计:定期审查权限配置和安全策略有效性
总结
低代码平台安全性设计需要全面考虑:
- 身份认证:多因素认证确保用户身份可信
- 权限控制:细粒度权限实现精准访问控制
- 沙箱隔离:安全执行环境防止恶意代码
- 数据隔离:多租户数据严格隔离保障数据安全
- 审计追溯:完整日志记录支持安全审计和溯源
安全设计是低代码平台的核心竞争力,需要持续投入和优化。