SQL 注入是非常常见的一种网络攻击方式, 主要是通过参数来让 MySQL 执行 sql 语句时进行预期之外的操作.
即: 因为传入的参数改变 SQL 的语义, 变成了其他命令, 从而操作了数据库.
产生原因: SQL 语句使用了动态拼接的方式.
例如, 下面这段代码通过获取用户信息来校验用户权限:
- import pymysql
- sql = 'SELECT count(*) as count FROM user WHERE id =' + str(input['id']) + 'AND password ="' + input['password'] + '"'
- cursor = dbclient.cursor(pymysql.cursors.DictCursor)
- cursor.execute(sql)
- count = cursor.fetchone()
- if count is not None and count['count']> 0:
- print('登陆成功')
但是, 如果传入参数是:
input['id'] = '2 or 1=1'
你会发现, 用户能够直接登录到系统中, 因为原本 sql 语句的判断条件被 or 短路成为了永远正确的语句.
这里仅仅是举一个例子, 事实上, sql 注入的方式还有很多种, 这里不深入介绍了.
总之, 只要是通过用户输入数据来拼接 sql 语句, 就必须在第一时间考虑如何避免 SQL 注入问题.
那么, 如何防止 SQL 注入呢?
2. 预防 SQL 注入 - pymysql 参数化语句
pymysql 的 execute 支持参数化 sql, 通过占位符 %s 配合参数就可以实现 sql 注入问题的避免.
- import pymysql
- sql = 'SELECT count(*) as count FROM user WHERE id = %s AND password = %s'
- valus = [input['id'], input['password']]
- cursor = dbclient.cursor(pymysql.cursors.DictCursor)
- cursor.execute(sql, values)
- count = cursor.fetchone()
- if count is not None and count['count']> 0:
- print('登陆成功')
这样参数化的方式, 让 MySQL 通过预处理的方式避免了 sql 注入的存在.
需要注意的是, 不要因为参数是其他类型而换掉 %s,pymysql 的占位符并不是 python 的通用占位符.
同时, 也不要因为参数是 string 就在 %s 两边加引号, MySQL 会自动去处理.
3. 预防 SQL 注入 - MySQL 存储过程
数据库存储过程是 MySQL 的一种高级用法, 但是一般来说, 并不建议使用数据库的存储过程.
主要原因是:
存储过程的语法与普通 SQL 语句语法相差太大, 增加维护成本
存储过程在各数据库间不通用且差别较大, 给数据库的移植和扩展带来困难
编写困难, 数据库脚本语言使用起来还是很不方便的, 包括很多数据结构的缺失, 让很多事情做起来很困难
调试困难, 虽然有一些功能强大的 IDE 提供了数据库存储过程的调试功能, 但是通常你需要同时在数据库层面上和业务中同时进行调试, 两处调试极为不便
业务耦合, 编写存储过程通常是需要在其中放入部分业务逻辑, 这使得业务分散在数据层, 业务层与数据层的耦合对于项目维护和扩展都会带来极大地不便.
但是, 虽然不建议使用存储过程, 但是毕竟可以依赖他实现各种跨语言的 sql 注入预防, 在复杂的场景下还是有其使用价值的.(以后需要用再去详细学, 这里只作简单介绍)
3.1. 存储过程编写
- delimiter \DROP PROCEDURE IF EXISTS proc_sql \CREATE PROCEDURE proc_sql (
- in nid1 INT,
- in nid2 INT,
- in callsql VARCHAR(255)
- )
- BEGIN
- set @nid1 = nid1;
- set @nid2 = nid2;
- set @callsql = callsql;
- PREPARE myprod FROM @callsql;
-- PREPARE prod FROM 'select * from tb2 where nid>? and nid<?'; 传入的值为字符串,? 为占位符
-- 用 @p1, 和 @p2 填充占位符
- EXECUTE myprod USING @nid1,@nid2;
- DEALLOCATE prepare myprod;
- END\delimiter ;
3.2. pymsql 中调用
- import pymysql
- cursor = conn.cursor()
- MySQL="SELECT * FROM user where nid> ? and nid < ?"
- cursor.callproc('proc_sql', args=(11, 15, MySQL))
- rows = cursor.fetchall()
- conn.commit()
来源: https://www.cnblogs.com/liangmingshen/p/10323535.html