SQL注入防护需多层加固:最小化数据库安装、关闭错误信息回显、严格隔离服务账户权限,仅参数化查询不足以抵御绕过攻击。

SQL注入防护不能只靠参数化查询
参数化查询是基础,但不是银弹。真实生产环境里,prepare + execute 写得再规范,只要数据库服务本身暴露了多余功能、默认账户没删、错误信息回显开启,攻击者照样能绕过应用层直达后端。
最小化安装模式直接砍掉高危组件
以 PostgreSQL 为例,装完默认带 pg_stat_statements、adminpack、dblink,这些扩展有的能执行远程 SQL,有的会把查询计划和原始语句明文记录到系统视图里——一旦应用报错把 pg_stat_statements.query 拖出来展示,等于把拼接痕迹白送给攻击者。
- 安装时加
--without-contrib(PostgreSQL)或用apt install postgresql-server-dev-all替代全量包(Debian/Ubuntu) - MySQL 启动时加
--skip-innodb(仅当真不用事务)、--skip-log-bin、--skip-performance-schema,减少可探测面 - 确认
show databases;返回结果是否包含information_schema和mysql(MySQL)或pg_catalog(PostgreSQL)之外的库——多出来的往往是运维脚本自动建的测试库,删掉
错误信息必须关,且不能靠应用层“try-catch”兜底
数据库连接失败、语法错误、权限拒绝这三类响应,如果直接透出给前端,等于告诉攻击者表名、字段名、甚至当前用户权限边界。Nginx 或 Apache 的 error_log 里出现 ERROR: relation "users" does not exist 这种日志,说明数据库已泄露结构信息。
- PostgreSQL:把
log_min_error_statement = error改成log_min_error_statement = panic,并确保client_min_messages = warning - MySQL:设
sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,同时关掉show_compatibility_56 = OFF(避免旧版兼容模式泄漏元数据) - 验证方式:手动输个错 SQL,比如
SELECT * FROM users WHERE id = '1' OR '1'='1';,看返回 HTTP 响应体里有没有column "xxx" does not exist这类字眼
服务账户必须按场景隔离,不能复用 root 或 postgres
一个 Web 应用连数据库,只需要查 orders、插 logs、更新 user_profiles —— 它不需要 DROP TABLE、不需要 CREATE FUNCTION、更不需要访问 pg_shadow。用超级用户跑应用,等于把锁芯交给小偷还告诉他钥匙在哪。
- 新建专用角色:
CREATE ROLE webapp WITH NOSUPERUSER NOCREATEDB NOCREATEROLE; - 只赋必要权限:
GRANT SELECT, INSERT ON TABLE orders TO webapp;,不加ON ALL TABLES IN SCHEMA public - MySQL 里用
CREATE USER 'webapp'@'10.0.2.%' IDENTIFIED BY 'xxx';显式限定来源 IP 段,比'%'安全得多
最小化安装不是装得少就完事,是让每个组件、每个账户、每条错误路径都经得起“它为什么需要存在”这一问。漏掉任意一环,前面所有参数化查询都只是纸糊的墙。

















