不能靠“禁止DROP”权限来防误删,Oracle没有该权限项;必须用对象级保护+会话级拦截+操作审计三重兜底:锁目标Schema账户、建DDL触发器拦截、限制高权限账号连接范围,并严控DIRECTORY与Data Pump权限。

直接结论:不能靠“禁止DROP”权限来防误删,Oracle没有该权限项;必须用对象级保护 + 会话级拦截 + 操作审计三重兜底。
为什么REVOKE DROP ANY TABLE不起作用
很多人第一反应是回收 DROP ANY TABLE 权限,但这治标不治本——高权限账号(如应用账号被授予 DBA 或 RESOURCE 角色)本身就不依赖该系统权限来删表。只要它对目标Schema下的对象有 OWNER 身份或被显式授予 ALTER/DROP 对象权限,DROP TABLE schema_name.table_name 就能成功执行。更关键的是:DROP USER ... CASCADE 根本不检查任何对象级权限,只认用户身份和 CREATE USER 系统权限。
真正有效的三道防线
防线一:禁用目标Schema的登录能力,切断直接操作入口
- 执行 ALTER USER target_schema ACCOUNT LOCK;
- 确保该用户无 CREATE SESSION 权限(查 DBA_SYS_PRIVS 和 DBA_ROLE_PRIVS)
- 即使账号密码泄露或被硬编码在代码里,也无法以该Schema身份连库执行DDL
防线二:用细粒度审计+触发器拦截高危语句
- 启用统一审计(Unified Auditing),捕获所有 DROP 类语句:AUDIT DROP ANY TABLE BY app_user;
- 更进一步,在数据库层加 BEFORE DDL 触发器(需SYS权限):
CREATE OR REPLACE TRIGGER prevent_drop_target_schema
BEFORE DROP ON DATABASE
DECLARE
l_obj_owner VARCHAR2(128);
BEGIN
SELECT ora_dict_obj_owner INTO l_obj_owner FROM dual;
IF l_obj_owner = 'TARGET_SCHEMA' THEN
RAISE_APPLICATION_ERROR(-20001, 'Dropping objects in TARGET_SCHEMA is prohibited');
END IF;
END;
- 注意:该触发器对 DROP USER ... CASCADE 无效,需配合防线三
防线三:限制高权限账号只能连接指定Schema,从源头隔离
- 在 sqlnet.ora 中配置 SQLNET.ALLOWED_LOGON_VERSION_SERVER=12a(防旧协议绕过)
- 使用 ALTER USER app_user SET CONTAINER_DATA = (CDB$ROOT) CONTAINER = CURRENT;(12c+多租户场景)
- 更实用的是:在应用连接字符串中强制指定 CURRENT_SCHEMA=target_schema,并确保该账号在创建时未被授予对其他Schema的 ALTER ANY TABLE 或 SELECT ANY TABLE
最容易被忽略的漏洞点:DIRECTORY对象与Data Pump权限
很多团队忘了,expdp 和 impdp 不走常规权限模型——只要账号对目录对象(如 DATA_PUMP_DIR)有 READ/WRITE,就能通过 SCHEMAS=target_schema 参数导出/导入整个Schema,等效于逻辑删除。所以必须:
- 检查 DBA_DIRECTORIES 中是否暴露了指向敏感Schema的目录
- 回收非必要账号对 EXP_FULL_DATABASE 和 IMP_FULL_DATABASE 角色的授予
- 若必须保留Data Pump权限,改用 REMAP_SCHEMA 配合最小化导出范围(如仅导出特定表)











