企业信息化管理系统

EIMS - 助力企业数字化转型

企业信息化系统低代码平台安全性设计

引言

低代码平台通过可视化配置和快速开发能力,大幅提升企业应用交付效率。然而,由于平台开放了配置能力和脚本执行环境,安全性设计变得尤为重要。本文将探讨低代码平台的安全架构设计。

安全架构总览

低代码平台的安全体系分层设计:

安全层次 防护措施 关键技术
身份认证 多因素认证、单点登录 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;
  }
}

最佳实践建议

总结

低代码平台安全性设计需要全面考虑:

安全设计是低代码平台的核心竞争力,需要持续投入和优化。

← 下一篇:企业信息化系统数据中台架构设计与实现