SQL注入问题与解决方案

Web 程序中,一般都会有后台根据用户输入内容查找或者执行相关动作的场景,如登录时查询
用户是否存在。后台在处理的时候可能是根据用户输入的用户名,拼接 SQL 之后到数据库查询来
判断,这时,如果用户恶意输入不正确内容或内容本身存在问题,会导致应用程序崩溃,甚至是
丢失数据等导致相关损失。即通过把 SQL 命令插入到 Web 表单提交或输入域名或页面请求的查询
字符串,最终达到欺骗服务器执行恶意的 SQL 命令。这种场景就称为 SQL 注入。
SQL 注入攻击是通过将恶意的 SQL 语句如添加、删除等插入到应用的输入参数中,经过后台解析
后发送到数据库服务器上解析执行进行的攻击。本文以 mysql 为例,讨论 SQL 注入以及在 Django
中如何防止 SQL 注入。

一个SQL注入的简单例子

如执行一条 SQL 语句 :
select * from tb_user where username = 'stone';

其中 stone 是用户输入的值,此时可以得到正确的值: 

mysql> select * -> from tb_user -> where username = 'stone'; 

+------+-----------+----------+------+ 
| id | username | password | age | 
+------+-----------+----------+------+ 
| 1 | stone | 123456 | 29 | 
+------+-----------+----------+------+ 
1 row in set (0.00 sec)
但是,当用户输入错误的值,如  stone';drop table tb_test; ,如果使用的是字符串拼接的方式
去执行, SQL 语句就变成了:
select * from tb_user where username = 'stone';drop table tb_test;';
执行结果为:
+------+-----------+----------+------+ 
| id | username | password | age | 
+------+-----------+----------+------+ 
| 1 | jacobzhou | 123456 | 29 | 
+------+-----------+----------+------+ 
1 row in set (0.00 sec) ERROR 1051 (42S02): Unknown table 'db-platform.tb_test' '>
以上就是一个 SQL 注入的例子,可以看到,如果 db-platform.tb_test 表存在,那就会被恶意删
除。例子相对较极端,对于 Django 自带的 connection 来说,使用 execute 函数执行 SQL 语句的时
候,每次只执行一条语句,后一条语句不会执行。因此上面的例子在 Django 中是不成立的,但是
足以说明 SQL 注入所带来的安全风险。那 SQL 注入都有哪些方式,如何才能防止 SQL注入呢?

SQL注入原理

Web 应用程序对于用户输入的数据和合法性没有严谨的判断,前端用户的输入直接传输给后端,
攻击者通过构造不同的参数,形成不同的 SQL 语句来实现对数据库的任意操作。
SQL 注入产生需要满足两个条件
  • 参数用户可控:前端传给后端的参数内容是用户可以控制的
  • 参数带入数据库查询:传入的参数直接拼接到SQL语句,且带入数据库查询

SQL注入类型

SQL 注入的分类有很多,如 POST注入、Cookie 注入、延时注入、搜索注入等,但是归根结底也
数字型字符型注入的不同展现形式或者是注入的位置不同。

数字型
用户输入为整数,假设 SQL 语句为:
select * from home_application_database where id = 3;
其中 3 为数字,是用户正常输入。
当满足如下条件,则可能存在数字型注入
  • 输入3' 页面报错(SQL语法错误)
  • 输入3 and 1 = 1 页面正常返回结果
  • 输入3 and 1=2 页面返回错误(SQL语句返回空数据)
如果后台使用的是 select * from home_application_database where id = 和未经验证的用户输入做
拼接后去数据库查询,就满足了上面的三个条件,存在数字型注入。
这里可以看到用户输入必须是整数,后端验证用户输入必须是整数才会继续执行即可解决。

如何防止SQL注入

在开发时应该秉持一种 外部参数皆不可信 的原则来进行开发。
  • 加强参数验证开发时,验证所有来自前端的输入,必须是符合要求的数据类型,符合指定规则的数据才允许继续往下执行。
  • SQL语句参数化处理 减少使用或不使用字符串拼接的方式执行SQL,而是将用户输入当着参数传给执行SQL的方法, Django中的cursor.execute()函数就支持在SQL语句中使用占位符,将输入作为参数传递给方 法执行。
  • 存储过程:使用存储过程也可以有效防止SQL注入,不过在存储过程中,需使用占位符,并且使用输入参数来预编译SQL语句后再执行。

Django中防止SQL注入

Django 中使用 ORM 可以有效防止 SQL 注入,所以应该尽可能使用 ORM 。但是 ORM 对于复杂查询
就无能为力了,这时就需要执行原生 SQL 时,可以使用如下方式:
  • 使用extra(不建议使用这种方式执行SQL
  • 使用raw
  • 使用django.db执行自定义SQL
  • 直接使用pymysql
在使用原生 SQL 语句时,应避免直接使用用户输入拼接 SQL 语句,上面三种执行原生 SQL 的方式
均提供了占位符来进行参数替换,防止 SQL 注入。我们比较常用的是 3 、和 4 ,两种方法都是使用
cursor.execue() 方法,具体如下:
  • django.db
from django.db import connection 
cursor = connection.cursor() 
cursor.execute(sql)
result = cursor.fetchall()
  • pymysql
connection = pymysql.connect(**mysql_server) 
cursor = connection.cursor() 
cursor.execute(sql) 
result = cursor.fetchall()
execute 中的 sql 语句使用占位符,并传入相应参数即可防止 SQL 注入:

 

从上面执行结果看出,对于整数型注入,使用 execute 中带参数执行的方式,并不满足注入条
件,当使用 3' 3 and 1 = 1 3 and 1 = 2 作为输入传给 execute 执行时,程序报错。
同理对于字符型注入也一样。
注意:在使用 execute 函数执行时, SQL 语句中的占位符,不管是字符还是整型,都使
%s ,且对于字符型的数据,在 SQL 语句里面不能使用 '%s' ,否则会报错。使用参数替换
本质上是对输入的参数进行转义处理,防止输入中的引号。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值