ddl触发器默认禁用,须显式执行enable trigger ... on database启用;提取对象名必须用eventdata()解析xml,xpath路径需精确;raiserror须≥16级并配rollback才能硬拦截;白名单应结合app_name()、context_info()等多维度校验;truncate table无法被ddl触发器捕获,需依赖权限控制或sql server audit。

DDL触发器必须显式启用才生效
新建的 CREATE TRIGGER ... ON DATABASE 默认是禁用状态,哪怕语法完全正确,它对 DROP TABLE 也毫无反应。线上误删事故里,超六成源于“建完就忘启用”。
- 检查是否启用:
SELECT is_disabled FROM sys.triggers WHERE name = 'tr_block_drop'—— 返回1表示禁用 - 启用命令必须带作用域:
ENABLE TRIGGER tr_block_drop ON DATABASE(当前库)或ENABLE TRIGGER tr_block_drop ON ALL SERVER(全局,慎用) - 服务器级触发器(
ON ALL SERVER)对用户数据库里的DROP TABLE无效,它只响应CREATE LOGIN、ALTER DATABASE这类跨库事件
提取对象名只能靠 EVENTDATA(),不能用 OBJECT_NAME()
OBJECT_NAME() 在 DDL 触发器中不可靠——表可能已被删或尚未创建,函数直接返回 NULL。真正可用的是 EVENTDATA() 返回的 XML,但节点路径错一个字符,整个判断就失效。
- 正确提取表名:
CAST(EVENTDATA() AS XML).value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname') - 提取 Schema 名:
CAST(EVENTDATA() AS XML).value('(/EVENT_INSTANCE/SchemaName)[1]', 'sysname') - 必须用
TRY...CATCH包裹解析逻辑,否则新版 SQL Server 微调 XML 结构(比如字段名大小写变化)会导致触发器报错中断事务,反而阻断合法运维 - 别写成
/ObjectNamee或漏掉[1],XPath 错一个字符,整个判断就跳过
RAISERROR 必须 ≥16 级 + ROLLBACK 才能硬拦截
级别低于 16 的 RAISERROR(比如 RAISERROR('禁止删除', 10, 1))只是警告,事务继续执行,DROP TABLE 仍会成功。
- 必须用
RAISERROR('禁止删除表', 16, 1)或更高(16–25),才能让 SQL Server 中断当前批处理并回滚 - 紧跟
RAISERROR后必须加ROLLBACK,不能依赖错误自动回滚;没ROLLBACK,事务可能残留未提交状态 - 白名单不能只比对登录名,比如硬编码
ORIGINAL_LOGIN() IN ('sa', 'dba_admin')是常见漏洞:所有归档脚本都用同一个账号运行,那这个账号删任何表都畅通无阻 - 应结合
APP_NAME()、HOST_NAME()或自定义上下文(SET CONTEXT_INFO)做联合判断,例如应用清理前先设标识:SET CONTEXT_INFO 0x5472756E636174654A6F62,触发器中用CONTEXT_INFO()提取校验
TRUNCATE TABLE 无法被 DDL 触发器捕获
FOR DROP_TABLE 可拦 DROP TABLE users,但拦不住 TRUNCATE TABLE users;FOR ALTER_TABLE 可拦 ALTER TABLE,也拦不住 TRUNCATE。
- 想审计
TRUNCATE,只能靠 SQL Server Audit 的SCHEMA_OBJECT_ACCESS_GROUP或 Extended Events 捕获sql_batch_completed事件并过滤文本 - 临时表放行不能靠
LIKE '%temp%',攻击者可建user_temp_drop_me绕过;应严格匹配系统命名规范(如以#或##开头) - 如果真要防
TRUNCATE,得用DENY ALTER ON OBJECT::dbo.orders TO dev_user(因为TRUNCATE需要ALTER权限),但注意:用户若是db_owner成员,DENY会被无视
DROP 和 TRUNCATE 的难点不在语法,而在上下文识别和权限继承链的穿透——比如 db_owner 身份绕过 DENY,或 CONTEXT_INFO 在连接池复用时被污染。这些细节不写进触发器逻辑里,再漂亮的代码也形同虚设。











