SQL注入

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

1. 核心定义

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

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

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

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

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

复制代码
李四'); DROP TABLE users; --

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

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),例如:

    python 复制代码
    # 无效:用%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操作,必须使用参数化查询,禁止字符串拼接。
相关推荐
科技小花23 分钟前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸24 分钟前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain26 分钟前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希1 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神1 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员1 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java1 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿2 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴2 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存
YOU OU2 小时前
三大范式和E-R图
数据库