审计、SQL 注入防护与密钥管理

共 20 题
📑 题目列表 20 题
#
★★★

1. 参数化查询(PreparedStatement)的应用?

请说明参数化查询(PreparedStatement)的应用与原理?

  • 参数化查询的机制
  • 与 SQL 注入的对抗
  • 编译与缓存

参数化查询(PreparedStatement)将 SQL 的"结构"与"参数"分离:SQL 语句在编译时被预编译为固定结构,参数通过占位符(?)传入,由数据库驱动将参数作为数据绑定,而不是拼接到 SQL 字符串中。这样用户输入永远不会被当作 SQL 语法执行,因此即使输入含单引号、注释符或关键字,也无法改变 SQL 结构,从根本上杜绝 SQL 注入。PreparedStatement 的另一优势是预编译语句可被数据库缓存复用,对重复执行的查询性能更好。应用场景包括所有用户输入参与的查询(按 ID、名称、条件搜索等),应统一使用参数化,禁止字符串拼接 SQL。参数化查询是 Web 应用与数据库安全的最基本、最有效的防线。

参数化查询解决的是"数据与代码"的混淆问题——把输入严格当作数据,从而让攻击者无法注入代码。它比 WAF 过滤更可靠,因为不依赖对攻击模式的识别,是防御 SQL 注入的第一原则。

String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, userId);   // 参数作为数据绑定,不参与 SQL 拼接
ResultSet rs = ps.executeQuery();
#
★★★

2. 审计的内容,登录、查询、DML、DDL?

请说明数据库审计的内容,包括登录、查询、DML、DDL 等?

  • 审计的四大类内容
  • 各类审计的意义
  • 审计的粒度与成本

数据库审计主要覆盖四类内容:登录审计(记录谁、何时、从何 IP 登录成功或失败,用于发现暴力破解与异常登录);查询审计(记录 SELECT 查询,尤其是敏感列访问,用于数据泄露溯源);DML 审计(记录 INSERT/UPDATE/DELETE,用于追踪数据变更责任);DDL 审计(记录 CREATE/ALTER/DROP 等结构变更,用于防范误操作与恶意篡改)。审计内容通常包括时间、用户、客户端 IP、SQL 语句、影响行数、对象等元数据。审计的意义在于追责、合规(如等保、PCI DSS、GDPR)与安全事件响应。但全量审计会带来显著性能与存储开销,因此需按风险分级采样,重点审计登录失败、DDL、敏感列访问等高危事件。

审计是"可追溯性"的体现,覆盖数据"从进到出、从读到写、从结构到登录"的全生命周期。审计粒度要与成本平衡,通常结合风险分级与采样策略,只对高风险操作做全量记录。

#
★★★

3. ORM 框架的 SQL 注入风险,Hibernate HQL、MyBatis ${}?

请说明 ORM 框架的 SQL 注入风险,特别是在 Hibernate HQL 与 MyBatis ${} 场景下的风险?

  • ORM 框架的注入入口
  • HQL 与参数绑定
  • MyBatis ${} 与 #{} 的区别

ORM 框架虽然封装了 SQL,但仍有注入风险。Hibernate 的 HQL 使用命名参数(:name)或 ? 占位符时是安全的,但若通过字符串拼接构造 HQL 或使用原生 SQL(createNativeQuery)拼接,攻击者可注入 HQL 或 SQL。MyBatis 提供了 #{}(预编译占位符,参数作为数据绑定,安全)与 ${}(字符串直接替换,拼进 SQL,危险)两种写法:${} 会把用户输入直接嵌入 SQL 文本,若用于 order by、表名、列名等结构位置,可能被注入;因此生产规范应禁用 ${} 拼接用户输入,动态结构若必须使用 ${} 需严格白名单校验。注入风险的根本在于"数据被当作 SQL 结构处理",ORM 的便利性不能替代开发者的参数化意识。

ORM 的注入风险不在 ORM 本身,而在开发者是否使用参数化机制。MyBatis 的 #{} 与 Hibernate 的命名参数都是安全的,风险集中在 ${}、字符串拼接 HQL 和原生 SQL 上。规范应强制参数化,并对动态结构做白名单。

