Access本身不支持参数化查询的完整语义,缺乏服务端预编译能力,DoCmd.RunSQL等直接受控于字符串拼接,手动转义仍无法防御注释符或Unicode绕过,故迁移至SQL Server并重构调用逻辑是唯一可行路径。

Access 本身不支持参数化查询的完整语义(尤其在 DAO / ADODB 的旧式 DoCmd.RunSQL 或拼接 CurrentDb.Execute 场景下),所谓“修复 SQL 注入”在 Access 层几乎无解——迁移不是可选项,而是唯一可行路径。加固必须发生在 SQL Server 层,且需同步重构 Access 前端调用逻辑。
为什么不能只修 Access 层的 SQL 注入
Access 的查询执行机制决定了它缺乏服务端预编译能力:DoCmd.RunSQL、CurrentDb.Execute、窗体/报表的 RecordSource 属性直接受控于字符串拼接;VBA 中用 "" 拼接用户输入进 SQL 是常态,而 Access 没有类似 SQL Server 的 sp_executesql + 参数占位符的可靠替代方案。即使手动转义单引号(Replace(input, "'", "''")),仍无法防御基于注释符(--)、分号(;)或 Unicode 变体的绕过。
迁移前必须清理的 Access 代码模式
在启动迁移前,先扫描所有 VBA 模块、窗体属性和宏,重点替换以下高危写法:
-
DoCmd.RunSQL "INSERT INTO Users VALUES ('" & Me.txtName & "')"→ 改为使用DAO.Recordset或准备迁移到 ADO 的参数化方式 -
Me.RecordSource = "SELECT * FROM Orders WHERE CustomerID = " & Me.cboCustomer→ 改为绑定到参数化查询(如新建查询qryOrdersByCustomer,设置WHERE CustomerID = [TempVars]![CustomerID],再用TempVars.Add赋值) - 所有直接调用
Application.CurrentProject.Connection.Execute的地方,必须重写为 ADOCommand对象 +Parameters.Append - 禁用任何含
Eval()、CallByName()或动态构建模块名/函数名的逻辑——这些在 SQL Server 后端无法等价映射
SQL Server 迁移中必须启用的三项加固配置
仅把表搬过去不等于安全。以下三处若遗漏,前端仍可能触发注入(尤其通过链接表或 passthrough 查询):
将 WCAG 2 AA 无障碍标准应用于设计系统、UI 组件和网页——通过可运行的验证器进行对比度计算,采用设计 token 模式使颜色天生可访问,设定排版和焦点状态规则,并提供语义检查清单。适用于构建或审查 UI。
- 在 SQL Server 中,对所有业务用户账号禁用
db_owner权限,仅授予db_datareader+db_datawriter+ 显式EXECUTE权限(针对存储过程)。避免使用sa或 Windows 管理员账号连接 Access 前端 - 将所有原 Access 查询逻辑,重构成带强类型参数的存储过程(例如
usp_GetOrdersByStatus @Status NVARCHAR(20)),并在 Access 中用 ADOCommand调用——这是唯一能确保参数不被解释为 SQL 文本的方式 - 在 SQL Server 实例级别启用
contained database authentication并关闭Ad Hoc Distributed Queries(sp_configure 'show advanced options', 1; RECONFIGURE; sp_configure 'Ad Hoc Distributed Queries', 0; RECONFIGURE),防止通过OPENROWSET或链接服务器间接执行任意语句
Access 前端链接 SQL Server 的安全陷阱
很多团队以为换成 ODBC DSN 就万事大吉,但实际极易掉坑:
- DSN 配置中若勾选了
Use ANSI quoted identifiers或Use regional settings when displaying numbers/dates,可能导致查询解析异常,诱使开发者退回到字符串拼接 - Access 链接表默认使用
SQL Server Native Client(已弃用),应改用ODBC Driver 17 for SQL Server或更新版本,并在连接字符串中显式添加Encrypt=yes;TrustServerCertificate=no; - 禁止在链接表的
Properties → Connection String中硬编码密码;改用 Windows 身份验证,或使用 SQL Server 密码策略 + 定期轮换,并将连接信息存入加密的 Access 表(非明文) - 所有
Pass-through 查询必须走存储过程,且其Connect属性里不能包含用户可控字段——这类查询绕过 Access 的查询引擎,直通 SQL Server,是注入高发区
真正难的不是把数据导进去,而是让 Access 前端彻底放弃“拼 SQL”这个肌肉记忆。哪怕只保留一个未参数化的 DoCmd.OpenForm 带条件字符串,整个加固就形同虚设。










