II. Phân tích, phòng chống các lỗ hổng Reflected XSS và Stored XSS (tiếp)
Như chúng ta thấy lỗ hổng xảy ra do kẻ tấn công có thể inject mã độc javascript, nếu thực sự không cần thiết, chúng ta có thể kiểm tra và block các dữ liệu nhập từ người dùng chứa các tags và attributes javascript độc hại. Ý tưởng là có thể tạo ra một blacklist các tags và attributes. Ví dụ:
<!DOCTYPE html>
<html>
<body> <form> Enter your name: <input type="text" name="name"><br> <input type="submit" value="Submit">
</form> </body>
</html> <?php
if (isset($_GET['name'])) { // Blacklist of tags and attributes to remove $blacklist = ['script', 'iframe', 'body', 'onclick', 'onresize', 'onload']; // Sanitize user input by removing blacklist tags and attributes $name = preg_replace('/<(?:' . implode('|', $blacklist) . ')([^>]*)>/i', '', $_GET['name']); // Escape user input using htmlspecialchars() function // $name = htmlspecialchars($name); echo "<p>Welcome to Viblo, " . $name . "!</p>";
}
?>
Với blacklist chứa các tags và attributes không được phép sử dụng, trang web kiểm tra và xóa bỏ các từ khóa nguy hiểm thông qua hàm preg_replace()
. Ví dụ kẻ tấn công không còn có thể sử dụng tag <script>
nữa:
Dễ dàng nhận thấy phương pháp ngăn chặn này chưa đạt hiểu quả tối ưu do khối lượng tags và attributes khá lớn nên có thể bỏ sót một số thành phần, và mỗi tags và attributes bỏ sót đó hoàn toàn có thể dẫn tới lỗ hổng XSS. Chẳng hạn, trang web vẫn cho phép sử dụng tag <img>
và attribute onerror
, chúng ta có thể xây dựng payload kích hoạt lỗ hổng Reflected XSS như sau: <img src=1 onerror="alert('XSS')"
Một ví dụ cụ thể về quá trình tìm kiếm các tags, attributes chứa bị block cũng như cách xây dựng payload tại bài lab Reflected XSS into HTML context with most tags and attributes blocked
Trang web chứa chức năng Search, thử tìm kiếm với một payload đơn giản <script>alert(123)</script>
nhận được thông báo Tag is not allowed
Có thể thấy trang web ngăn chặn một số tags. Thực hiện brute force qua Intruder xác định các tags đã bị ngăn chặn.
Danh sách tags được lấy tại https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
Các payload có status trả về là các tags có thể sử dụng, như hình trên chúng ta có body, custom tags. Tiếp theo, thực hiện brute force xác định các attributes có thể sử dụng:
Chúng ta có attribute duy nhất được sử dụng là onresize
, sự kiện này được thực thi khi người dùng thay đổi kích thước giao diện trình duyệt.
Do đó, chúng ta có thể kết hợp thẻ <body>
và sự kiện onresize
xây dựng payload tấn công XSS như sau:
Payload: <body onresize="print()">
Khi thay đổi kích thước giao diện trình duyệt, sự kiện print()
được thi:
Config exploit server để gửi script tấn công tới victim: Sử dụng thẻ <iframe>
với giá trị src
là URL trang web đang chứa script của chúng ta:
Payload: <iframe src="https://0a7000320375cf86c08d185a00f3006d.web-security-academy.net/?search=%3Cbody+onresize%3D%22print%28%29%22%3E">
View exploit để xem kết quả, hàm print()
không được thực thi do không tồn tại thao tác thay đổi kích thước giao diện. Chúng ta có thể thêm sự kiện onload=this.style.width='123px'
để thay đổi kích thước giao diện ngay sau khi giá trị src
được tải.
Payload: <iframe src="https://0a7000320375cf86c08d185a00f3006d.web-security-academy.net/?search=%3Cbody+onresize%3D%22print%28%29%22%3E" onload=this.style.width='123px'>
Gửi tới victim, bài lab hoàn thành:
Một số bài lab có ý tưởng tương tự (tất nhiên cần tính tư duy cao hơn) xin dành cho bạn đọc thử sức:
- Reflected XSS into HTML context with all tags blocked except custom ones
- Reflected XSS with event handlers and href attributes blocked
- Reflected XSS with some SVG markup allowed
Ý tưởng đưa ra blacklist các tags và attributes chưa đạt hiệu quả tốt do chúng có số lượng lớn và khó kiểm soát. Ngoài ra chúng ta có một ý tưởng quen thuộc hơn đó là thực hiện encode các ký tự đặc biệt thường dùng trong payload tấn công XSS.
Chắc hẳn các bạn cũng nghĩ tới các ký tự đầu tiên cần nhắm tới là dấu ngoặc <
và >
(angle brackets). Có thể sử dụng HTML-encode - hàm htmlspecialchars()
để encode input từ người dùng. Hàm htmlspecialchars()
chuyển các ký tự đặc biệt trong HTML thành các mã HTML tương ứng. Cú pháp:
htmlspecialchars(string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true]]]) : string
Ví dụ trong trường hợp ngăn chặn tấn công XSS:
<!DOCTYPE html>
<html>
<body> <form> Enter your name: <input type="text" name="name" value="<?php echo htmlspecialchars($_GET['name'], ENT_NOQUOTES); ?>">
<br> <input type="submit" value="Submit">
</form> <?php
if (isset($_GET['name'])) { // Encode angle brackets using htmlspecialchars() function with ENT_NOQUOTES flag $name = htmlspecialchars($_GET['name'], ENT_NOQUOTES); echo "<p>Welcome to Viblo, " . $name . "!</p>";
}
?> </body>
</html>
Chúng ta không còn có thể sử dụng các payload chứa <
và >
nữa:
Kết quả encode có thể được thấy rõ hơn trong source code:
Tuy nhiên, chú ý rằng trang web chỉ encode các ký tự <
và >
, đồng thời input người dùng xuất hiện trong thuộc tính "value" - điều này giúp trang web giữ lại kết quả nhập trong ô input.
Đồng nghĩa với việc kẻ tấn công có thể tùy ý thêm các thuộc tính trong thẻ <input>
này. Thật vậy, chẳng hạn có thể tận dụng thuộc tính onmouseover
để kích hoạt thông báo alert.
Ý tưởng như sau: đầu tiên đóng giá trị value bằng "
, thêm thuộc tính onmouseover
và sự kiện alert('XSS')
, cuối cùng không cần đóng lại dấu nháy "
cho sự kiện alert này do source code đã chứa dấu nháy đóng. (Thực tế nếu đóng lại cũng không ảnh hưởng)
Payload: " onmouseover="alert('XSS')
Script được inject vào đoạn code:
Bạn đọc có thể luyện tập tình huống này trong bài lab Reflected XSS into attribute with angle brackets HTML-encoded.
Để ngăn chặn hành vi tấn công này, chúng ta không nên chủ quan chỉ thực hiện encode các ký tự <
và >
, thay vào đó cần encode tất cả ký tự nhạy cảm, đồng thời hạn chế input từ người dùng "chen" vào trong các thẻ. Ví dụ đoạn code tối ưu việc encode:
<!DOCTYPE html>
<html>
<body> <form> Enter your name: <input type="text" name="name">
<br> <input type="submit" value="Submit">
</form> <?php
if (isset($_GET['name'])) { // Encode angle brackets using htmlspecialchars() function $name = htmlspecialchars($_GET['name']); echo "<p>Welcome to Viblo, " . $name . "!</p>";
}
?> </body>
</html>
Vậy thì, câu hỏi Viblo đặt ra cho các bạn là: "Trang web đã thực sự an toàn trước tấn công XSS khi sử dụng htmlspecialchars()
thực hiện encode input người dùng?". Cùng Viblo trả lời ở phần sau nhé!