数据库同义词(Synonym)权限继承是一个极容易被忽视的安全隐患。简单来说,当你在数据库中创建一个同义词指向某个表、视图或其他对象时,如果该同义词的权限设置不当,任何能访问该同义词的用户都可能间接获得底层对象的操作权限,从而实现越权访问。核心解决思路就是:严格控制同义词的创建权限、显式授予而非依赖继承、定期审计同义词与底层对象的权限映射关系、启用细粒度的访问控制策略。下面我会从原理、风险场景、具体防护手段到最佳实践,把这个问题彻底讲透。

什么是数据库同义词以及权限继承机制

数据库同义词本质上是一个别名(Alias),它指向数据库中的另一个对象,比如表、视图、存储过程、函数甚至另一个数据库中的对象。创建同义词的初衷是简化访问路径、解耦应用程序与底层对象的绑定关系。比如你有一张表叫 T_ORDER_2024,你可以创建一个同义词叫 ORDERS 指向它,应用程序只需要引用 ORDERS 就行,不用关心表名是否会变。

问题出在权限上。在大多数主流数据库(如 Oracle、SQL Server、PostgreSQL、MySQL)中,同义词创建后,其权限行为取决于具体实现。以 Oracle 为例,如果你创建了一个 PUBLIC 同义词并且底层对象有 GRANT 权限,那么所有用户都可以通过这个同义词访问底层对象,完全绕过了对底层对象本身的权限检查。SQL Server 中,同义词默认不继承任何权限,但如果你用 EXECUTE AS 或配合 SCHEMA 绑定,同样可能产生权限放大效应。PostgreSQL 的规则类似,同义词本身没有独立权限,但如果底层对象是公开可访问的,同义词就成了一条便捷通道。

越权风险的三大典型场景

场景一:PUBLIC 同义词导致全员越权。很多DBA为了方便,直接创建 PUBLIC 同义词指向核心业务表。比如在 Oracle 中执行 CREATE PUBLIC SYNONYM emp FOR hr.employees; 如果 hr.employees 表已经对 PUBLIC 有 SELECT 权限,那么任何数据库用户都能通过 emp 这个同义词查到员工信息,包括薪资、身份证号等敏感字段。这在生产环境中是极其危险的。

场景二:跨 Schema 同义词权限穿透。假设 Schema A 下有一张敏感表,Schema B 的用户通过同义词访问了这张表。如果同义词创建时使用了 CURRENT_USER 或其他权限上下文,Schema B 的用户可能以 Schema A 的权限执行操作,而不是以自己的受限权限。这种情况在存储过程、函数调用链中尤为突出。

场景三:同义词链式继承。A 同义词指向 B,B 同义词指向 C 表。如果每一层都没有做权限隔离,最终用户通过 A 就能直接操作 C,中间的权限检查全部被跳过。这种链式结构在大型项目中非常常见,也是审计时最容易遗漏的盲区。

具体防护措施和操作方法

第一,严格限制同义词的创建权限。不要把 CREATE SYNONYM 权限随意授予普通用户。在 Oracle 中,应该只授予特定的 DBA 角色或应用专用角色。在 SQL Server 中,需要 CONTROL 或 ALTER 权限才能创建同义词,同样要收紧。

-- Oracle: 仅授予特定角色创建同义词权限
GRANT CREATE SYNONYM TO app_admin_role;
GRANT CREATE PUBLIC SYNONYM TO dba_role;

-- 收回普通用户的同义词创建权限
REVOKE CREATE SYNONYM FROM public_user_role;

第二,避免使用 PUBLIC 同义词指向敏感对象。如果确实需要 PUBLIC 同义词,必须确保底层对象本身没有对 PUBLIC 的任何权限授予。宁可让应用程序通过角色统一管理访问,也不要图省事开 PUBLIC。

-- 检查 Oracle 中哪些表对 PUBLIC 有权限
SELECT * FROM DBA_TAB_PRIVS 
WHERE GRANTEE = 'PUBLIC' 
AND TABLE_NAME IN (
    SELECT TABLE_NAME FROM DBA_SYNONYMS 
    WHERE SYNONYM_NAME IN ('emp', 'orders', 'salary')
);

第三,显式授权而非依赖隐式继承。创建同义词后,不要假设底层对象的权限会自动通过同义词传递。应该针对同义词本身单独做权限控制,或者通过视图+同义词的组合来实现权限隔离。比如先创建一个只包含非敏感列的视图,再用同义词指向这个视图,而不是直接指向原始表。

-- 创建安全视图,再用同义词指向视图
CREATE VIEW v_emp_public AS
SELECT employee_id, first_name, last_name, department_id
FROM hr.employees;

GRANT SELECT ON v_emp_public TO app_read_role;

CREATE SYNONYM emp FOR v_emp_public;

