SQL注入

一、什么是SQL注入(SQL Injection)

1. 核心定义

SQL注入是一种恶意的网络攻击手段,攻击者通过在请求参数中插入特殊的SQL语句片段(如引号、关键字OR/AND/DROP等),让数据库执行原本未预期的SQL命令,从而实现窃取数据、修改数据、删除表甚至控制数据库服务器的目的。

2. 通俗举例(结合你的插入接口)

假设你的代码没有使用参数化查询,而是用字符串拼接的方式构造SQL:

# 危险:字符串拼接构造SQL(极易被SQL注入)
insert_sql = f"INSERT INTO users (name) VALUES ('{user_name}');"

此时攻击者在请求中传入这样的name值:

李四'); DROP TABLE users; --

拼接后的SQL语句就会变成:

INSERT INTO users (name) VALUES ('李四'); DROP TABLE users; -- ');

我们来拆解这个恶意SQL的执行逻辑:

  • '李四');:先正常完成一条插入语句(闭合前面的单引号,结束插入语句);
  • DROP TABLE users;:执行删除users表的危险命令;
  • --:SQL中的注释符,将后面剩余的'注释掉,避免语法错误。

最终结果:users表被直接删除,数据全部丢失,这就是典型的SQL注入攻击(破坏性极强)。

3. SQL注入的危害等级

SQL注入属于高危安全漏洞(OWASP Top 10 常年排名前列),常见危害包括:

  • 窃取敏感数据(如用户密码、数据库配置信息);
  • 篡改数据库数据(如修改用户余额、伪造用户信息);
  • 删除数据库表/库(如DROP TABLE/DROP DATABASE);
  • 提权操作(如获取数据库管理员权限,控制整个服务器)。

二、再理解:你的参数化查询如何避免SQL注入

你的代码cursor.execute(insert_sql, (user_name,))是标准的参数化查询,它通过「先编译SQL模板,再传入参数」的机制,从根源上阻断了SQL注入,具体拆解如下:

步骤1:明确参数化查询的两个核心部分

你的代码中,参数化查询分为两个独立的部分,永远不会拼接成一个完整的字符串

  1. insert_sql = "INSERT INTO users (name) VALUES (%s);":SQL模板(占位符%s代替实际参数值);
  2. (user_name,):实际参数(元组格式,存放要插入的用户名)。

步骤2:参数化查询的执行流程(关键防注入原理)

数据库执行参数化查询时,会严格按照「先编译,后传参」的顺序执行,和字符串拼接有本质区别:

  1. 第一步:编译SQL模板
    数据库先接收INSERT INTO users (name) VALUES (%s);这个SQL模板,对其进行语法解析和编译,确定SQL的执行逻辑(就是「插入一条数据到users表的name字段」),此时%s只是一个纯粹的「参数占位符」,不具备任何SQL语法含义。

    • 这个阶段,数据库已经明确了「要做什么」(插入数据),不会因为后续的参数而改变执行逻辑。
  2. 第二步:传入并转义参数值
    数据库再接收(user_name,)中的实际参数值,将其作为「纯粹的字符串数据」填充到已编译好的SQL模板的%s位置,同时会自动对参数值中的特殊字符(如';--等)进行转义处理

    • 举例:攻击者传入李四'); DROP TABLE users; --,数据库会将其转义为李四\'); DROP TABLE users; --(不同数据库转义方式略有差异);
    • 转义后,特殊字符失去了SQL语法含义,仅作为普通字符串的一部分存储到数据库中,不会被当作SQL命令执行。

步骤3:对比字符串拼接和参数化查询(核心差异)

对比项字符串拼接(危险)参数化查询(安全)
执行顺序先拼接成完整SQL,再编译执行先编译SQL模板,再传入参数
参数的角色作为SQL语句的一部分,参与语法解析作为纯粹的数据,不参与SQL语法解析
特殊字符处理不转义,特殊字符具备SQL语法含义自动转义,特殊字符仅作为普通字符串
执行逻辑是否可变可被参数篡改,执行恶意SQL不可变,仅执行预编译的逻辑(插入/查询等)
能否被SQL注入极易被注入从根源上阻断SQL注入

4. 你的代码执行结果(攻击者参数无效化)

对于攻击者传入的李四'); DROP TABLE users; --,你的参数化查询执行后,最终存入usersname字段的内容就是李四'); DROP TABLE users; --(普通字符串),而不会执行DROP TABLE命令,完美避免了SQL注入。

三、关键补充:关于参数化查询的注意点

  1. 占位符的格式:不同数据库/驱动的占位符格式不同,你的代码中%spymysql(MySQL驱动)的占位符,其他常见格式:

    • PostgreSQL(psycopg2):%s(和MySQL一致);
    • SQLite(sqlite3):?
    • SQL Server(pyodbc):?%s
      核心:不要手动替换占位符,必须通过cursor.execute()的第二个参数传入
  2. 参数必须是元组/列表格式:你的代码中(user_name,)是一个元组(末尾的逗号不能少,否则不是元组),也可以用列表[user_name]pymysql会自动解析其中的参数,切忌直接传入字符串user_name

  3. 仅对参数有效,对SQL关键字/表名/字段名无效:参数化查询的占位符%s只能替代「参数值」,不能替代SQL关键字(如SELECT/INSERT)、表名(如users)、字段名(如name),例如:

    # 无效:用%s替代表名,依然有注入风险
    sql = "INSERT INTO %s (name) VALUES (%s);"
    cursor.execute(sql, ("users", "李四"))  # 表名不能用参数化占位符
    

    核心:表名、字段名若需动态指定,需手动做白名单校验(如只允许指定的几个表名),不能直接接收用户输入。

四、总结

  1. SQL注入:攻击者通过在参数中插入特殊SQL片段,篡改原本的SQL执行逻辑,实现恶意操作的高危攻击;
  2. 参数化查询防注入核心:「先编译SQL模板,后传入并转义参数」,参数仅作为纯粹数据,不参与SQL语法解析;
  3. 你的代码为何安全%s是占位符,(user_name,)是独立参数,数据库自动转义特殊字符,阻断了SQL注入;
  4. 开发准则:凡是涉及用户输入(或外部传入参数)的SQL操作,必须使用参数化查询,禁止字符串拼接。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值