WEB第三方应用SQL语句安全规范

WEB第三方应用SQL语句安全规范

1. 规范目的

随着RDS用户的增多,越来越多的应用开始使用RDS数据进行数据存储,很多应用都直接或间接跟金钱相关,所以关于第三方应用WEB系统的代码安全、以及和SQL有关的编码规范变得越来越重要
本规范是为了帮助RDS用户,处理SQL注入、脱库攻击、数据泄漏的攻击,帮助用户一起修补WEB系统中的SQL注入漏洞,共同保护用户的数据安全

 

 

2. 使用范围

所有接入RDS的第三方应用

 

 

3. 编码原则

SQL注射是由于程序中对使用到的SQL没有做到数据和结构分离导致程序的SQL能被恶意用户控制,从而引发的数据库信息泄漏,修改,服务器被入侵等一系列严重后果。

注入攻击的本质,是把用户输入的数据当作代码执行。这里有两个关键条件,

1) 第一个是用户能够控制输入
2) 第二个是原本程序要执行的代码,拼接了用户输入的数据

 

 


4. SQL语法规范

1. 少用空值作为查询条件。原因有两个:
    1) 空值将不能用上索引,会影响到数据库性能,尽量在缺省值,在插入数据时自动让数据库填上缺省值
    2) 容易产生空值永真、从而触发拦截规则
例:
原语句:
        select * from employee where flag is null;
改进后语句:
        select * from employee where flag=0(让数据库自动缺省为0)

 

2. 对数字型的SQL查询采用明确的数字型强制类型转换,原因有2点:
    1) 原来的业务逻辑本身就是数字INT型,强制转换后并不影响正常的业务
    2) 对数字型的参数进行数字型强制类型转换后,能防止攻击者输入除数字之外的额外的参数进行注入攻击
例:
原语句:
        $id = $_GET[id];
        "select * from employee where id=$id";
改进后语句:
        $id = intval($_GET[id]);
        "select * from employee where id=$id"

 

3. 对字符型的SQL查询采用对应编码转义操作,原因有2点:
    1) 原来的业务逻辑本身就是纯字符STRING型,转义后不会影响到业务的运行
    2) 对字符型的参数进行转义后,能防止攻击者输入单引号、双引号、反斜线等"闭合类字符"进行闭合注入,是一种有效防御注入的方法
例:
原语句:
        $name = $_GET[name];
        "select * from employee where name=‘$name‘";
改进后语句:
        $name = mysql_real_escape_string($_GET[id]);
        "select * from employee where name=$name";

Ps:
1. 使用转义函数的时候一定要注意当前脚本语言的编码格式和数据库的编码格式是否一致,例如PHP和Mysql的编码同时为GBK、或同时为UTF8时,转义函数才能有效地对注入攻击进行防御,
否则存在被绕过的可能
2. 在连接数据库的时候,需要执行以下语句,进行编码统一: //例如你的数据库采用的是GBK的编码,则你的连接代码应该写成类似如下的格式 $mysqli = new mysqli("localhost", "root", "111", "test", 3306); $mysqli->set_charset(gbk); //例如你的数据库采用的是UTF8的编码,则你的连接代码应该写成类似如下的格式 $mysqli = new mysqli("localhost", "root", "111", "test", 3306); $mysqli->set_charset(utf8);

 

4. 避免在SQL中出现永真
永真条件常常被黑客用来进行注入探测,黑客可以利用永真条件的执行情况来判断当前WEB应用是否存在被注入的可能性

 

5. 在SQL中强烈推荐使用参数化查询
对程序中使用到的SQL语句,使用变量绑定的方式进行数据和结构的分离是一种很好的防御SQL注入的方法  
问题代码示例:
<?php
    $id=$_GET[id];
    $conn = mysql_connect("localhost","root","") or die ("wrong!");
    $sel=mysql_select_db("mydb",$conn);
    $sql="select * from user where id = ".id
    $que=mysql_query($sql,$conn);