<!-- 安全:使用 #{} 预编译占位符 -->
<select id="findById" resultType="User">SELECT * FROM user WHERE id = #{id}</select>
<!-- 危险:使用 ${} 直接拼接,可能被注入 -->
<select id="findByOrder" resultType="User">SELECT * FROM user ORDER BY ${orderBy}</select>
#
★★★

4. ORM 安全实践,参数化、HQL 注入?

请说明 ORM 安全实践,包括参数化与 HQL 注入防护?

  • ORM 的参数化实践
  • HQL 注入的防护
  • 动态结构的安全处理

ORM 安全实践的核心是"始终参数化、禁用拼接"。Hibernate 中应使用命名参数(:name)或占位符,避免字符串拼接 HQL;若必须用原生 SQL,用 PreparedStatement 参数化;对 HQL 注入的防护与 SQL 注入相同——把用户输入当数据绑定,同时对 HQL 中的实体名、集合名等结构位置做白名单校验。MyBatis 统一使用 #{} 参数化,禁用 ${} 拼接用户输入;对 order by、列名等动态结构,用白名单映射(如把用户选择的排序字段映射为允许的固定枚举值)。此外还应:为数据库账号设置最小权限、对错误信息脱敏(避免泄露 SQL 结构)、使用参数化连接池、定期审计。ORM 安全实践是把"参数化"这一原则落实到每个 ORM 用法中,并建立代码审查与安全扫描。

ORM 安全实践不是某个特性,而是"参数化 + 白名单 + 最小权限 + 审计"的组合。HQL 注入与 SQL 注入同源,防护核心仍是隔离数据与结构;动态结构用白名单替代拼接,是彻底避免注入的工程手段。

// Hibernate 命名参数(安全)
Query q = session.createQuery("from User u where u.name = :name");
q.setParameter("name", userName);
#
★★★

5. SQL 注入(SQL Injection)的攻击向量与防护?

请说明 SQL 注入(SQL Injection)的攻击向量与防护措施?

  • SQL 注入的攻击向量
  • 注入的检测与利用
  • 多层次的防护

SQL 注入是攻击者通过在输入中注入恶意 SQL 片段,使数据库执行非预期命令的攻击。攻击向量包括:输入框、URL 参数、Cookie、HTTP 头等所有用户可控且进入 SQL 的入口;攻击者通过单引号逃逸、注释(--)、UNION 联合查询、布尔盲注、时间盲注、堆叠查询(分号分隔多语句)等手法操纵 SQL 结构。防护上采用纵深防御:首要是参数化查询/预编译,从源头杜绝结构注入;其次是输入校验(类型、长度、白名单)与输出编码;数据库层面配置最小权限账号、禁用危险函数、关闭不必要的存储过程;应用层使用 WAF 作为辅助(但不可替代参数化);最后是通过审计与监控发现异常。防护的关键是"把所有输入当作数据,永远不当作 SQL 结构"。

SQL 注入的根因是"数据与代码未隔离"。攻击向量覆盖所有输入入口,防护则需从参数化这一根本手段出发,配合输入校验、最小权限、WAF 与审计构成多层防线。WAF 只能缓解,不能替代参数化。

#
★★★

6. SQL 注入的类型,Union-Based、Boolean-Based、Time-Based?

请说明 SQL 注入的三种类型:Union-Based、Boolean-Based、Time-Based?

  • Union-Based 注入的原理
  • Boolean-Based 盲注
  • Time-Based 盲注

Union-Based 注入利用 UNION 联合查询,将用户可控的查询结果与目标查询结果合并返回,从而在页面中直接显示注入数据,需要原查询的列数一致,攻击者通常先探测列数再构造 UNION SELECT 提取敏感数据。Boolean-Based 盲注在页面不直接返回数据时使用,通过构造"条件为真/假"的布尔表达式(如 AND 1=1 / AND 1=2),观察页面响应的差异来逐位推断数据,称为盲注。Time-Based 盲注在布尔差异不可见时使用,通过延时函数(如 MySQL 的 SLEEP、PostgreSQL 的 pg_sleep)构造条件,若条件成立则响应延迟,从而按延迟推断数据。三者是对同一注入点的不同利用方式,按"是否有回显、是否可观察差异"选择。防护手段仍是参数化查询,使这些手法无法改变 SQL 结构。

