WITH ENCRYPTION 是代码混淆而非加密,仅隐藏sp_helptext输出,无法阻止有权限者通过DAC、缓存或备份还原获取明文;必须在CREATE和ALTER时显式声明,否则报错或退为明文;真正安全靠权限控制、逻辑下推与行级安全。

WITH ENCRYPTION 不是加密,是混淆;它让 sp_helptext 返回空,但不阻止有权限的人还原逻辑。真正需要防的不是“看一眼”,而是“改一行就上线”或“顺手导出整段逻辑”。下面说清楚怎么做、为什么这么干、以及哪些坑一踩就翻车。
CREATE PROCEDURE 必须带 WITH ENCRYPTION,ALTER 时也必须带
一旦用 WITH ENCRYPTION 创建了存储过程,后续所有 ALTER PROCEDURE 都得显式带上这个子句。否则 SQL Server 直接报错:Cannot alter encrypted object without source text。
常见错误现象:开发在 SSMS 里右键“修改”,改完直接点执行——忘了补 WITH ENCRYPTION,结果整个过程被删掉重建成明文,等于白加。
- 新建时写全:
CREATE PROCEDURE dbo.CalcBonus WITH ENCRYPTION AS ... - 修改时不能只写
ALTER PROCEDURE dbo.CalcBonus AS ...,必须是ALTER PROCEDURE dbo.CalcBonus WITH ENCRYPTION AS ... - 如果已经建了明文版,想补加密?不行。只能
DROP再CREATE,且必须确保中间没业务调用
加密后 definition 字段为空,但内存和 DAC 连接仍可提取
sys.sql_modules.definition 字段会变成 NULL,sp_helptext 返回空行,不是报错。很多人误以为“没建成功”,其实是建成了,只是看不见。
但它挡不住以下路径:
- 通过 DAC(专用管理员连接)连上去,查
sys.dm_exec_cached_plans+sys.dm_exec_sql_text,能拿到首次执行时解密进内存的原始文本 - 从旧备份里还原数据库,在未加密状态下重建对象
- SQL Server 启动后首次执行该过程时,计划缓存里短暂存在明文(需配合调试工具抓取)
所以别指望靠它防 DBA 或运维人员——他们本就有权限,也本该有权限。它的实际作用,只是防止开发连不上生产库时,随手敲个 sp_helptext 就看到工资计算公式。
加密前必须确保所有引用对象都存在且可解析
SQL Server 在加密过程中会对语句做静态解析。哪怕你只是写了 SELECT * FROM nonexistent_table,哪怕它藏在 IF 0 = 1 BEGIN ... END 里,也会失败。
典型报错:Invalid object name 'nonexistent_table',发生在 CREATE PROCEDURE ... WITH ENCRYPTION 阶段,而不是运行时。
- 检查所有
SELECT/UPDATE/INSERT涉及的表、视图、函数名是否真实存在 - 避免使用延迟名称解析(deferred name resolution)逻辑,比如先建 SP 再建表
-
SET NOCOUNT ON后面紧跟不可解析语句,照样报错——它不看执行路径,只看语法树
比加密更有效的三件事:权限控制、逻辑下推、行级安全
真正管用的安全手段,往往不在“藏代码”,而在“控访问”和“减暴露”。
- 收回
VIEW DEFINITION权限:REVOKE VIEW DEFINITION ON dbo.GetOrderSummary FROM [dev_user]。比加密更直接,且对管理员也生效(只要没被显式授予权限) - 把复杂计算逻辑(如多层折扣、积分换算)移到应用层,SQL 层只留参数化
SELECT/INSERT/UPDATE。这样既降低 DB 负载,又让核心规则脱离 DB 管控范围 - 若必须留在 DB,优先用
CREATE SECURITY POLICY+ 行级安全(RLS),控制谁能看到哪些订单,而不是试图隐藏“怎么算金额”的逻辑
最常被忽略的一点:加密后的存储过程依然能被 EXECUTE,也能被 GRANT EXECUTE 给任意账号。如果你没关好执行权限,那代码再“加密”,别人照样能跑出结果、反推出逻辑。










