SQL 注入离我们有多远:参数化示例
发布时间:2025-12-29 21:08       
更新时间:2025-12-29 22:52
SQL 语句里出现第一处字符串拼接,注入风险就产生了。它不是玄学攻击,原理一句话讲完:用户输入被当成了 SQL 代码执行。它离每个写后端的人都不远:只要有一条 SQL 拼接了用户输入,风险就是实打实存在的,和项目大小无关。
一个注入长什么样
登录查询拼字符串:
sql = "SELECT * FROM users WHERE name='" + name + "' AND pwd='" + pwd + "'"
攻击者在密码框输入 ' OR '1'='1,拼出来的语句变成:
SELECT * FROM users WHERE name='x' AND pwd='' OR '1'='1'
OR '1'='1' 恒为真,密码校验被整个绕过。更狠的输入还能删表、拖库,原理相同:把数据当代码塞进了执行流。真实的攻击是工具化的:自动扫全站参数,把各种变形载荷挨个试一遍,远比手工试探高效。
参数化改写
参数化查询(预编译语句)把 SQL 结构和参数值分开传送,参数从头到尾只当数据解析:
Python(pymysql):
cursor.execute("SELECT * FROM users WHERE name=%s AND pwd=%s", (name, pwd))
PHP(PDO):
$stmt = $pdo->prepare("SELECT * FROM users WHERE name=? AND pwd=?");
$stmt->execute([$name, $pwd]);
改写成本就这几行,但注入路径被彻底堵死,是安全措施里少有的"做了就等于修好"。查询条件动态变化的场景,把 SQL 骨架按分支拼好,值依然全部走占位符传入,灵活和安全可以兼得。
ORM 也别拼字符串
用了 ORM(对象关系映射框架)不等于进了保险箱。框架自带的查询接口是参数化的,可一旦写原生 SQL 片段,老问题就回来了:where("name='" + name + "'") 这种写法在 ORM 里照样注入。原则只有一条:任何用户输入进 SQL 必须走占位符,没有例外。代码评审时看到字符串拼进查询语句,无论出现在哪一层,都按注入风险处理。
附加防线
参数化之外再叠两层:数据库账号按最小权限配置,就算被打穿,能拖走的也有限;入口层拦掉明显异常的超长输入和特殊字符,能挡掉大量扫描器噪音。防线可以多层,但核心自始至终只有参数化这一条。另外,错误信息里别把用户输入原样回显,能少给探测者不少反馈。