什么是SQL注入
假设你的网站登录页面的代码是这样的:SELECT * FROM users WHERE username = '加上用户输入' AND password = '加上密码输入'。
正常情况下用户输入 username=admin password=123456 生成的SQL是正常的。但如果有人在用户名框输入:admin' OR '1'='1' -- ,生成的SQL就变成了:查找用户名为admin或者1=1的用户——由于1=1永远为真,攻击者就不需要密码就能登录你的系统了。
这只是最基础的SQL注入。更危险的操作包括:读取整个数据库(拖库)、修改数据(篡改金额或权限)、写入webshell(获取服务器控制权)、甚至执行系统命令(彻底接管服务器)。
为什么还在发生
SQL注入已经被认识了将近30年(最早公开讨论在1998年),但它依然是OWASP Top10排名第一的Web安全漏洞。原因很简单:
拼接SQL字符串太方便了——初学者教程里就是这么教的。很多"能跑就行"的项目从来不考虑安全。即使是有经验的开发者也会在赶工期的时候偷懒用字符串拼接。老旧系统没人维护但还在运行。
邢台我们在给本地企业做安全扫描时发现,大约40%的网站存在不同程度的SQL注入风险。这个比例令人担忧。
防护方法
方法一:参数化查询(预编译语句)。这是最有效最根本的防护手段。不管用户输入什么内容都只会被当作数据处理而不会被当作SQL指令执行。所有主流编程语言和数据库驱动都支持参数化查询——没有任何理由不用。
方法二:ORM框架。使用ORM(如Hibernate、MyBatis、Sequelize、Django ORM)可以在很大程度上避免手写SQL从而避免SQL注入。但要注意某些ORM的不安全用法(如MyBatis的${}拼接)仍然存在风险。
方法三:输入验证。对所有用户输入做严格的白名单验证(比如用户名只允许字母数字下划线)。这是纵深防御的一部分但不能替代参数化查询。
方法四:最小权限原则。数据库连接使用的账号不应该有DROP TABLE、CREATE TABLE、读写其他库等不必要的权限。万一发生了注入也能限制损害范围。
方法五:使用WAF。Web应用防火墙可以检测和拦截常见的SQL注入攻击模式。作为额外的防护层很有价值但不能替代代码层面的安全。