三种类型本质上是"回显类型"的差异:Union 有直接回显、Boolean 靠页面差异、Time 靠时间差异。它们都依赖注入点能改变 SQL 结构,因此参数化查询能一并消除。盲注通常出现在无法直接看到结果的场景,需自动化工具逐字符爆破。

#
★★★

7. 二阶 SQL 注入(Second-Order SQLi)的原理?

请说明二阶 SQL 注入(Second-Order SQLi)的原理?

  • 二阶注入的阶段与存储
  • 与一阶注入的区别
  • 防护的重点

二阶 SQL 注入(Second-Order SQLi)指恶意输入在第一次进入数据库时被存储(如存入用户资料、评论),本身不触发注入,但在后续某次查询中被拼接到 SQL 中执行,从而触发注入。其原理是"存储与触发分离":第一阶段攻击者输入特殊字符(如单引号、HTML 标签等特殊字符)被安全地存储进数据库;第二阶段,当应用读取该存储值并用于拼接 SQL(如拼接进动态查询、用户名查询、配置文件)时,注入才生效。与一阶注入(直接注入)不同,二阶注入隐蔽性强,因为存储阶段可能已做输入过滤,但读取阶段的拼接未做处理。防护重点是"任何数据读取后拼接 SQL 都需参数化",不能因为存储阶段已过滤就放松读取阶段的防注入处理;同时存储与读取的统一参数化规范是根治手段。

二阶注入的难点在于"攻击发生时输入早已存储",静态扫描难以发现,且绕过一次性的输入过滤。防护的核心是"读取阶段同样必须参数化",强调参数化是所有 SQL 组装位的统一原则,而不是只在用户输入入口做一次。

#
★★★

8. WAF(Web Application Firewall)的数据库角色?

请说明 WAF(Web Application Firewall)在数据库安全中的角色?

  • WAF 的作用与定位
  • WAF 对 SQL 注入的检测
  • WAF 的局限与辅助地位

WAF(Web Application Firewall)位于应用与客户端之间,通过检测和过滤 HTTP 请求中的恶意模式(如 SQL 注入特征、XSS、CSRF)来保护应用。在数据库安全中,WAF 的角色是"应用层防护的辅助层",它能在 SQL 到达数据库前拦截明显恶意请求,提供一层快速过滤与告警,缓解零日或未修复的注入漏洞,并作为纵深防御的一部分。但 WAF 有显著局限:它基于规则/特征匹配,可被编码绕过(如双编码、注释混淆、大小写变形)、存储过程与二阶注入绕过,且无法理解应用与数据库的语义,因此不能替代参数化查询。WAF 的数据库角色应定位为"缓解与告警的辅助防线",根因防御仍靠参数化、最小权限与审计。

WAF 的价值在于"快速、可部署的缓解层",但本质是黑名单式防护,存在绕过与误报。数据库安全不能依赖 WAF,而应把参数化查询作为根因,WAF 作为补充的检测与告警层。

#
★★★

9. 凭据轮换(Credential Rotation)的策略?

请说明凭据轮换(Credential Rotation)的策略?

  • 凭据轮换的目的
  • 轮换频率与自动化
  • 与密钥管理集成

凭据轮换(Credential Rotation)是定期更换数据库账号密码、API 密钥等凭据,以限制泄露凭据的可用窗口的安全实践。策略上,凭据轮换应自动化、定期执行(如每 30-90 天),避免人工手动更换导致的遗漏与停机;轮换时需保证平滑,协调应用连接池、配置中心与数据库账号,避免新旧凭据切换瞬间中断连接。现代实践常与密钥管理(Vault、AWS Secrets Manager)集成,通过动态凭据(按需生成短期密码、自动吊销)或自动轮换(定时更新密码并通知应用)实现"零停机轮换"。轮换频率的权衡是:过于频繁增加运维与故障风险,过低则泄露窗口大。还应结合应急轮换(凭据泄露时立即轮换)与审计日志(记录轮换事件)。

凭据轮换的核心是"限制凭据生命周期"与"自动化"。通过密钥管理平台实现动态凭据与自动轮换,可大幅降低人工成本与中断风险。轮换策略需平衡安全性与运维可行性,并保留应急轮换能力。

#
★★★