第四,启用细粒度访问控制(FGAC/RLS)。Oracle 的 Virtual Private Database(VPD)、SQL Server 的 Row-Level Security、PostgreSQL 的 Row Security Policy,这些技术可以在同义词访问时强制附加过滤条件,即使通过同义词也只能看到被授权的数据行。

-- Oracle VPD 示例:限制通过同义词只能看到本部门数据
BEGIN
  DBMS_RLS.ADD_POLICY(
    object_schema   => 'HR',
    object_name     => 'EMPLOYEES',
    policy_name     => 'emp_dept_policy',
    function_schema => 'HR_SECURITY',
    policy_function => 'fn_emp_dept_filter',
    statement_types => 'SELECT'
  );
END;
/

第五,定期审计同义词与权限映射。建立自动化审计脚本,定期扫描数据库中所有同义词,检查其指向的底层对象是否存在权限过度开放的情况。重点关注:同义词指向的对象是否对 PUBLIC 有权限、同义词是否被非授权用户创建、同义词链是否超过两层。

-- SQL Server: 审计同义词及其底层对象权限
SELECT 
    s.name AS synonym_name,
    s.base_object_name,
    dp.permission_name,
    dp.state_desc
FROM sys.synonyms s
JOIN sys.database_permissions dp 
    ON OBJECT_ID(s.base_object_name) = dp.major_id
WHERE dp.grantee_principal_id = 0;  -- PUBLIC

不同数据库平台的差异化处理

Oracle 平台:Oracle 的同义词权限模型相对复杂,PUBLIC 同义词是最大风险点。建议使用基于 Schema 的同义词而非 PUBLIC,同时配合 Oracle Database Vault 或 VPD 做纵深防御。另外,Oracle 12c 以后引入了 Invoker's Rights 和 Definer's Rights 的区分,在存储过程中调用同义词时要特别注意权限上下文。

SQL Server 平台:SQL Server 的同义词默认不传递权限,但如果同义词指向的对象在同一 Schema 下且该 Schema 有默认权限,仍可能出现问题。建议使用 SCHEMABINDING 绑定同义词到特定 Schema,并配合 DENY 语句做显式拒绝。

-- SQL Server: 使用 SCHEMABINDING 限制同义词范围
CREATE SYNONYM dbo.emp FOR HR.dbo.v_emp_public;
-- 确保底层视图有 SCHEMABINDING
CREATE VIEW HR.dbo.v_emp_public
WITH SCHEMABINDING
AS
SELECT employee_id, first_name FROM HR.dbo.employees;

PostgreSQL 平台:PostgreSQL 没有传统意义上的同义词,但可以通过创建 Schema 或使用视图模拟。如果使用了 dblink 或 foreign data wrapper,远程表的访问同样需要注意权限传递问题。建议在 foreign server 的 user mapping 中使用受限账户。

MySQL 平台:MySQL 的同义词功能较弱(8.0 才正式支持),但视图+权限的组合同样存在类似风险。MySQL 的核心防护在于严格管理 GRANT 语句,不要对核心表做 GRANT ALL ON *.* 的操作。

权限继承的底层逻辑与防御思维

从根本上理解这个问题,你需要明白一个原则:数据库的权限检查是在对象层级进行的,而同义词只是一个指针。当用户通过同义词访问时,数据库引擎会解析同义词指向的真实对象,然后对真实对象做权限检查。如果真实对象的权限已经对某个角色或 PUBLIC 开放,那么通过同义词访问和直接访问在权限层面没有区别——这就是越权的根源。

防御思维应该是"最小权限+纵深防御"。最小权限意味着每个同义词指向的对象本身就应该是权限最小化的(比如视图只暴露必要列),纵深防御意味着即使某一层被突破,还有 VPD、审计、告警等机制兜底。不要把安全寄托在"同义词不会被发现"这种侥幸心理上,自动化扫描工具几秒钟就能找出所有同义词。

企业级落地建议

对于中大型企业,建议将同义词管理纳入数据库变更管理流程。任何同义词的创建、修改、删除都需要经过审批,并且在变更工单中明确标注底层对象和权限范围。同时,在数据库安全基线中加入同义词检查项,作为上线前的必检内容。

另外,建议建立权限矩阵文档,记录每个同义词对应的业务用途、指向对象、授权角色、审计负责人。这个文档不需要很复杂,但必须有人维护、有人负责。很多安全事故不是技术问题,而是管理缺位。

最后提醒一点:不要以为只有 DBA 才需要关注这个问题。应用开发人员在设计数据库访问层时,如果大量使用同义词来解耦,同样需要了解权限继承的风险。开发阶段就把安全考虑进去,远比上线后再打补丁成本低得多。

总结一下,数据库同义词权限继承的核心风险在于:同义词作为别名绕过了直接的权限检查路径,如果底层对象权限过宽,就等于开了一扇后门。解决方案不复杂但需要执行到位——限制创建权限、避免 PUBLIC 同义词、用视图做隔离、启用行级安全、定期审计。把这些做到位,同义词就只是一个便利工具,而不是安全漏洞。