sql注入防护需严控preparedstatement参数绑定时机、实施数据库账号权限最小化、启用查询日志监控异常行为、采用数据库原生加密并安全管理密钥。

SQL 注入检测必须检查 PreparedStatement 的参数绑定方式
用 PreparedStatement 不等于防住 SQL 注入——常见错误是拼接字符串后才塞进 setString(),比如 "' OR 1=1 -- " 被当成普通值传入,但实际已污染 SQL 结构。关键在「绑定时机」:所有用户输入必须通过 setXxx() 方法注入,不能出现在 SQL 字符串模板里。
实操建议:
- 检查所有
prepareStatement("SELECT * FROM user WHERE id = " + userInput)类写法,立刻替换成prepareStatement("SELECT * FROM user WHERE id = ?")+setLong(1, Long.parseLong(userInput)) - 对动态表名、列名等无法参数化的部分,必须白名单校验(如
if (!Arrays.asList("orders", "users").contains(tableName)) throw new IllegalArgumentException();) - 启用 JDBC 驱动的
allowMultiQueries=false(MySQL 默认为false,但显式配置更稳妥)
权限最小化要落实到数据库用户而非应用账号
应用连接数据库时用的账号,权限常被设为 root 或 db_owner,一旦漏洞触发,攻击者可直接 DROP TABLE 或读取 mysql.user。真实防护靠的是数据库层账号隔离。
实操建议:
- 为每个业务模块创建独立 DB 用户,例如报表服务只给
SELECT权限,且限制到具体视图(GRANT SELECT ON sales_summary_view TO report_user@'10.20.%') - 禁用
FILE权限(防止SELECT ... INTO OUTFILE外泄)、PROCESS(避免看到其他查询)、SUPER(防止修改全局变量) - PostgreSQL 中用
REVOKE CREATE ON SCHEMA public FROM PUBLIC阻止新建对象
pg_stat_statements 和 slow_query_log 是异常 SQL 行为的第一道哨兵
注入成功后,攻击者常试探性执行 SELECT version()、SELECT pg_sleep(5) 或大量 UNION SELECT,这类行为会明显偏离正常查询模式——日志里不是看“有没有报错”,而是看“有没有高频非常规语句”。
云点滴客户解决方案是针对中小企业量身制定的具有简单易用、功能强大、永久免费使用、终身升级维护的智能化客户解决方案。依托功能强大、安全稳定的阿里云平 台,性价比高、扩展性好、安全性高、稳定性好。高内聚低耦合的模块化设计,使得每个模块最大限度的满足需求,相关模块的组合能满足用户的一系列要求。简单 易用的云备份使得用户随时随地简单、安全、可靠的备份客户信息。功能强大的报表统计使得用户大数据分析变的简单,
实操建议:
- MySQL 开启
slow_query_log=ON+long_query_time=1,重点监控含sleep(、benchmark(、information_schema的慢查 - PostgreSQL 启用
pg_stat_statements扩展,定期查SELECT query, calls FROM pg_stat_statements ORDER BY calls DESC LIMIT 10,突增的简单查询(如SELECT 1)往往是探测流量 - 把
general_log仅用于临时排障,生产环境禁用——它本身会拖垮性能
敏感字段加密不能只靠应用层 AES,必须结合数据库透明加密(TDE)或列级加密
应用层加解密看似可控,但密钥常硬编码或存在内存中,一旦进程被 dump,密文+密钥一起泄露。而 TDE(如 MySQL 5.7+ 的 innodb_encrypt_tables=ON)或 PostgreSQL 的 pgcrypto 函数,让加密发生在存储引擎或 SQL 层,密钥由 KMS 管理。
实操建议:
- 身份证、手机号等字段,优先用数据库原生加密函数:MySQL 用
AES_ENCRYPT(col, @key)(注意@key必须来自外部 KMS,不存配置文件) - 避免在 SQL 中明文写密钥:
WHERE AES_DECRYPT(ssn, 'hardcoded_key') = '123'—— 这会导致密钥出现在pg_stat_activity或慢日志里 - 审计时重点查
SELECT语句是否包含未加密的敏感字段别名(如SELECT ssn AS social_security),这类别名可能被下游缓存误存
真正难的不是选哪种加密,而是密钥轮换时怎么不锁表、不中断服务——这一步多数人卡在运维脚本没覆盖密文迁移路径。









