什么是 SQL 注入?为什么我们要防止它?
在开发网站或 App 时,用户常常需要输入一些信息,比如账号、密码、昵称、评论等。如果这些输入没有被好好处理,就可能会被黑客利用,进行一种叫做 SQL 注入(SQL Injection) 的攻击。
SQL 注入是一种常见的网络攻击方式,攻击者通过在输入框中输入一些恶意的 SQL 语句,试图绕过程序的正常逻辑,从而窃取、篡改或删除数据库中的数据。
举个例子:
假设你正在开发一个注册页面,用户输入账号和密码,程序会把这些信息保存到数据库里。
正常情况下,用户输入的账号是:zhangsan
但攻击者可能输入这样的账号:
zhangsan'); DROP TABLE users; --
如果你的程序没有做好防护,这段输入就可能被当作 SQL 命令执行,导致整个用户表被删除!
SQL 注入的危害
- 数据泄露:黑客可以获取用户的敏感信息,比如密码、手机号。
- 数据被篡改:黑客可以修改你的数据库内容,比如把某个用户的余额改成100万。
- 数据被删除:黑客可以删除整个数据库,让你的网站瘫痪。
- 服务器被控制:极端情况下,黑客甚至可以控制你的服务器。
所以,SQL 注入是非常危险的,我们必须防止它!
如何防止 SQL 注入?
防止 SQL 注入的核心思想是:永远不要相信用户的输入!
下面是几种常用的防护方法:
✅ 方法一:使用参数化查询(推荐)
这是最安全、最常用的方法。
举个例子:
如果这样写 SQL:
let query = "SELECT * FROM users WHERE username = '" + username + "'";
这种写法存在SQL注入风险。如果用户输入的username值是 ' OR '1'='1,最终SQL会变成:
SELECT * FROM users WHERE username = '' OR '1'='1'
这将返回所有用户数据,导致严重的安全漏洞。攻击者可以利用这种方式绕过认证、窃取数据甚至破坏数据库。
参数化查询(以 Node.js + MySQL 为例):
- 这里的
?是一个占位符,数据库知道这里将有一个参数。 - 参数绑定:随后传入的参数值会被严格作为数据处理,不会被解释为SQL代码
let query = "SELECT * FROM users WHERE username = ?";
connection.query(query, [username], function (error, results) {
// 处理结果
});
这样即使用户输入的是 SQL 语句,也会被当作普通字符串处理,不会被执行。
✅ 方法二:对输入进行转义(防 HTML/JS 注入)
有时候用户输入的内容会被显示在网页上,比如评论、昵称等。如果用户输入了恶意的 HTML 或 JavaScript 代码,可能会导致页面弹窗、跳转等危险行为。
比如用户输入:
<script>alert('你已中奖');</script>
如果你直接显示在网页上,所有看到这条评论的用户都会弹出一个“你已中奖”的窗口,这就是一种叫做 XSS(跨站脚本攻击) 的攻击。
解决方法:对输入内容进行转义
我们可以写一个函数,把 <、>、&、"、' 这些特殊字符转义成 HTML 实体:
// 防止 HTML/JS 注入
export function escapeHtml(str) {
return str.replace(/[<>&"']/g, function (match) {
return {
'<': '<',
'>': '>',
'&': '&',
'"': '"',
"'": '''
}[match];
});
}
然后在使用用户输入的地方调用它:
username = escapeHtml(username);
password = escapeHtml(password);
nickname = escapeHtml(nickname);
查询数据库,我们发现'<'被转义成了<,别的也被进行了不同的处理。这样这个数据就安全啦。
✅ 方法三:对输入进行校验(白名单过滤)
比如用户输入账号时,你可以规定:
- 只能使用字母、数字、下划线
- 长度限制在 6-20 个字符
这样可以提前阻止很多非法输入。
✅ 方法四:使用 ORM 框架(如 Sequelize、TypeORM)
ORM(对象关系映射)框架会自动帮你处理 SQL 注入问题,比如你只需要写:
User.findOne({ where: { username: username } });
底层会自动使用参数化查询,帮你安全地操作数据库。