10. 审计日志如何防篡改(独立存储、WORM、哈希链)?为什么 DBA 能改业务数据却不能删审计记录?

请说明审计日志如何防篡改(独立存储、WORM、哈希链),并解释为什么 DBA 能改业务数据却不能删审计记录?

  • 审计日志防篡改的机制
  • 独立存储与 WORM
  • 哈希链与职责分离

审计日志防篡改依赖多种机制:独立存储(审计日志写在与数据库分离的独立系统或独立账号下,避免与业务数据同库被篡改);WORM(Write Once Read Many,只写一次只读多次,如合规存储、云对象存储的 WORM 锁,物理上禁止修改或删除);哈希链(每条日志记录包含前一条记录的哈希,形成链式结构,任何一条被篡改都会导致后续哈希失配,从而被检测)。为什么 DBA 能改业务数据却不能删审计记录:因为审计日志由独立权限体系和独立存储管理,DBA 的数据库权限不覆盖审计系统;同时哈希链与 WORM 使删除或篡改在物理与逻辑上都被阻止或被检测。这正是"职责分离"(SoD)的体现——审计者与数据库管理权限分离,保证审计的可信度与追责能力。

审计日志防篡改的本质是"审计者与管理者分离 + 存储不可变 + 篡改可检测"。独立存储打破 DBA 的权限范围,WORM 提供物理不可变,哈希链提供逻辑篡改检测,三者共同构成可信审计链,是安全合规与追责的基础。

#
★★

11. 应用的凭据注入(Credential Injection)模式?

请说明应用的凭据注入(Credential Injection)模式?

  • 凭据注入的概念
  • 凭据注入的常见模式
  • 安全实践

凭据注入(Credential Injection)指将数据库凭据以安全的方式注入到应用运行环境中的模式,核心是"凭据不硬编码在代码或配置中,而是运行时动态注入"。常见模式包括:环境变量注入(凭据放在环境变量中,由容器/编排平台注入,避免写入代码仓库);Secret 卷注入(K8s 中凭据作为 Secret 挂载为文件或环境变量);密钥管理平台注入(应用启动时从 Vault、AWS Secrets Manager 拉取凭据,支持动态凭据与轮换);配置中心注入(凭据存放在配置中心并由 IAM 权限保护)。凭据注入的价值是把敏感信息与代码、镜像解耦,避免版本库泄露、镜像泄露,并支持凭据集中管理与自动轮换。安全实践要求:凭据注入源需加密、配最小权限访问、记录审计,并在运行时避免明文落盘。

凭据注入是对"凭据存放位置"的治理——把凭据从代码/配置中移出,改为运行时从可信源注入。它结合环境变量、K8s Secret、密钥管理平台等手段,实现凭据的解耦、集中管理与动态轮换,是应用容器化与云原生部署的安全标准。

#
★★

12. 数据库凭据管理,Vault、AWS Secrets Manager、HashiCorp Vault?

请说明数据库凭据管理的工具:Vault、AWS Secrets Manager、HashiCorp Vault 及其应用?

  • 凭据管理工具的核心能力
  • 动态凭据与静态凭据
  • 与数据库的集成

数据库凭据管理将数据库账号密码集中存储在专用管理平台中,支持安全存储、自动轮换、权限控制与审计。HashiCorp Vault 是开源的密钥管理工具,支持静态凭据存储与动态数据库凭据(Vault 的 database secret engine 可动态生成短期凭据,如一次性密码,到期自动吊销,并按需授予最小权限),是安全与运维界的主流选择。AWS Secrets Manager 是云托管服务,支持密文存储、自动轮换、通过 IAM 权限控制访问,并可与 AWS 数据库(RDS)集成自动轮换。Vault 或 Secrets Manager 的价值在于:应用不直接持有长期静态密码,而是启动时拉取动态/短期凭据,减少凭据泄露面;凭据轮换、吊销、审计集中管理。选择时需权衡自建(Vault)与云托管(Secrets Manager)的运维成本、扩展性与合规。

凭据管理的核心是"动态化 + 集中化 + 自动化"。Vault 的动态凭据(数据库 secret engine)与云托管的自动轮换(Secrets Manager)都实现了"应用不持有长期密码",极大缩小了凭据泄露窗口。选择取决于环境与运维偏好。

#
★★

13. 连接串(Connection String)中的凭据保护?

请说明连接串(Connection String)中的凭据保护方法?

  • 连接串中的凭据泄露风险
  • 保护凭据的手段
  • 动态凭据与最小权限

连接串(Connection String)通常包含数据库地址、用户名和密码,若明文写在代码、配置文件或环境变量中,一旦代码仓库泄露、配置暴露或镜像被扒,凭据即泄露。保护方法包括:不硬编码密码在代码中,使用配置加密或密钥管理平台保存;从环境变量、Secret 或 Vault/Secrets Manager 动态注入凭据;为连接串使用最小权限账号(只授予应用所需权限),避免使用超级账号;对含密码的配置做权限控制与加密存储;使用连接池并避免在日志中输出连接串。更高级的做法是使用动态/短期凭据(如 Vault 动态数据库凭据),减少长期静态密码的暴露面。保护的根本是"把凭据从代码与配置中移出,并控制其访问权限"。

连接串凭据保护的核心是"不落地、不扩散、最小权限"。通过动态注入、加密存储、最小权限账号与日志清理,把凭据暴露面降到最低。它属于凭据管理实践的一部分,与注入与轮换配合。

#
★★

14. GDPR 的被遗忘权(Right to Erasure)实现?

请说明 GDPR 的被遗忘权(Right to Erasure)在数据库中的实现?

  • 被遗忘权的含义
  • 数据删除与物理删除
  • 与备份、日志的冲突

GDPR 的被遗忘权(Right to Erasure)要求当数据主体请求删除其个人数据时,组织必须删除相关数据。数据库实现包括:逻辑删除(在数据表中标记删除状态,但需保证数据不再被业务使用)与物理删除(DELETE 从表结构中移除记录);加密驱逐(对数据用密钥加密后,删除密钥即可使数据不可读,即"crypto-shredding");数据匿名化(将个人标识替换为不可逆的匿名值,使其不可识别)。实现难点在于:备份包含旧数据、日志与审计记录包含历史数据、缓存与副本中残留数据,这些都需要同步处理或通过密钥驱逐实现覆盖。被遗忘权要求"数据不可恢复地不可识别",因此对备份、日志、缓存、副本的清理与密钥驱逐是关键。实现需在合规与数据保留(如审计、法律要求)之间权衡。

被遗忘权的难点在于"数据无处不在"——主表、备份、日志、缓存、副本。物理删除可能清不干净,密钥驱逐(crypto-shredding)通过对加密数据删除密钥实现"不可读",是处理散布数据的高效手段。实现需评估所有数据承载点。

#
★★

15. 数据库合规要求,GDPR、等保、PCI DSS、HIPAA?

请说明数据库合规要求:GDPR、等保、PCI DSS、HIPAA 各自的要求与影响?

  • 各合规标准的定位
  • 对数据库的具体要求
  • 合规的落地

各合规标准对数据库安全提出不同要求:GDPR(欧盟通用数据保护条例)关注个人数据保护,要求数据加密、被遗忘权、数据最小化、审计与数据泄露通知;等保(中国网络安全等级保护)分级要求身份鉴别、访问控制、审计、数据完整性备份、加密存储与传输,不同等级要求递进;PCI DSS(支付卡行业数据安全标准)针对持卡人数据,要求加密存储(强加密)、传输加密、最小权限、定期轮换密钥、审计日志与覆盖持卡人数据;HIPAA(美国健康保险携带和责任法案)针对受保护健康信息(PHI),要求加密、访问控制、审计、数据备份与最小权限。落地时需结合具体标准进行差距分析、落实加密与审计、配置最小权限、定期评估与整改。数据库通常需要同时满足多个标准,采用"加密、审计、最小权限、备份"等通用安全基线。

合规要求本质上是把"加密、审计、访问控制、最小权限、备份"等安全实践固化为强制要求。不同标准侧重点不同(数据保护、等级保护、支付数据、健康数据),落地的通用策略是建立统一的安全基线,再按各自标准补齐差异。

#
★★

16. pgAudit 扩展如何记录审计日志,其分类与权限配置如何做?

