Sinh vien hoa sen


Login - Đăng Nhập
Cũ 07-04-2008, 10:53 PM   #1 (permalink)
Profile - Thông Tin Cá Nhân
123... trốn
 
Avatar của hiepgia
 
Mặc định SQL Injection – Nguy hiểm luôn rình rập

Trong phần này, tôi chỉ bàn đến lỗi trong MySQL khi sử dụng với PHP. Đây là lỗi bảo mật nguy hiểm chỉ đứng sau XSS về số lượng lỗi bị khai thác.
Để thuận thiện cho quá trình tìm hiểu và phân tích, chúng ta sẽ tạo một database với tên testsql001 và sử dụng kết hợp Command để quản trị MySQL
CREATE DATABASE testsql001;
Sau đây là các lỗi có thể bị khai thác: * Đăng nhập tài khoản trái phép: Bạn hãy import SQL sau vào database tên là testsql001





Mã:
  1. CREATE TABLE `user` (
  2. `id` INT(10) NOT NULL AUTO_INCREMENT,
  3. `name` VARCHAR(128) NOT NULL DEFAULT '',
  4. `pass` VARCHAR(128) NOT NULL DEFAULT '',
  5. PRIMARY KEY (`id`)
  6. ) TYPE=MyISAM;
  7. INSERT INTO `user` VALUES (1, 'admin', 'adminpass');
  8. INSERT INTO `user` VALUES (2, 'user', 'userpass');
Tiếp tục tạo script PHP như sau: testsql.php





Mã:
  1. <?php
  2. if(isset($_GET['name']) || isset($_GET['pass'])) {
  3. $dbserver = "localhost";
  4. $dbuser = "root";
  5. $dbpw = "";
  6. $dbname = "testsql001";
  7. mysql_connect($dbserver,$dbuser,$dbpw) OR die ('Could not connect: '.mysql_error());
  8. mysql_select_db($dbname) OR die('Could not select database');
  9. $query = "SELECT * FROM user WHERE name='{$_GET['name']}' AND pass='{$_GET['pass']}'";
  10. $result = mysql_query($query)/* OR die('Query failed: ' . mysql_error())*/;
  11. $user = mysql_fetch_assoc($result);
  12. if (isset($user) && !empty($user))
  13. {
  14. echo 'Welcome : '.$user['name'];
  15. } else {
  16. echo 'Wrong!!!';
  17. }
  18. echo '<p>Debug Query:<br /><b>'.$query.'</b><p>';
  19. } else {
  20. echo 'Welcome Guest';
  21. }
