---
# System prepended metadata

title: '**Nghiên cứu về lỗ hổng Cross Site Scripting (XSS) trong Django framework**'

---

# **Nghiên cứu về lỗ hổng Cross Site Scripting (XSS) trong Django framework**

# Khái niệm về lỗ hổng Cross Site Scripting (XSS)
Cross Site Scripting (hoặc XSS) là kỹ thuật cho phép người dùng đưa các tập lệnh phía client vào trình duyệt của những người dùng khác nhằm thực thi Javascript tùy ý (và độc hại).
    
# Mục đích của XSS
Kẻ tấn công khai thác lỗ hổng XSS cho các mục đích khác nhau, bao gồm các trường hợp phổ biến sau:
1. Đánh cắp thông tin nhạy cảm
2. Đánh cắp, mạo danh danh tính người khác
3. Thực thi mã từ xa

# Phân loại XSS
Có 3 loại XSS chính là:

**1. Reflected XSS**

Reflected XSS phát sinh khi một ứng dụng nhận được dữ liệu trong một yêu cầu HTTP và bao gồm dữ liệu đó trong phản hồi tức thì theo cách không an toàn. 

Reflected XSS phổ biến trong cả ứng dụng phía server và phía client.  Một lỗ hổng XSS phổ biến trong các ứng dụng mà kẻ tấn công sẽ tìm kiếm là trang tìm kiếm. 

