SQL注入和防护简介

SQL注入详解

SQL注入指通过向服务器发送恶意的SQL语句或片段注入到服务器中,改变原有的SQL逻辑,从而实现攻击者的目的

  • 比如在源代码中存在这样的语句:
public Object sqlInjection(String param){
    ...
    String sql = "select * from user where username='" + param + "'";
    resultSet = prepareStatement.executeQuery(sql);
    ...
}

在变量sql定义中,param参数通过拼接的方式称为了sql语句的一部分。比如传来正常的参数admin,此时的sql语句便是select * from user where username='admin',该语句便会正常执行,并返回想要查询的结果。但是如果传来的值为' or 1=1#,那么此时sql会变成select * from user where username='' or 1=1#'。由于传来值的第一个'号和前边的'进行了闭合,因此后边的部分代码便变成了sql语句的一部分而不是一个值。此时由于or 1=1永远成立,并且#符将后边的'注释掉,因此现在的sql语句变成了一段恶意代码,这便是sql注入

防护

sql注入的防护有两种方法,第一是对参数进行预编译,第二是在拼接参数前对参数进行安全校验,通过后才可以拼接到sql语句中。

预编译

当一条SQL语句执行前,需要先进行语法分析。如果没有使用预编译的方法来执行SQL,那么每次在执行SQL时都需要先进行语法分析,然后再执行。此时进行SQL注入,恶意的代码(payload)拼接上SQL语句之后才会进行语法分析,其中payload中包含的关键字等内容便会被识别,因此会被成功执行。

而预编译则是先在待传入参数的位置增加占位符(?),并对SQL语句进行语法分析和编译,并将结果存入内存中等待调用。在调用时,传入的参数会自动放入占位符的位置,并执行SQL语句。此时如果进行注入,由于并没有重新进行语法分析,payload中包含的关键字等不会被识别,而是作为字符串的形式存在于语句中,因此payload不会成为SQL语句的一部分。

select * from user where username=?,如果payload是' or 1=1#时,执行的代码是:select * from user where username="' or 1=1#"(为了阅读更加清晰,此处将''替换为了""),注入失败

参数校验

在一些情况下是不能使用预编译的方法来避免SQL注入(比如在某些业务场景下需要拼接字段名

String sql = "select * from user where " + username + "=?"

此时需要防止SQL注入,便需要对username进行校验:

  • 类型转换:如果拼接上的值是整数或浮点数等,那么便可以对参数进行类型转换来避免SQL注入

    String page = request.getParameter("page");
    String size = request.getParameter("size");
    String sql = "select * from user limit " + page + "," + size;
    

    此时可以在拼接前先把pagesize两个参数转换成整型再进行拼接,如果两个参数中包含有恶意代码,则在类型转换时便会报错,从而避免SQL注入

  • 白名单校验:设定一个白名单,当变量的值符合白名单中的某一个时,便可拼接

    String username = request.getParameter("username");
    //白名单校验
    String whitelist = "username,password,gender";
    String sql = "";
    if(username != null && whitelist.indexOf(username) > -1){
        sql = "select * from user where " + username + "=?";
    }
    

注入方法

通常在有输入的地方,可以判断是否会执行SQL语句,比如查询等。当判断存在有SQL执行时,便可通过尝试注入。如果能够看到源代码,便通过分析源代码构建payload(其中分为字符型注入,数值型注入等情况);如果看不到源代码,则需要进行盲注(分为报错型盲注,时间型盲注和布尔型盲注

数值型

(此处以靶场Pikachu为例)

  • 分析代码,可得此处存在数字型注入

  • 由于在放入个人的payload前要将前边的代码闭合(即让前边的条件成立),因此需要先随便放上一个数字payload=3,然后再跟上自己的代码:payload=2 or 1=1#

  • 注入成功

字符型注入

查看代码

  • 由于拼入的参数值是字符类型的,它被''所包裹,因此需要考虑怎样将''闭合起来。构建payload='=0#'

  • 注入成功

联合查询注入

  • 联合查询注入是在判断此处存在sql注入后,通过使用union all来查看自己想要的数据

  • 由于联合查询要求多个查询之间的字段数目相等,因此先使用正常的字符来判断它的返回结果有几个字段

  • 可以判断可能存在三个字段,因此构建payload=' union all select 1,2,3#来进行尝试(最后一个3前加引号是为了和原sql中最后一个'进行闭合)

  • 没有报错并且显示出了联合查询的内容,因此字段数目对应上,并且确定了每个字段的显示位置,因此便可构建新的payload=' union all select database(),version(),3#来查看自己想看到的内容

  • 如果在上一步中字段数目不对,则可动态的添加或删除字段数来进行调试payload

盲注

盲注没有返回值或返回值不清晰,无法判断是否注入成功,因此需要想办法验证注入结果

boolean型

boolean型即根据条件的不同,得到不同的结果来判断是否存在SQL注入

  • 当输入admin时,结果可以正常显示

  • 构建payloadadmin' and 1=1#

  • payload:admin' and 1=2#

因此可以判断存在SQL注入

时间注入

使用sleep()函数可以使当前执行SQL的线程睡眠指定的时间,因此通过时间的长短来判断是否存在SQL注入

  • 发送请求后,使用burpsuite抓包,并将参数的值改为payload后发包。(此处使用if函数为了让结果更直观明显)

  • 对比结果

  • 根据结果的不同可以判断,sleep函数成功执行,因此此处存在SQL注入

报错注入

在一些更新或者插入的地方,无法使用正常的那些注入方式,便可通过构建一个错误的语句,让系统报错,从而判断是否存在SQL注入

  • 构建payload:' or updatexml(1,concat(0x7e,database()),3,) or '

  • 在报错中显示出了数据库名,因此此处存在报错注入,并且能导致信息泄露

扫描工具

判断是否存在SQL注入的神器 -> sqlmap

SQLMap

  • 在kali系统中,默认安装好了sqlmap工具直接在控制台使用即可

  • 使用sqlmap扫描接口。在控制台输入:

    sqlmap -u "http://localhost/pikachu/vul/sqli/sqli_str.php?name=asd&submit=%E6%9F%A5%E8%AF%A2" -p name
    

    回车后可以看到执行的过程和结果

  • sqlmap具体使用教程不过多介绍,深入了解请移步:sqlmap教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值