最可靠的方法是始终使用参数化查询或预处理语句,而不是通过简单的字符串连接用户值来形成查询。
对于更复杂的场景(存储过程中的动态SQL)— 使用特殊的转义机制和白名单(严格过滤)参数。
cursor.execute('SELECT * FROM users WHERE login = %s', [login])
EXECUTE format('SELECT * FROM %I WHERE id = $1', tablename) USING id;
这里%I以安全的方式只插入表名/列名。
在将数字插入字符串时,以下方法是否安全?
EXECUTE 'SELECT * FROM users WHERE id = ' || user_input;
不!即使你期望的是数字,攻击者也可以传递代码:
1; DROP TABLE users; — 查询将会被执行。因此始终使用占位符和对输入参数类型的明确检查。
故事1
员工搜索表单通过应用层字符串连接来构建WHERE条件。攻击者输入:' OR 1=1 --,这导致输出所有员工的数据。后来,采用相似的方法删除了数据部分。结论:连接重要参数 — 始终存在风险。
故事2
在报告选择的管理界面中,表名从用户字段中提取并插入SQL查询。用户能够插入无效名称并访问系统表。之后,只允许硬编码名称(白名单)。
故事3
在电子商务项目中,用户通过GET参数传递字段名称进行动态排序。黑客插入price; DELETE FROM orders; --,导致所有订单丢失。过滤允许的列名并使用参数化查询解决了问题。