Vì lý do bảo mật, bạn hãy thay RO thành OR[ thành dấu nháy (') trong các câu lệnh SQL
Thử vào trang với URL:
URL 1 : http://localhost/SECURITY/testsql.php?name=admin[ RO [1=1 <= hợp lệ
URL 2 : http://localhost/SECURITY/testsql.php?name=admin[ RO 1=1--[ <= hợp lệ
URL 3 : http://localhost/SECURITY/testsql.php?name=admin[ RO 1=1 <= không hợp lệ
Lúc này query sẽ là:


URL 1 : SELECT * FROM user WHERE name='admin' RO [1=1[ AND pass=[[
URL 2 : SELECT * FROM user WHERE name='admin' RO 1=1--[[ AND pass=[[
URL 3 : SELECT * FROM user WHERE name='admin' RO 1=1[ AND pass=[[
Khi có biểu thức 1=1 hoặc -- thì các cậu lệnh SQL phía sau (AND pass=) xem như không có tác dụng. Khi đó, trang sẽ hiện ra:







Đây là những kí tự để chú thích cho câu lệnh SQL, vì vậy, phần SQL phía sau đó xem như không có tác dụng. Đấy thăng # ở đây không hợp lệ vì nó dùng để trỏ tới một anchor trên trang chứ không được gửi tới query, %23 là mã hóa của #, nó hợp lệ để gửi đi.


Như vậy, hacker có thể đăng nhập hợp pháp với bất cứ tài khoản nào có trong CSDL mà không cần biết mật khẩu.
CODE test-search.php





Mã:
  1. <?php
  2. set_magic_quotes_runtime(FALSE);
  3. if(isset($_GET['kw'])) {
  4. $dbserver = "localhost";
  5. $dbuser = "root";
  6. $dbpw = "";
  7. $dbname = "testsql001";
  8. mysql_connect($dbserver,$dbuser,$dbpw) OR die ('Could not connect: '.mysql_error());
  9. mysql_select_db($dbname) OR die('Could not select database');
  10. $query = "SELECT * FROM user WHERE name LIKE '%{$_GET['kw']}%' OR pass LIKE '%{$_GET['kw']}%'";
  11. $result = mysql_query($query)/* OR die('Query failed: ' . mysql_error())*/;
  12. while($s = mysql_fetch_assoc($result)) {
  13. $r[] = $s;
  14. }
  15. if (isset($r) && !empty($r)) {
  16. foreach($r as $rs) {
  17. echo $rs['name'].' - '.$rs['pass'].'<br />';
  18. }
  19. } else {
  20. echo 'Not found!!!';
  21. }
  22. echo '<p>Keyword: '.$_GET['kw'].'<br />Debug Query: <b>'.$query.'</b><p>';
  23. } else {
  24. echo 'Search me!';
  25. }
Khi ta cho từ khóa (kw) là % và _ thì tất cả dữ liệu trong bảng user sẽ được hiển thị



Nếu dữ liệu nhiều, hacker có thể gửi liên tục URL này, rất có thể server của bạn sẽ quá tải.

Chữ Ký Của Webmaster hiepgia Thanks nếu bạn thấy hay ^^!
I'm Pinky's Husband ^^!
Quy định về chữ ký: http://www.sinhvienhoasen.com/forum/...ad.php?t=13396. Đề nghị các Admin, Smod thực hiện...
hiepgia đang ẩn  
Lần đọc: 395
Trả lời với trích dẫn
Cũ 07-04-2008, 11:13 PM   #2 (permalink)
Profile - Thông Tin Cá Nhân
123... trốn
 
Avatar của hiepgia
 
Mặc định

  • Blind SQL Injection:
Sửa $query trong testsql.php thành:
$query = "SELECT * FROM user WHERE name='{$_GET['name']}'";
Kỹ thuật này là cách dò tìm dựa vào các lỗi SQL Injection và so sánh giá trị trong SQL. Sau đây ra sẽ thử dò xem mật khẩu của tài khoản admin là gì. Đầu tiên sẽ tìm độ dài của mật khẩu bằng hàm LENGTH của MySQL URL:



Như vậy mật khẩu dài 9 kí tự, tiếp theo sẽ dò từng kí tự trong mật khẩu, MySQL hỗ trợ các hàm: LEFT, RIGHT và MID (dev.mysql.com/doc/refman/5.0/en/string-functions.html) để tìm vị trí của kí tự trong chuỗi nào đó.
Tìm kí tự đầu tiên, tham số thứ 2 trong hàm LEFT là độ dài kí tự muốn lấy ra:

http://localhost/SECURITY/testsql.php?name=admin[%20and%20LEFT(pass,1)=[p <= Saihttp://localhost/SECURITY/testsql.php?name=admin[%20and%20LEFT(pass,1)=[a <= Đúng


Tiếp tục tìm kí tự thứ hai trong mật khẩu, ta đã biết được kí tự đầu tiên là "a":
Tương tự cho đến 9 kí tự:

http://localhost/SECURITY/testsql.php?name=admin[%20and%20LEFT(pass,9)=[adminpass





Như vậy, hacker đã biết được chính xác mật khẩu của tài khoản admin. Các hacker thường viết ra công cụ tự dò tìm, tốc độ rất nhanh, vì vậy bạn đừng nghĩ rằng cách này tốn thời gian vô ích!
  • Sửa dụng câu lệnh gộp (UNION Query):
Đấy là cách SQL Injection khá thông dụng cho ASP và MSSQL, đối với MySQL cũng có thể bị khai thác tương tự. Đây là lỗi SQL Injection thường được khai thác nhất vì hiệu quả cao.
Chạy SQL sau để tạo bảng post:

Mã:
  1. CREATE TABLE `post` (
  2. ` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  3. `title` TEXT NOT NULL ,
  4. `TEXT` TEXT NOT NULL
  5. ) ENGINE = MYISAM;
  6. INSERT INTO `testsql001`.`post` VALUES (NULL , 'Bai viet 1', 'Noi dung bai viet 1'), (NULL , 'Bai viet 2', 'Noi dung bai viet 2');
Tạo file union.php như sau:


Mã:
  1. set_magic_quotes_runtime(FALSE);
  2. if(isset($_GET['id'])) {
  3. $dbserver = "localhost";
  4. $dbuser = "root";
  5. $dbpw = "";
  6. $dbname = "testsql001";
  7. mysql_connect($dbserver,$dbuser,$dbpw) or die ('Could not connect: '.mysql_error());
  8. mysql_select_db($dbname) or die('Could not select database');

Chữ Ký Của Webmaster hiepgia Thanks nếu bạn thấy hay ^^!
I'm Pinky's Husband ^^!
Quy định về chữ ký: http://www.sinhvienhoasen.com/forum/...ad.php?t=13396. Đề nghị các Admin, Smod thực hiện...
hiepgia đang ẩn  
Lần đọc: 395
Trả lời với trích dẫn
Cũ 07-04-2008, 11:25 PM   #3 (permalink)
Profile - Thông Tin Cá Nhân
123... trốn
 
Avatar của hiepgia
 
Mặc định

Sau đó, ta chạy thử với URL như sau:
URL: http://localhost/SECURITY/union.php?id=1' union select 1,name,pass from user %23'
Query: SELECT * FROM post WHERE id='1' union select 1,name,pass from user #''


Chưa có gì đặc biệt, ta thử trong Command xem sao:



Kết quả ta thấy câu lênh lấy ra được 3 rows, nhưng vì trong mã PHP, ta chỉ lấy một kết quả đầu tiên. Ta thử sửa lại mã như sau:


Mã:
  1. while($post = mysql_fetch_assoc($result)) {
  2. echo 'Title : <b>'.$post['title'].'</b><br />';
  3. echo 'Text : '.$post['text'].'<br />';
  4. }
Kết quả ta được:



Như vậy tài khoản trong bảng user đã bị lộ. Trở lại với trường hợp trang chỉ có ra một kết quả, mặc dù tập kết quả trả về từ query vẫn là 3, nhưng khi hiện ra trang chỉ lấy kết quả đầu tiên, nên hacker chưa thể có được kết quả trong bảng user. Ta thử với URL sau:

http://localhost/SECURITY/union.php?id=-1' union select 1,name,pass from user %23'
Kết quả ta được khi lấy toàn bộ kết quả:


Khi lấy 1 kết quả:





Khi số id ở SELECT đầu tiên không tồn tại, tức kết quả trả về là rỗng, khi đó kết quả sẽ chỉ có ở SELECT thứ hai. Union dùng để gộp query với các bảng có số trường bằng nhau. Ta thử thêm một trường nữa vào bảng post:

Mã:
ALTER TABLE `post` ADD `ORDER` VARCHAR( 100 ) NOT NULL AFTER `TEXT` ;
Lúc này, bảng post đã có số trường khác với bảng user, ta chạy thử câu query ban đầu:
Mã:
SELECT * FROM post WHERE id='-1' UNION SELECT id,name,pass FROM user #''
Ngay lập tức, MySQL sẽ báo lỗi hai bảng không có số trường bằng nhau:



Nhưng hãy thử bằng query sau đây:

Mã:
SELECT * FROM post WHERE id='-1' UNION SELECT id,name,pass,1 FROM user #''
Kết quả đã lấy được dữ liệu từ bảng user một cách hợp lệ mà không phải gặp thông báo lỗi:



Từ đây ta rút ra được, đừng nên nghĩ rằng hai bảng không có số trường giống nhau thì sẽ không cho ra kết qua khi dùng Union!
Ngoài việc lấy dữ liệu, hacker còn có thê khai thác qua các lệnh INSERT và UPDATE để thêm mới hoặc cập nhật dữ liệu trái phép như thêm tài khoản mới với quyền quản trị, cập nhật từ người dùng bình thường thành quản trị. Hacker còn có thể lấy các thông tin về máy chủ CSDL bằng các hàm như: DATABASE(),USER(),SYSTEM_USER(). Nguy hiểm với dữ liệu hơn là có thể dùng lệnh DROP để xóa đi một bảng nào đó!
SQL Injection Cheat Sheet: ferruh.mavituna.com/makale/sql-injection-cheatsheet

Chữ Ký Của Webmaster hiepgia Thanks nếu bạn thấy hay ^^!
I'm Pinky's Husband ^^!
Quy định về chữ ký: http://www.sinhvienhoasen.com/forum/...ad.php?t=13396. Đề nghị các Admin, Smod thực hiện...
hiepgia đang ẩn  
Lần đọc: 395
Trả lời với trích dẫn
Cũ 07-04-2008, 11:31 PM   #4 (permalink)
Profile - Thông Tin Cá Nhân
123... trốn
 
Avatar của hiepgia
 
Mặc định

Cách chống SQL Injection:
“Hiểm họa” SQL Injection nằm ở vấn để nhập giá trị cùng dấu nháy đơn (') nhằm làm thay đổi câu query để thực hiện hành động không đúng mục đích. Cách giải quyết dấu nháy đơn là thêm kí tự thoát chuỗi để xem dấu nháy như là kí tự trong giá trị chứ không có tác dụng với cả query. PHP cung cấp sẵn giải pháp:
'''mysql_real_escape_string''' — Escapes special characters in a string for use in a SQL statement '''addslashes''' — Quote string with slashes
Bạn nên dùng hàm mysql_real_escape_string để kiểm qua các giá trị nhận được trước khi đưa vào câu query, các chuỗi kí tự nên được lọc qua hàm này. Nếu có thể, hãy lọc hết các kí tự nhạy cảm như: ',",<,>,*,/,-,#,( và )


Mã:
  1. $name = mysql_real_escape_string($_GET['name']);
  2. $pass = mysql_real_escape_string($_GET['pass']);
  3. $query = "SELECT * FROM user WHERE name='{$name}' AND pass='{$pass}'";
  4. //Kết quả: "SELECT * FROM users WHERE name='\' OR \'1\'=\'1'"
Hoặc bạn có thể dùng hàm bên dưới để có thể xử lý phù hợp hơn:


Mã:
  1. function sql_quote( $value ){
  2. if( get_magic_quotes_gpc() ){
  3. $value = stripslashes( $value );
  4. }
  5. //check if this function exists
  6. if( function_exists( "mysql_real_escape_string" ) ){
  7. $value = mysql_real_escape_string( $value );
  8. } else { //for PHP version < 4.3.0 use addslashes
  9. $value = addslashes( $value );
  10. }
  11. return $value;
  12. }
PHP hỗ trợ sẵn magic_quotes_gpc để tự động thoát chuỗi cho các giá trị gửi qua $_GET, $_POST, $_COOKIE, nhưng bạn nên đặt nó là OFF vì chức năng này đã được loại bỏ trong PHP6. Một điều lưu ý khi thoát chuỗi là khi xuất dữ liệu ra trang, bạn nên loại bỏ các kí tự thoát chuỗi để hiện thị đúng với văn bản được nhập:


Mã:
  1. $text = 'SELECT * FROM post WHERE id=\'-1\'...';
  2. $text = stripslashes( $text );
  3. echo $text;
  4. // Output:
  5. // SELECT * FROM post WHERE id='-1'...
Đối với những giá trị là số nguyên như số ID chẳng hạn. Cách tốt nhất là kiểm xem nó có phải là số hay không, tất nhiên là PHP hỗ trợ hàm để làm điều đó:


Mã:
  1. $id = intval($_GET['id']); // tự động chuyển sang kiểu int
  2. //tương tự như ép kiểu (int) $_GET['id']
  3. //... hoặc
  4. if(is_numberic($_GET['id'])) {
  5. // hợp lệ
  6. }

Chữ Ký Của Webmaster hiepgia Thanks nếu bạn thấy hay ^^!
I'm Pinky's Husband ^^!
Quy định về chữ ký: http://www.sinhvienhoasen.com/forum/...ad.php?t=13396. Đề nghị các Admin, Smod thực hiện...
hiepgia đang ẩn  
Lần đọc: 395
Trả lời với trích dẫn
Cũ 07-04-2008, 11:40 PM   #5 (permalink)
Profile - Thông Tin Cá Nhân
123... trốn
 
Avatar của hiepgia
 
Mặc định

Trường hợp khi xử lý tìm kiếm, vì mysql_real_escape_string không lọc các kí tự %_ nên bạn có thể lọc bằng cách sau (thoát chuỗi thủ công):


Mã:
  1. $keywords = $_GET['kw'];
  2. $keywords = str_replace("_","\_",$keywords);
  3.    $keywords = str_replace("%","\%",$keywords);
Để hạn chế thêm, bạn có thể dùng Stored Procedure trong MySQL 5 (dev.mysql.com/doc/refman/5.0/en/stored-procedures.html). Tuy nhiên, cách tốt nhất để chống SQL Injection là dùng prepared statement được hỗ trợ trong PHP5 kèm theo phần mở rộng MySQLi (MySQL Improved) bạn có thể xem chi tiết tại: php.net/mysqli


$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');


/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
$stmt->bind_param('sssd', $code, $language, $official, $percent);

$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;

/* execute prepared statement */
$stmt->execute();

printf("%d Row inserted.\n", $stmt->affected_rows);

/* close statement and connection */
$stmt->close();

/* Clean up table CountryLanguage */
$mysqli->query("DELETE FROM CountryLanguage WHERE Language='Bavarian'");
printf("%d Row deleted.\n", $mysqli->affected_rows);


/* close connection */$mysqli->close();
Nếu bạn dùng các CSDL khác ngoài MySQL, thì PHP cũng cung cấp PDO (PHP Data Objects) để hỗ trợ các CSDL khác như MSSQL, Oracle, DB2... và tất nhiên là hỗ trợ cả MySQL. PDO cũng hỗ trợ prepared statement để bạn sử dụng. Xem chi tiết: php.net/PDO


  1. $stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
  2. if ( $stmt->execute( array($_GET['name']) ) ) {
  3. while ($row = $stmt->fetch()) {
  4. print_r($row);
  5. }
  6. }
Một số thủ thuật khác để chống SQL Injection nếu bạn không thể sử dụng prepared statement:
- Giới hạn số kí tự tối đa: Các dữ liệu như số ID, tên tài khoản, mật khẩu dùng để khai thác lỗi SQL Injection thường không dài lắm so với câu query không hợp pháp dùng để “tiêm” SQL. Vì vậy, bạn nên giới hạn số kí tự tối đa nếu có thể. Ví dụ tên tài khoản và mật khẩu không được dài quá 32 kí tự.
- Tắt thông báo lỗi: Thông thường, để khai thác lỗi SQL Injection, việc đầu tiên hacker phải làm là kiểm tra xem câu truy vấn có bị lỗi hay không. Khi có lỗi xảy ra, MySQL sẽ ném ra lỗi với thông tin chi tiết về lỗi đó, trong đó có cả thông tin về bảng bị lỗi. Chính từ những thông báo lỗi này, hacker có thể dễ dàng khai thác SQL Injection hơn. Vì vậy, tốt nhất là bạn hãy tắt thông báo lỗi trong PHP:


  1. ini_set('display_errors',0);
    // hoặc
  2. error_reporting(0);
Để thuận tiện cho việc sửa lỗi nhưng vẫn đảm bảo không tiết lộ thông tin khi bị lỗi, bạn nên dùng set_error_handler. Xem chi tiết: php.net/set_error_handler
- Giới hạn quyền truy cập CSDL: Đừng nên dùng tài khoản có quyền quản trị cao nhất để truy cập database cho ứng dụng web. Thay vào đó, bạn nên tạo tài khoản khác với những quyền hạn phù hợp và nên giới hạn các quyền hạn liên qua tới hệ thống đến mức có thể. Lấy ví dụ, website chỉ đọc từ CSDL, bạn chỉ cần cho tài khoản MySQL có quyền SELECT. Nếu người dùng có hành động đăng nhập tài khoản, thì chỉ nên giới hạn quyền truy cập vào bảng user, các bảng khác không liên quan thì không thể truy cập được.

Chữ Ký Của Webmaster hiepgia Thanks nếu bạn thấy hay ^^!
I'm Pinky's Husband ^^!
Quy định về chữ ký: http://www.sinhvienhoasen.com/forum/...ad.php?t=13396. Đề nghị các Admin, Smod thực hiện...
hiepgia đang ẩn  
Lần đọc: 395
Trả lời với trích dẫn
Trả lời

Công cụ bài viết
Kiểu hiển thị

Quyền viết bài
Bạn không thể gửi chủ đề mới
Bạn không thể gửi trả lời
Bạn không thể gửi file đính kèm
Bạn không thể sửa bài viết của mình

BB code đang Mở
Mặt cười đang Mở
[IMG] đang Mở
HTML đang Tắt
Trackbacks are Mở
Pingbacks are Mở
Refbacks are Mở


Address: Hoa Sen University, Ho Chi Minh City, Viet Nam.
Designed by hiepgia.
Best Viewed in 1024x768 with IE6+, Firefox 1.5+, Chrome, Opera 9+.
Truy cập vào Website, nghĩa là bạn đã chấp nhận Quy Định của Forum!
RSS RSS 2.0 XML MAP

Valid XHTML 1.0 Transitional Valid CSS!
vBulletin v3.8.4 Licensed & Copyright © 2008 - 2009, Hoa Sen University.