?> 
修复范例:
<?php
    $id=$_GET[id];
    $conn = mysql_connect("localhost","root","") or die ("wrong!");
    $sel=mysql_select_db("mydb",$conn);
    $sql="select * from user where id = :id"
    $stmt = $conn->prepare($sql); 
    $stmt->execute(array(:id=>$id)); 
?>

 

 

 


5. 案例、场景

1. 不允许访问系统表
合理的SQL语句
1) select * from information_schema.COLUMNS;
这条语句druid认为是合法的,因为这条语句没有注入点的存在,SQL语句本身的唯一目的就是查询系统表,说明用户在进行正常的业务操作
不合理的SQL语句
1) SELECT id
FROM admin
WHERE id = 1
    AND 5 = 6
UNION
SELECT concat(0x5E252421, COUNT(8), 0x2A5B7D2F)
FROM (SELECT `column_name`, `data_type`, `character_set_name`
    FROM `information_schema`.`COLUMNS`
    WHERE TABLE_NAME = 0x73696E6765725F616C62756D
        AND TABLE_SCHEMA = 0x796971696C61695F757466
    ) t
这条语句druid认为是非法的注入攻击,因为SQL在子句(可能是注入点的地方)采取了union拼接,进行了连接系统表的查询的操作

 

2. 不允许访问系统变量
合理的SQL语句
1) select @@basedir;
这条语句druid不做拦截,因为这里没有注入点的存在,也就不可能是黑客的注入攻击,应该归类于业务的正常需要
不合理的SQL语句
2) SELECT * FROM cnp_news where id=23 and len(@@version)>0 and 1=1
这条语句druid会做拦截,攻击者在子句中利用逻辑表达式进行非法的探测注入
 

 

3. 不允许访问系统函数
合理的SQL语句
1) select load_file(\\etc\\passwd);
druid不会拦截这条语句,还是同样的道理,SQL注入的关键在于注入点,这条语句没有注入点的存在,所以只能是用户正常的业务需求
不合理的SQL语句
1) select * from admin where id =(SELECT 1 FROM (SELECT SLEEP(0))A);   
druid会智能地检测出这个敏感函数出现在"where子句节点"中,而"where子句节点"经常被黑客用来当作一个SQL注入点,故druid拦截之

 

4. 禁止永真条件
合理的SQL语句
1) 正常的业务语句
SELECT F1, F2 FROM ADMIN WHERE 1 = 1;        -- 允许
SELECT F1, F2 FROM ADMIN WHERE 0 = 0;        -- 允许
SELECT F1, F2 FROM ADMIN WHERE 1 != 0;        -- 允许
SELECT F1, F2 FROM ADMIN WHERE 1 != 2;        -- 允许
不合理的SQL语句
1) 有攻击性的SQL语句
select * from admin where id =-1 OR 17-7=10;             -- 拦截
select * from admin where id =-1 and 1=2                 -- 拦截
select * from admin where id =-1 and 2>1                 -- 拦截
select * from admin where id =-1 and a!=b               -- 拦截
select * from admin where id =-1 and char(32)>char(31)        -- 拦截
select * from admin where id =-1 and 1 like 1           -- 拦截
select * from admin where id =-1 and 17-1=10              -- 拦截
select * from admin where id =-1 and NOT (1 != 2 AND 2 != 2)    --拦截
select * from admin where id =-1 and id like %%        -- 拦截 
select * from admin where id =-1 and length(abcde) >= 5    -- 拦截

 

 

 


6. 技术FAQ

对任何防御系统来说,在有效拦截和误拦截之前的分界线总是一个不断优化的过程。因此,可能会出现一些本身正常的业务SQL语句,由于触发了我们的拦截规则从而导致被误拦截,以下列出一些可能会被拦截的SQL语句样例:

1. 0x1
1) SQL语句
SELECT COUNT( * ) 
FROM (

SELECT header.id, header.tenant_id, header.create_date header_create_date, header.modify_date header_modify_date, header.supplier_id, 
header_detail.amount header_amount, header_detail.sku_id header_sku_id
FROM purchase_order_header header
INNER JOIN purchase_order_detail header_detail ON ( header.id = header_detail.pid ) 
WHERE approve =1
)header
LEFT JOIN (

SELECT arrive.id arrive_id, arrive.create_date arrive_create_date, arrive.modify_date arrive_modify_date, arrive_detail.qty arrive_qty, 
arrive_detail.sku_id arrive_sku_id, arrive.pid arrive_pid
FROM purchase_arrive_order_header arrive
INNER JOIN purchase_arrive_order_detail arrive_detail ON ( arrive.id = arrive_detail.pid ) 
WHERE approve =1
)arrive ON (
header.id = arrive_pid
AND header_sku_id = arrive_sku_id
)
LEFT JOIN (

SELECT return_detail.amount return_amount, return_detail.qty return_qty, return_header.id return_id, return_header.tenant_id return_tenant_id, 
return_detail.sku_id return_sku_id, return_header.supplier_id return_supplier_id, return_header.create_date return_create_date, 
return_header.modify_date return_modify_date
FROM purchase_return_order_header return_header
INNER JOIN purchase_return_order_detail return_detail ON ( return_header.id = return_detail.pid ) 
WHERE approve =1
)return_header ON (
tenant_id = return_tenant_id
AND supplier_id = return_supplier_id
AND return_sku_id = header_sku_id
)
WHERE 1 =1
OR header_modify_date >=  2014-03-13 07:53:30
OR arrive_create_date >=  2014-03-13 07:53:30
OR return_create_date >=  2014-03-13 07:53:30
OR header_modify_date <  2014-03-13 08:04:00
OR arrive_modify_date <  2014-03-13 08:04:00
OR return_modify_date <  2014-03-13 08:04:00

2) 拦截原因
WHERE 1=1的永真违反了规则
select alway true condition not allow

 

2. 0x2
1) SQL语句
SELECT id, name, IFNULL( parentId, 0 ) parentId, isParent
FROM goods_category
WHERE companyId =17
UNION ALL SELECT 0 ,  所有分类, NULL , NULL 
FROM dual

2) 拦截原因
UNION ALL SELECT 0 ,  所有分类, NULL , NULL FROM dual这句话违反了子查询注入规则
UNION ALL query not contains from clause

 

3. 0x3:   
DELETE FROM tds_permission; 
INSERT INTO tds_permission( id, name, memo, permission_key ) 
VALUES ( 1,  首页, NULL , NULL ) , ( 2,  店铺诊断, NULL , NULL ) , ( 3,  店铺监控, NULL , NULL ) , ( 4,  订单中心, NULL , NULL ) , 
( 5, 营销活动, NULL , NULL ) , ( 6, 促销管理, NULL , NULL ) , ( 7, 客户管理, NULL , NULL ) , ( 8, 系统管理, NULL , NULL ) ; DELETE FROM tb_role; INSERT INTO tb_role( id, name, memo ) VALUES ( 100000, 管理员, NULL ) , ( 100001, 普通用户, NULL ); -- 角色_权限.普通用户比管理员少一个"系统管理"权限 DELETE FROM tb_role_permission; INSERT INTO tb_role_permission( role_id, permission_id ) VALUES ( 100000, 1 ) , ( 100000, 2 ) , ( 100000, 3 ) , ( 100000, 4 ) , ( 100000, 5 ) , ( 100000, 6 ) , ( 100000, 7 ) , ( 100000, 8 ) ,
( 100001, 1 ) , ( 100001, 2 ) , ( 100001, 3 ) , ( 100001, 4 ) , ( 100001, 5 ) , ( 100001, 6 ) , ( 100001, 7 ) ; 2) 拦截原因 2.1) -- 角色_权限.普通用户比管理员少一个"系统管理"权限: SQL语句中出现注释 2.2) 一次SQL请求中包含了多条SQL语句,即堆叠查询,这是黑客常用的攻击方式

WEB第三方应用SQL语句安全规范,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。