请说明 PostgreSQL 的 pgAudit 扩展如何记录审计日志,以及其分类与权限配置如何做?

  • pgAudit 的审计机制
  • 审计分类(read、write、function、ddl 等)
  • 权限配置与加载

pgAudit 是 PostgreSQL 的审计扩展,通过 hook 拦截并记录数据库操作到标准日志中,提供细粒度的审计。它支持分类审计:READ(SELECT)、WRITE(INSERT/UPDATE/DELETE)、FUNCTION(函数调用)、ROLE(角色/权限变更)、DDL(结构变更)、MISC(其他)等,还可按对象(表、schema)和会话(session)配置。配置在 postgresql.conf 中通过 shared_preload_libraries 加载 pgAudit,并用 pgaudit.log 设置审计分类(如 'read,write,ddl')、pgaudit.log_relation 记录对象等。权限配置上,pgAudit 通过创建 pgaudit 扩展并设置 GUC 参数控制全局审计,对象级审计可通过 pgaudit.log_relation 与 DDL 记录实现。审计日志输出到 PostgreSQL 标准日志,配合日志轮转存储。pgAudit 的授权需在配置中启用,且需注意全量审计的性能开销,通常按需分类开启。

pgAudit 的价值在于"原生、细粒度、基于分类"的审计。它把审计动作分类(read/write/ddl/function 等),通过共享库与 GUC 参数配置,落盘到 PostgreSQL 日志,便于合规审计。配置时需权衡分类粒度与性能开销。

# postgresql.conf
shared_preload_libraries = 'pgaudit'
pgaudit.log = 'read, write, ddl'
pgaudit.log_relation = on
CREATE EXTENSION pgaudit;
#
★★

17. WAF 为什么不能替代参数化查询?编码绕过、存储过程与二阶注入场景下 WAF 的盲区在哪里?

请说明 WAF 为什么不能替代参数化查询,并指出编码绕过、存储过程与二阶注入场景下 WAF 的盲区?

  • WAF 的规则检测局限
  • 编码绕过的手段
  • 存储过程与二阶注入的盲区

WAF 基于规则/特征匹配检测恶意请求,是黑名单式防护,无法理解 SQL 语义,因此不能替代参数化查询。其盲区主要体现在:编码绕过(攻击者用双 URL 编码、十六进制、Unicode、注释混淆、大小写/空白变换等绕过特征匹配);存储过程(恶意输入进入存储过程,由存储过程内部拼接或执行 SQL,WAF 只看到调用存储过程的调用,无法识别内部注入);二阶注入(恶意输入先被存储,后续另外的请求触发拼接,WAF 无法关联存储与触发两步,无法识别)。此外 WAF 有误报与漏报,且无法防护应用内已验证的合法输入在拼接时的风险。因此 WAF 只能作为辅助缓解层,根因防护必须依靠参数化查询,因为参数化在语义层面隔离了数据与结构,不依赖规则匹配。

WAF 的盲区来自"黑名单模式无法穷尽编码变体"与"无法理解应用与数据库的语义"。编码绕过、存储过程、二阶注入都让 WAF 的模式匹配失效,而参数化查询从语义上杜绝了结构注入,是唯一能根本防御的手段。

#
★★

18. 全量审计会带来显著性能开销,如何按风险分级采样(登录失败、DDL、敏感列访问)在合规与性能间平衡?

请说明全量审计会带来显著性能开销,如何按风险分级采样(登录失败、DDL、敏感列访问)在合规与性能间平衡?

  • 全量审计的性能成本
  • 风险分级与采样
  • 合规与性能的平衡

全量审计会显著增加数据库 CPU、IO 与存储开销,因为每条 SQL 都要被解析、记录、落盘。为在合规与性能间平衡,采用按风险分级采样策略:对高风险、必须全量记录的事件完整审计,如登录失败(用于发现暴力破解)、DDL(结构变更,频率低且关键)、敏感列访问(如身份证、银行卡列查询);对低风险、高频事件(如普通 SELECT)采用采样或忽略。具体包括:配置审计日志只记录特定对象/操作(如只审计敏感库表)、对低频高危操作全量 + 对高频低危操作采样、将审计日志异步写入独立存储避免阻塞业务、设置采样率与日志保留策略。这样既满足合规对关键事件的审计要求,又控制性能与存储开销。分级采样的关键在于"明确哪些操作必须全量、哪些可以采样",并定期评估调整。

