Trang

Thứ Năm, 18 tháng 7, 2013

Chống lỗi SQL_Injection [Bảo mật]

Cách 1: Ẩn thông báo lỗi MySQL

Cách này được sử dụng để tắt các thông báo lỗi khi các tác vụ liên quan đến việc truy vấn cơ sở dữ liệu gặp sự cố. Nó không có tác dụng ngăn chặn việc tấn công.


Trong PHP:

1. Thêm @ trước các hàm dễ gây lỗi
Ví dụ:
while( $row = @mysql_fetch_array($tin) ){}

2. Thêm error_reporting(0) ở đầu đoạn code PHP
Nếu muốn hiển thị lỗi thì thay đổi 0 thành -1

Cách 2: Loại bỏ dấu nháy đơn ('), nháy kép ("), dấu (\) trong sql injection

Đây là cách ngăn chặn SQL_Injection hiệu quả mà đơn giản.

Trong PHP:

Ví dụ
Ta sẽ lấy tham số id trên thanh địa chỉ làm tham số truy vấn
Url: http://localhost/baomatweb_sql/vidu6.php?id=1
if( isset($_GET['id']) )
   {
      $id=$_GET['id'];
  }else{
      $id=1;
          }
$select = "SELECT * FROM tin WHERE idTin = $id "
$qr = mysql_query($select)

Nếu bây giờ Hacker thêm dấu (') vào sau số 1: http://localhost/baomatweb_sql/vidu6.php?id=1'  

Thì sẽ gặp thông báo lỗi:
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in C:\AppServ\www\baomatweb_sql\vidu6.php on line 62

Với câu thông báo lỗi này thì chúng ta bị SQL Injection

Phòng tránh:

1. Khi đưa tham số vào câu truy vấn, luôn luôn để tham số trong dấu nháy đơn.

Có nháy đơn:
$select = "SELECT * FROM tin WHERE idTin =  '$id'  "
Không nháy đơn:
$select = "SELECT * FROM tin WHERE idTin = $id "

Với câu truy vấn không nháy đơn, sẽ tiềm ẩn lỗi SQL Injection.

Nếu bây giờ Hacker thêm vào câu truy vấn một đoạn như sau:
id=1 AND 1=2 UNION SELECT 1,2,3,4--
thì câu truy vấn sẽ thành:

Có nháy đơn:
$select = "SELECT * FROM tin WHERE idTin =  '1 AND 1=2 UNION SELECT 1,2,3,4--'  "
Không nháy đơn:
$select = "SELECT * FROM tin WHERE idTin = 1 AND 1=2 UNION SELECT 1,2,3,4-- "

Rõ ràng với câu truy vấn có nháy đơn, thì nguyên một đoạn đó trở thành tham số của idTin. Do vậy bước đầu tiên đã ngăn được 1 phần của việc tấn công. Còn với truy vấn không có nháy đơn, đoạn chèn thêm sẽ hợp Logic của câu truy vấn và Hacker sẽ lấy được thông tin cần thiết.

2. Escape các dấu nháy đơn, nháy kép, dầu (\).

Trường hợp các tham số trong câu truy vấn có dấu nháy đơn như trên thì Hacker vẫn có thể tấn công được bằng cách thêm đoạn như sau:

id=1' AND 1=2 UNION SELECT 1,2,3,4--

Như vậy câu truy vấn sẽ thành:
$select = "SELECT * FROM tin WHERE idTin =  '1' AND 1=2 UNION SELECT 1,2,3,4--'  "

Dấu nháy đơn Hacker thêm vào có tác dụng đóng dấu nháy đơn phía trước, dấu (--) phía cuối có tác dụng biến các câu truy vấn sau nó thành chú thích. Do vậy mà câu truy vấn trên trở thành hợp lệ.

Ta để ý thấy rằng Hacker nhất định sẽ phải thêm vào dấu (') để biến 1 thành tham số, còn đoạn phía sau trở thành một mệnh đề Logic. Từ suy nghĩ đó, ta sẽ vô hiệu hóa dấu (') mà Hacker thêm vào để không cho đoạn đó trở thành một mệnh đề Logic. Để vô hiêu hóa dấu (') ta chỉ việc thêm dấu (\) trước dấu nháy đơn đó.

$select = "SELECT * FROM tin WHERE idTin =  '1\' AND 1=2 UNION SELECT 1,2,3,4--'  "

Trong PHP có nhiều hàm giúp ta thêm dấu (\) trước dấu nháy đơn trước khi đưa vào truy vấn như: addslaches, mysql_real_escape_string. Ngoài ra còn có chế độ magic_quotes_gpc() giúp ta escape khi $_GET[] hay $_POST[] dữ liệu.

Tuy nhiên để sử dụng hợp lý tất cả các hàm trên ta nên xây dựng một function sql_escape kết hợp tất cả lại
function sql_escape($value)
  {
      
      if(get_magic_quotes_gpc())
        {
            $value=stripcslashes($value);    
        }
        
      if(function_exists("mysql_real_escape_string"))
       {
           $value=mysql_real_escape_string($value);   
       }else{
              $value=addslashes($value);
            }
     return $value;    
     
     /** 
       - Phải dùng câu truy vấn giống thế này mới phát huy hàm này 
         $qr2="SELECT * FROM tin WHERE idLT= '".$id."'";
         Hoặc
         $qr2="SELECT * FROM tin WHERE idLT= '$id' ";
       - Tuyệt đối không sử dụng câu truy vấn như thế này
         $qr2="SELECT * FROM tin WHERE idLT= $id ";
      Tóm lại; tất cả các tham số khi truy vấn phải để trong nháy đơn.     
     **/        
  }

Sử dụng hàm sql_escape: trước khi sử dụng ta require hàm đó vào trang ta đang muốn dùng

require "sql_escape.php";

if( isset($_GET['id']) )
   {
      $id=sql_escape($_GET['id']);
  }else{
      $id=1;
          }
$select = "SELECT * FROM tin WHERE idTin = '$id' "
$qr = mysql_query($select)

Cách 3: Ép kiểu dữ liệu

Cách này chỉ có tác dụng khi dữ liệu lấy về là số, ví dụ như id=123

Trong PHP

  Cú pháp: settype($value,"int");

if( isset($_GET['id']) )
   {
      $id= $_GET['id']);
      settype($id,"int");
  }else{
      $id=1;
          }
$select = "SELECT * FROM tin WHERE idTin = '$id' "
$qr = mysql_query($select)

Trong JQuery

Thường khi truyền tham số trong JQuery ta hay truyền trong Ajax. Do vậy để đảm bảo ta cũng nên bảo mật trong JQuery.

  Cú pháp: id = parseInt(id)

<script>
$(document).ready(function(){
   var id = 1.2;   
   id = parseInt(id);
   alert(id);   
});
</script>
Kết quả: id=1

Cách 4: Sử dụng blacklist chứa những từ khóa nguy hiểm

Ta sẽ sử dụng một danh sách chứa những từ khóa nguy hiểm. Những từ trong câu truy vấn của chúng ta sẽ được so sánh với những từ khóa trong danh sách này. Nếu có từ khóa nguy hiểm nào được phát hiện thì không cho Query, ngược lại thì được truy vấn.

function sql_blacklist($qr)
{
    // Chuyển câu truy vấn thành chữ hoa
    $strToUpper = strtoupper($qr);
    $blackList=array(
                     "UNION","INFORMATION","SCHEMA",
                     "DELETE","INSERT","DROP","--"
                    );
    for($i=0;$i<count($blackList);$i++)
     {
          $blackKey=$blackList[$i];
          // Nếu có từ khóa được phát hiện thì trả về vị trí của nó
          $pos=strpos($strToUpper,$blackKey);
          if( $pos > 0)
           {
              return true;
           }
     }
     
     return false;
}

Sử dụng hàm sql_blacklist: trước khi sử dụng ta require hàm đó vào trang muốn dùng
require "sql_blacklist.php";
 
if( isset($_GET['id']) )
   {
       $id=$_GET['id'];
    }else{
             $id=1;
            }

$qr2="SELECT * FROM tin WHERE idLT= '$id' ";

if( sql_blacklist($qr2) == false )
    { // Không chứa từ khóa nguy hiểm
         // Query       
    }else{
              // Chứa từ khóa nguy hiểm thì chuyển về trang nào đó
              header("Location:index.php");
            }

6 nhận xét: