I. Mở đầu
DOM (Document Object Model) là một mô hình lập trình của trang web, cho phép các ngôn ngữ lập trình như JavaScript truy cập và thay đổi các phần tử trong HTML hoặc XML của trang web theo cách động (dynamic). Kỹ thuật này tạo ra một cấu trúc cây, trong đó mỗi phần tử HTML hoặc thuộc tính là một nút trong cây đó, giúp JavaScript có thể thay đổi nội dung, cấu trúc hoặc phong cách của trang web mà không cần tải lại trang.
DOM-based vulnerability là một dạng lỗ hổng bảo mật phát sinh từ việc sử dụng JavaScript trong DOM mà không kiểm soát đúng cách các dữ liệu đầu vào. Các lỗ hổng này xảy ra khi ứng dụng web tin tưởng vào dữ liệu không được xác thực (có thể từ URL, đầu vào người dùng, hoặc các nguồn bên ngoài) và sử dụng chúng để thay đổi cấu trúc DOM mà không kiểm tra, dẫn đến hành vi không mong muốn, chẳng hạn như tấn công XSS (Cross-Site Scripting).
Ví dụ: Nếu một ứng dụng web sử dụng dữ liệu từ URL để thay đổi nội dung trang mà không kiểm tra hay làm sạch dữ liệu, kẻ tấn công có thể chèn mã JavaScript độc hại vào URL, gây nguy hiểm cho người dùng khi họ truy cập vào liên kết đó.
II. Một số khái niệm quan trọng
1. Taint-flow vulnerabilities
Lỗ hổng dạng trên còn được biết tới với một cái tên khác là Taint-flow vulnerabilities:
- Taint: Dữ liệu được coi là "tainted" (bị nhiễm độc) khi nó đến từ một nguồn không đáng tin cậy hoặc chưa được kiểm tra. Ví dụ, các đầu vào từ người dùng, cookies, tham số trong URL, v.v., có thể là các nguồn tainted.
- Taint Flow: Taint flow mô tả quá trình mà dữ liệu tainted "chảy" qua các phần khác nhau của ứng dụng mà không bị kiểm tra hoặc làm sạch, từ đó có thể dẫn đến việc lây lan lỗ hổng bảo mật, đặc biệt là các loại tấn công như XSS, SQL Injection, hoặc Command Injection.
2. Sources và sinks
Sources và Sinks là hai khái niệm quan trọng trong việc phân tích và hiểu rõ các lỗ hổng bảo mật, đặc biệt là trong các loại tấn công liên quan đến Taint-flow vulnerabilities.
2.1. Sources (Nguồn)
Source là bất kỳ điểm nào trong ứng dụng web mà dữ liệu không đáng tin cậy (tainted data) xuất hiện hoặc được nhập vào. Những nguồn này có thể là bất kỳ đầu vào nào mà người dùng hoặc hệ thống bên ngoài có thể thay đổi mà không qua kiểm tra hoặc làm sạch đúng cách.
Thông thường, Sources có thể bao gồm:
- Dữ liệu từ người dùng: Dữ liệu đầu vào qua các biểu mẫu (forms), ô nhập liệu (input fields), URL query parameters, hoặc cookies.
- Dữ liệu từ hệ thống bên ngoài: Ví dụ như thông tin từ API, truy vấn GET hoặc POST, hoặc thậm chí từ cơ sở dữ liệu không đáng tin cậy.
- Dữ liệu từ các đối tượng không xác định: Đôi khi, dữ liệu đến từ các đối tượng không rõ ràng hoặc có thể bị thay đổi bởi kẻ tấn công, ví dụ như HTTP headers hoặc referrer.
Ví dụ về Sources:
- URL parameters: Một tham số URL như
?userInput=<script>alert('xss')</script>
có thể là nguồn của dữ liệu tainted. - Form inputs: Một trường nhập liệu trên trang web, chẳng hạn như
username
, có thể chứa dữ liệu độc hại nếu không được kiểm tra. - Cookies: Nếu cookies được gửi từ phía người dùng và không được xác thực, dữ liệu từ cookies có thể trở thành nguồn tainted.
- HTTP headers: Dữ liệu từ headers như User-Agent, Referer, có thể bị tấn công hoặc giả mạo.
2.2. Sinks (Điểm đích)
Sink là những điểm trong ứng dụng mà dữ liệu tainted được sử dụng hoặc tiến hành thao tác nguy hiểm, mà không được kiểm tra hoặc làm sạch một cách thích hợp. Đây là những vị trí mà nếu dữ liệu tainted đến đó mà không qua xử lý, có thể dẫn đến việc thực thi mã độc hoặc các lỗ hổng bảo mật.
Sinks có thể là:
- Câu lệnh SQL: Khi dữ liệu tainted được đưa vào một câu truy vấn SQL mà không được tham số hóa, có thể dẫn đến SQL Injection.
- HTML hoặc JavaScript: Khi dữ liệu tainted được chèn trực tiếp vào trang web mà không được mã hóa, có thể gây ra XSS (Cross-Site Scripting).
- Lệnh hệ thống: Khi dữ liệu tainted được đưa vào lệnh hệ thống (chẳng hạn như exec() trong PHP, eval() trong JavaScript), có thể dẫn đến Command Injection.
Ví dụ về sinks có thể phức tạp hơn một chút:
- Câu lệnh SQL: Nếu
userInput
chứa dữ liệu độc hại, có thể dẫn đến SQL Injection.
var query = "SELECT * FROM users WHERE name = '" + userInput + "'";
executeQuery(query);
- HTML (XSS): Nếu
userInput
chứa mã JavaScript độc hại, mã này có thể được thực thi trên trình duyệt người dùng, dẫn đến XSS.
document.getElementById('user-name').innerHTML = userInput;
- Command excution: Nếu
userInput
chứa lệnh hệ thống độc hại, có thể thực thi mã độc trên máy chủ.
os.system("ping " + userInput);
3. Mối quan hệ giữa sources và sinks
Quá trình mà dữ liệu tainted di chuyển từ sources đến sinks là trọng tâm của việc phát hiện và ngăn ngừa các lỗ hổng bảo mật. Cụ thể:
- Dữ liệu tainted từ nguồn không đáng tin cậy (source) có thể được truyền qua nhiều bước trong ứng dụng, cho đến khi nó đến một điểm quan trọng (sink), nơi dữ liệu này có thể được sử dụng để gây hại cho ứng dụng.
- Nếu dữ liệu tainted không được kiểm tra hoặc làm sạch khi đi qua các điểm này, nó có thể dẫn đến các lỗ hổng bảo mật như XSS, SQL Injection, hoặc Command Injection.
4. Quá trình Taint-flow: Một ví dụ đơn giản
Chúng xem xét một ứng dụng web đơn giản cho phép người dùng nhập tên và hiển thị nó trên trang.
<form action="/welcome" method="POST"> <input type="text" name="username"> <input type="submit" value="Submit">
</form>
Ở phía server xử lý như sau:
// Server-side code
var userInput = request.body.username; // Source
response.send("<h1>Welcome, " + userInput + "!</h1>"); // Sink
Trước hết, xác định source và sink:
- Source: Dữ liệu tainted có thể đến từ tham số
username
(do người dùng nhập vào). - Sink: Dữ liệu tainted được đưa vào HTML mà không qua kiểm tra hoặc làm sạch, gây nguy cơ XSS nếu người dùng nhập mã JavaScript độc hại.
Nếu kẻ tấn công nhập vào <script>alert('XSS')</script>
, mã độc này sẽ được thực thi trong trình duyệt của người dùng.
5. Một số source và sink điển hình
Sources:
document.URL
document.documentURI
document.URLUnencoded
document.baseURI
location
document.cookie
document.referrer
window.name
history.pushState
history.replaceState
localStorage
sessionStorage
IndexedDB (mozIndexedDB, webkitIndexedDB, msIndexedDB)
Database
Sinks:
document.write()
window.location
document.cookie
eval()
document.domain
WebSocket()
element.src
postMessage()
setRequestHeader()
FileReader.readAsText()
ExecuteSql()
sessionStorage.setItem()
document.evaluate()
JSON.parse()
element.setAttribute()
RegExp()
Trong phần tiếp theo chúng ta sẽ phân tích sâu hơn từng trường hợp các lỗ hổng cụ thể hoạt động ra sao trên các sources và sinks.