![](https://i.imgur.com/NAdwNP8.png)


**2. Stored XSS**
 
Stored XSS phát sinh khi ứng dụng nhận dữ liệu từ một nguồn không đáng tin cậy và bao gồm dữ liệu đó trong các phản hồi HTTP sau này của nó theo cách không an toàn.

Dữ liệu được đề cập có thể được gửi đến ứng dụng thông qua các yêu cầu HTTP; ví dụ: nhận xét về bài đăng trên blog, biệt hiệu của người dùng trong phòng trò chuyện hoặc chi tiết liên hệ về đơn đặt hàng của khách hàng.

Các cuộc tấn công Stored XSS  nguy hiểm hơn nhiều đối với một ứng dụng vì cuộc tấn công có cơ hội tác động đến tất cả người dùng của trang web, bao gồm cả quản trị viên trang web.

![](https://i.imgur.com/7N9hfHK.png)


**3. DOM based XSS**

Là loại tấn công XSS nâng cao, có thể thực hiện được khi tập lệnh phía máy khách của ứng dụng web ghi dữ liệu do người dùng cung cấp vào Document Object Model (DOM).

Sau đó, ứng dụng web sẽ đọc dữ liệu từ DOM và gửi nó đến trình duyệt. Nếu dữ liệu không được xử lý chính xác, kẻ tấn công có thể đưa ra payload được lưu trữ như một phần của DOM. Payload sau đó sẽ thực thi khi dữ liệu được đọc lại từ DOM.

![](https://i.imgur.com/XuALxlx.png)


# Môi trường bị ảnh hưởng
* Máy chủ web
* Máy chủ ứng dụng
* Môi trường ứng dụng web
# Django Security
Django framework giả định rằng tất cả dữ liệu đầu vào là không an toàn trừ khi được chỉ định khác.
Điều này có nghĩa là hầu hết các hình thức tấn công XSS không hoạt động với các framework Django.

Ví dụ: Với đoạn mã code HTML sau:
```htmlmixed=
<html lang="en-AU">
<body>
<form method="get">
     <label for="query">Search: </label>
     <input type="text" id="query" name="query" />
     <input type="submit" value="Search" />
</form>
 
<span id="search-query" >You searched for {{ query }}</span>
</body>
</html>
 ```
Giả sử chuỗi ***query*** mã độc truyền vào từ người dùng là:
```
<script>alert("XSS")</script>
```
Django sẽ tự động thoát khỏi chuỗi mã độc trong ***query*** thành:
```
 &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;
```
Nó thực hiện điều này bằng cách chuyển tất cả dữ liệu chuỗi thông qua hàm ***html.escape()*** của Python. Hàm này sẽ:

* Thay thế bất kỳ ký tự `&` bằng `&amp;` 
* Thay thế bất kỳ ký tự < hoặc > bằng `&lt;` hoặc `&gt;` 
* Thay thế bất kỳ ký tự `"` bằng `\"`
* Thay thế bất kỳ ký tự `'` bằng `\'`

# Một số cách thức bypass XSS trong Django
Như đã nói ở trên, Django được các nhà phát triển thiết lập cơ chế chống XSS bằng cách thoát chuỗi đối với các ký tự HTML nhạy cảm. Tuy nhiên vẫn có thể gặp phải XSS nếu mắc một số vấn đề sau:

**1. Lạm dụng "safe strings"**
 
Việc chống XSS trong Django có thể bị vô hiệu hóa khi một chuỗi được đánh dấu là “safe”, bằng cách sử dụng hàm `mark_safe()`. 
```
format_html(
    "{} <b>{}</b> {}",
    mark_safe(some_html),
    some_text,
    some_other_text,
 )
```
Hoặc trong mẫu sử dụng bộ lọc `safe` để tắt tính năng HTML escape.

`<span id="search-query" >You searched for {{ query | safe }}</span>`


**2. Một số kiểu thoát thuộc tính không được bảo vệ**
 
Nhiều kỹ thuật XSS sử dụng trình xử lý  HTML/JavaScript Event làm thuộc tính của các thẻ hiện có thay vì dùng các thẻ`<script>` mới.

Ví dụ: nhiều phần tử HTML hỗ trợ sử dụng một `onload` Event.

`<img src=/img/home.jpg onload=alert(1)>` 
 
Nếu Django của bạn có thuộc tính `src`  được thay thế bằng một input như:
 
`<img src={{ profile_photo }}>`

Kẻ tấn công có thể đặt input src là `"/img/home.jpg onload=alert(1)"` để thực hiện cuộc tấn công. Bởi vì không đóng gói thuộc tính `src` trong dấu nháy kép, cơ chế bảo vệ XSS của Django sẽ không thoát khỏi input này.

**3. Các thuộc tính khác có thể chấp nhận JavaScript**

Giả sử ứng dụng cho phép người dùng thêm một liên kết đến trang web cá nhân của họ và hiển thị trên trang hồ sơ cá nhân của họ.

`<a href="{{ user.profile_url }}">Website</a>`

Kẻ tấn công có thể đặt `"javascript:do_stuff(abcxyz)"` làm liên kết đến trang web của họ, vì vậy nếu người dùng nhấp vào liên kết, nó sẽ thực thi bất kỳ JavaScript nào mà kẻ tấn công muốn. 
 
Không chỉ vậy, một số biến thể trong cú pháp của javascript sau đây cũng có thể hợp lệ trong hầu hết các trình duyệt:
* `<a href="JaVaScript:alert(1)">XSS</a>`(giao thức không phân biệt chữ hoa chữ thường)
* `<a href=" javascript:alert(1)">XSS</a>`(các ký tự \x01- \x20 được cho phép trước giao thức)
* `<a href="javas cript:alert(1)">XSS</a>`(các ký tự \x09, \x0a, \x0d được phép bên trong giao thức)
* `<a href="javascript \x09:alert(1)">XSS</a>`(các ký tự \x09, \x0a, \x0d được cho phép sau tên giao thức trước dấu hai chấm)

**4.Các chuỗi được mã hóa base64**

Django theo mặc định sử dụng bảng mã Unicode và UTF-8 . Khi bạn cung cấp thông tin đầu vào, Django bắt buộc mã hóa nó và sau đó thoát khỏi các ký tự nguy hiểm. Nhưng điều này không hoạt động đối với các chuỗi được mã hóa base64.
 
Để làm điều này, trước tiên phải thay đổi dât protocol thành base64 trong request: 

`data:text/html;base64`
 
Sau đó chuyển đổi nội dung cần tiêm của mình thành chuỗi base64. 
Ví dụ: mã hóa chuỗi **`<script>alert('XSS')</script>`** sang base64, nó sẽ chuyển đổi thành **`PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=`** và chuỗi data protocol string sẽ là:

`data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=`

Điều này sẽ cho phép bypass **`html.escape()`**.
 
**5. JavaScript template strings**

Hàm thoát của Django thoát khỏi dấu nháy đơn và dấu nháy kép. Tuy nhiên có một thứ có cùng chức năng với những ký tự này nhưng không được thoát, đó là backtick ( ` ).

Đối với trình duyệt, **'Hello World!'** cũng giống như **"Hello World!"**

Backtick là một ký tự mẫu hoạt động như dấu nháy kép nhưng không được thoát bởi hàm thoát của Django. Vậy nên có thể tạo payload XSS từ nó.

Giả sử ứng dụng lấy vị trí của một hình ảnh và hiển thị nó trên trang bằng thẻ **img** . Vì vậy, nếu thông tin đầu vào là **/Images/cyber.png**, thì trang sẽ là:

**`<img src="/Images/cyber.png"/>`**
 
Nếu cố gắng đưa một payload độc hại bằng cách sử dụng dấu nháy kép, giả sử input là **`javascript: alert (“XSS”)`** . Input này sẽ được thực hiện thoát:

**`<img src="javascript:alert(&quot;XSS&quot;)>`**

Payload sẽ không thực thi tập lệnh. Nhưng chúng ta có thể vượt qua điều này bằng cách sử dụng các ký tự mẫu. Nếu đầu vào của bạn là:
    
![](https://i.imgur.com/NmUAdNa.png)
    
Django sẽ không thoát khỏi backtick ( ` ). Vì vậy, chúng ta sẽ nhận được:

![](https://i.imgur.com/WznciiC.png)

Và đây là một payload hợp lệ.

# Một số lỗ hổng XSS trong Django
**1. CVE-2019-12308**
> **Phiên bản bị ảnh hưởng:**
> * 1.11 - 1.11.21 
> * 2.1 - 2.1.9 
> * 2.2 - 2.2.2
>     
> **Đối tượng ảnh hưởng:** AdminURLFieldWidget
>     
> **Mô tả:** 
Giá trị URL hiện tại có thể click vào được hiển thị bởi AdminURLFieldWidget sẽ hiển thị giá trị được cung cấp mà không xác thực nó như một URL an toàn. Do đó, giá trị chưa được xác thực được lưu trữ trong cơ sở dữ liệu hoặc giá trị được cung cấp dưới dạng tải trọng tham số truy vấn URL, có thể dẫn đến liên kết JavaScript.
>     
> **POC:**
>     
>Version Django được sử dụng trong POC là: **1.11.10**
>     
>![](https://i.imgur.com/kljJp1s.jpg)
> 
> Tạo file models.py và admin.py như sau:
>     
> ![](https://i.imgur.com/LoLUlHN.jpg)
>     
> Link URL được hiển thị bởi **AdminURLFieldWidget** mà không mã hóa chính xác có thể dẫn đến việc chèn javascript.
>     
> ![](https://i.imgur.com/H1BH5Ca.jpg)
> 
**2. CVE-2020-13596**
> **Phiên bản bị ảnh hưởng:**
> * 2.2 - 2.2.13 
> * 3.0 - 3.0.7 
>     
> **Đối tượng ảnh hưởng:** admin ForeignKeyRawIdWidget
>     
> **Mô tả:** 
Các tham số truy vấn do quản trị viên Django ForeignKeyRawIdWidget tạo không được mã hóa đúng URL, dẫn đến khả năng xảy ra tấn công XSS.
> 
> **POC:**   
> Version Django được sử dụng trong POC là: **3.0.6**
>     
> ![](https://i.imgur.com/xEzxv9a.jpg)
> 
> Tạo file models.py và admin.py như sau:
>     
> ![](https://i.imgur.com/VGGUo12.jpg)
>     
> ![](https://i.imgur.com/fU6FooS.jpg)
>     
> Truy cập vào trang admin và sử dụng chức năng thêm object:
>     
> 
> 
> 
> Sử dụng chức năng review source-code để kiểm tra. Như hình các tham số truy vấn cho AdminURLFieldWidget không được mã hóa chính xác đẫ đến tạo ra vector attack XSS.  
>     
> ![](https://i.imgur.com/Fd4QPd5.jpg)
> 
> XSS payload sẽ được thực thi nếu chúng ta thay đổi giá trị của  parameter “limit_choices_to” thành : **"><script>alert("demo_XSS")</script><a**
> 
> ![](https://i.imgur.com/whSOnLs.jpg)
> 
> Script đã được thực thi.
> 
> ![](https://i.imgur.com/XKgW993.jpg)
> 
> ![](https://i.imgur.com/FxRvBac.jpg)
                                                                         
# Phòng chống XSS trong Django
**1. Sử dụng Safe Filter** 
    
Việc sử dụng sai **Safe Filter** có thể nguy hiểm, như được mô tả trong ví dụ phía trên. Do đó, điều rất quan trọng là phải cẩn thận trong việc sử dụng nó. Không sử dụng bộ lọc này nếu nó không cần thiết. Nếu ứng dụng được phát triển, hãy tìm tất cả các phiên bản của bộ lọc này. Sau đó có thể đánh giá xem nó có thực sự cần thiết hay không. Nếu cần, hãy phân tích tác động của nó đối với bảo mật và thực hiện các biện pháp bổ sung.

Ví dụ ta có thể viết một bộ filter sử dụng bleach (một thư viện làm sạch HTML từ các nguồn không đáng tin cậy dựa trên danh sách được phép thoát hoặc đánh dấu và thuộc tính )để nhằm xóa mọi dữ liệu có thể gây hại:

```python=
from django import template
from django.utils.safestring import mark_safe
import bleach

register = template.Library()
_ALLOWED_ATTRIBUTES = {
        'a': ['href', 'title'],
        'img': ['src', 'class'],
        'table': ['class']
}
_ALLOWED_TAGS = ['b', 'i', 'ul', 'li', 'p', 'br', 'a', 'h1', 'h2', 'h3', 'h4', 'ol', 'img', 'strong', 'code', 'em', 
    'blockquote', 'table', 'thead', 'tr', 'td', 'tbody', 'th']

@register.filter()
def safer(text):
    return mark_safe(bleach.clean(text, tags=_ALLOWED_TAGS, attributes=_ALLOWED_ATTRIBUTES))
```


Chúng ta sẽ sử dụng thẻ này như một thẻ thay thế cho thẻ `safe`:

`<span id="search-query" >You searched for {{ query | safer }}</span>`

   
**2. Input Validation**
    
Quy tắc đầu tiên của bảo mật ứng dụng web là không bao giờ tin tưởng input đầu vào của người dùng. Có thể có vô số đầu vào của người dùng và rõ ràng, không phải tất cả đều an toàn. Đầu vào không an toàn từ người dùng có thể là cố ý hoặc vô ý. Bất kể mục đích là gì, chúng vẫn nguy hiểm. Cần phải kiểm tra tất cả thông tin đầu vào của người dùng để loại bỏ rủi ro. Làm sạch đầu vào là một phương pháp làm cho đầu vào an toàn để sử dụng trong hoặc bởi ứng dụng web.
    
**3. Sử dụng HttpOnly**
    
Các ứng dụng web sử dụng cờ HttpOnly để hạn chế quyền truy cập cookie từ tập lệnh phía client. Nếu cờ HttpOnly được bật, quyền truy cập vào cookie sẽ bị hạn chế đối với máy chủ. Bằng cách sử dụng cờ HttpOnly, ta có thể loại bỏ nguy cơ kẻ tấn công lấy thông tin cookie bằng cách sử dụng các cuộc tấn công XSS.

Có thể bật tính năng này trong Django bằng cách thêm dòng sau vào tệp **`settings.py`** của  Django project:

`SESSION_COOKIE_HTTPONLY = True`
    
**4. Sử dụng CSP**
    
Content Security Policy (CSP) là chính sách bảo mật nội dung, được sử dụng để xác định nội dung an toàn trên website mà trình duyệt có thể tải về cho người dùng. CSP là biện pháp đối phó rất hiệu quả với kiểu hack chèn mã độc Cross Site Scripting (XSS). Với CSP, trình duyệt chỉ chạy JavaScript từ những domain được chỉ định.

Khi bạn truy cập vào một trang web, trình duyệt sẽ gửi yêu cầu tải nội dung đến máy chủ. Máy chủ sẽ gửi trả lại nội dung của trang web này cho trình duyệt, trong đó bao gồm các file CSS, Javascript, Font, Frame … Trình duyệt sẽ tải toàn bộ những file này, vì nó được chỉ định phải làm như vậy từ mã nguồn của trang web để có thể hiển thị nội dung.

Hacker có thể lợi dụng điều này để đặt một đoạn mã ở trong mã nguồn (những người sử dụng mã nguồn không rõ nguồn gốc rất hay bị dính) hoặc trong một phần bình luận trên trang web để tải một số file Javascript độc hại từ một nguồn bên ngoài. Trình duyệt sẽ không tự biết được có nên tải những file này hay không, nó chỉ thực thi theo yêu cầu cho dù nguồn gốc của file Javascript đến từ trang mà hacker sở hữu. Đây chính là lý do chúng ta cần đến CSP.
   
Để sử dụng CSP, server chỉ cần thêm header **`Content-Security-Policy`** vào mỗi response. Nội dung header chứa những domain mà ta tin tưởng.
   
![](https://i.imgur.com/ZgvlgyK.jpg)

# Tài liệu tham khảo
* [https://django-book.readthedocs.io/en/latest/chapter20.html#cross-site-scripting-xss](https://)
* [https://tincoinviet.net/tong-quan-ve-tan-cong-ddos-x-xss-csp-phuong-thuc-tan-cong/#Tan_Cong_XXSS_la_gi](https://)
* [https://www.stackhawk.com/blog/django-xss-examples-prevention/](https://)
* [https://tonybaloney.github.io/posts/xss-exploitation-in-django.html](https://)
* [https://snyk.io/vuln/pip:django](https://)
* [https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-13596](https://)
* [https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12308](https://)
