一次sql注入引发的多个mysql进程锁住的问题(针对myisam)
以前碰到过一个sql注入式攻击引发大量mysql进程被锁住的例子,现在分享给大家。当时数据表用的是myisam存储引擎。
基本知识点:
InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,而MyISAM支持的是表级锁。
对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;MyISAM表的读操作与写操作之间,以及写操作之间是串行的。
模拟示例:
create table myisam_test(id int primary key not null auto_increment,
v1 varchar(64),
v2 varchar(64)) engine=myisam;
insert into myisam_test(v1,v2) values('a','b');
多次执行 insert into myisam_test(v1,v2) select concat(v1,'a'),concat(v2,'b') from myisam_test;
接下来大boss上场了,就是那段被注入的sql
select * from myisam_test where (id=1000) xor(if(453=453,sleep(5),0)) limit 1;
由于程序没有过滤非法参数值(xor(if(453=453,sleep(5),0))),sql一直在执行中,但是读操作不会阻塞其他用户对同一个表的读请求因此执行另一条select后会立刻返回执行结果:
但是假如我们在此时执行写操作呢?见下图:
读操作会阻塞对同一表的写请求,而写操作会阻塞其他用户对同一表的读和写操作,因此在这里后续的update和select语句都因为id=8的那个process而一直处于等待状态,无法返回结果。
我们kill掉那条被注入的sql执行进程,锁立刻释放,后续的sql立刻返回结果。
Sql注入式攻击再配合myisam的特性,导致大量进程处于等待状态,因此我们编程时基本的安全意识还是要有的,如净化用户输入,如使用如下参数化查询而不是将参数拼接到sql语句中:
PreparedStatement prep = conn.prepareStatement("SELECT * FROM myisam_test WHERE v1=? AND v2=?");
prep.setString(1, value1);
prep.setString(2, value2);
prep.executeQuery();