如何防止SQL注入?
用准备好的陈述和最低特权进行辩护
该博客上撰写的文章是出于教学目的而创建的。 另外,文章中的信息可能不正确,请谨慎。
从输入中删除所有引号并不能阻止 SQL 注入。 SQL命令和用户数据必须从一开始就分开,这样数据就不会被解释为命令。
使用准备好的语句
准备好的语句或参数化查询分别标记了 SQL 语句中应放置值的位置。
const sql = 'SELECT id, name, password_hash FROM users WHERE name = ?';
const [rows] = await database.execute(sql, [userName]);
if (rows.length === 1)
{
const passwordMatches = await passwordHasher.verify(
rows[0].password_hash,
password
);
}
这里?输入的值作为数据传输,而不是 SQL 语法。 如果用户使用引号或OR即使输入相同的字符串,查询结构也无法更改。
SQL 中的明文密码不会进行比较。 在获取用户信息作为参数查询后,将输入值与存储在经过验证的密码哈希库中的哈希值进行比较。
使用 ORM 并不会自动让一切变得更安全。 如果不使用ORM提供的参数函数将Raw Query组合成字符串,也会出现同样的漏洞。
表名称和排序标准根据允许列表进行检查。
参数通常用于值。 表名、列名、ASC我DESC很多情况下,相同的SQL语法位置不能作为参数处理。
这些值不是作为用户的字符串粘贴的,而是从服务器预先确定的允许列表中选择的。
const allowedSortColumns = {
newest: 'created_at',
name: 'name'
};
const sortColumn = allowedSortColumns[requestedSort] ?? 'created_at';
const sql = `SELECT id, name FROM users ORDER BY ${sortColumn} DESC`;
requestedSort将其转换为服务器拥有的安全字符串,而不是将其本身放入 SQL 中,这一点很重要。
减少数据库帐户权限
如果向 Web 服务器使用的数据库帐户授予管理员权限,则单个漏洞造成的损害可能会扩散到整个数据库。
- 仅执行搜索的函数仅被授予读取权限。
- 仅访问您需要的表。
- 不要使用 Web 应用程序帐户执行数据库管理任务。
- 密码不是以纯文本形式存储,而是使用经过验证的哈希方法。
错误会记录在服务器上并对用户隐藏。
如果您按原样返回详细的 SQL 错误,则可能会暴露表名或列名等内部结构。 应向用户显示一般失败消息,并应将详细错误记录在具有受控访问权限的服务器日志中。
输入验证、防火墙和检测工具也可以提供帮助。 但首先要做的就是参数化所有查询,将动态输入的 SQL 语法限制在白名单中,并最小化数据库权限。