审计分级采样的核心是"把有限的审计资源投入最该全量记录的事件"。登录失败、DDL、敏感列访问属于高危且低频,全量记录成本可控;高频普通查询则采样,兼顾合规与性能。异步落盘与独立存储也缓解了写入开销。

#

19. ORM 框架的 SQL 注入风险,参数化查询之外的拼接场景(动态排序、IN 列表、原生查询)为何仍可能被注入,如何系统防范?

请说明 ORM 框架在参数化查询之外的拼接场景(动态排序、IN 列表、原生查询)为何仍可能被注入,并说明如何系统防范?

  • 动态排序、IN 列表、原生查询的注入风险
  • 参数化无法覆盖的场景
  • 系统化的防范

即使使用参数化查询,仍有拼接场景无法被参数化覆盖:动态排序(排序字段、升降序不能作为参数绑定,需拼接列名,若直接拼接用户输入可被注入);IN 列表(参数化绑定通常只支持单个值,IN 列表若用字符串拼接可注入,需用动态参数占位符批量绑定);原生查询(绕过 ORM 的 SQL,若拼接用户输入可注入)。这些场景的共性是"参数化无法绑定结构位置(列名、表名、操作符)"。系统防范包括:对动态排序字段/列名用白名单映射(把用户选择映射到固定允许的枚举值);对 IN 列表用动态生成占位符批量绑定参数,而非字符串拼接;原生查询必须参数化,并对结构位置做白名单;禁用 ${} 拼接;结合最小权限账号、审计与代码审查。核心是"结构位置永不拼接用户输入,用白名单替代"。

参数化只能绑定"值",无法绑定"结构"(列名、操作符)。动态排序、IN 列表、原生查询正是把结构位置暴露给用户的场景,是注入的残留风险。系统性防范是"白名单映射结构 + 参数化绑定值 + 禁用拼接",多数注入都可被根除。

// IN 列表:动态生成占位符批量绑定,而非字符串拼接
StringBuilder ph = new StringBuilder();
for (int i = 0; i < ids.size(); i++) { if (i > 0) ph.append(","); ph.append("?"); }
PreparedStatement ps = conn.prepareStatement("SELECT * FROM t WHERE id IN (" + ph + ")");
for (int i = 0; i < ids.size(); i++) ps.setLong(i + 1, ids.get(i));
#

20. 应用账号与运维账号分离、代理登录(proxy 用户)审计真实用户身份如何落地?与连接池复用如何共存?

请说明应用账号与运维账号分离、代理登录(proxy 用户)审计真实用户身份如何落地,以及与连接池复用如何共存?

  • 应用账号与运维账号分离
  • 代理登录(proxy 用户)审计真实身份
  • 与连接池复用的共存

应用账号与运维账号分离是基本安全实践:应用使用独立、最小权限的账号(如只读或特定库表权限),运维使用高权限账号(如 DBA、superuser),两者互不混用,降低应用被攻破后的影响面。代理登录(proxy 用户)用于审计真实用户身份:数据库连接并非直接以真实用户身份建立,而是通过代理账号(proxy user)连接,应用在事务中通过 SET ROLE / 会话标识(如 PostgreSQL 的 session_user/current_user、MySQL 的 proxy 机制)动态切换为真实用户身份或注入审计标识,使数据库审计日志能记录"哪个真实用户执行了操作"。与连接池复用共存的关键是:连接池复用同一物理连接,但可通过会话级属性(SET ROLE、SET session 变量、应用在每次请求注入用户上下文)区分真实用户,使审计仍能正确归属;同时要保证连接归池时清理会话上下文,避免串用户。这样既复用连接、又保留真实身份的审计能力。

账号分离控制权限,代理登录解决"审计归属"问题。连接池复用同一物理连接,通过会话级上下文(SET ROLE、用户标识)在复用的连接上区分真实用户,实现审计而不牺牲连接复用。落地关键是"连接归池时清理上下文"。

-- 应用通过代理连接后,注入真实用户身份用于审计
SET SESSION application_name = 'real_user:alice';
SET ROLE app_role;
-- 执行操作,审计日志记录 alice 的身份