Vài lỗ hổng thường gặp và cách phòng chống trong Django

Vài lỗ hổng thường gặp và cách phòng chống trong Django
Photo by Werner Moser from Pixabay

Bảo mật các trang Web luôn là vấn đề đau đầu cho bất cứ ai. Có vô vàn cách thức tấn công khác nhau. Và ngay khi chúng ta có thể phòng chống được cách này thì lại có các khác được sinh ra.

Trong bài viết này, tôi sẽ trình bày một số lỗ hổng phổ biến và các cách phòng tránh chúng.

Lỗ hổng

SQL Injection (SQLi)

SQL Injection là một lỗ hổng kinh điển. Nó xuất hiện gần như ngay liền với sự xuất hiện của các Web app.

Giả sử chúng ta có một ứng dụng, trong đó có chức năng quản lý user (rất phổ thông). Nếu như trang web cho người dùng nhập tên tùy ý, và nó sẽ tìm kiếm những user phù hợp với nội dung đó.

Một trang web đơn giản sẽ build một câu truy vấn SQL tương tự như dưới đây, tìm kiếm bất cứ thứ gì người dùng nhập vào:

SELECT * FROM employees WHERE name LIKE  '#{NAME}'

Nếu chúng ta nhập vào Alice, chúng ta sẽ có truy vấn là:

SELECT * FROM employees WHERE name LIKE 'Alice'

Với câu truy vấn như trên thì mọi thứ hoạt động bình thường. Tuy nhiên, không phải người dùng web nào cũng thật thà, chất phác, và có thể có những người thích nghịch cho vui nên điền NAME như sau:

Alice'; DROP TABLE EMPLOYEES;

Lúc này câu query được build sẽ thế này:

SELECT * FROM employees WHERE name LIKE 'Alice'; DROP TABLE EMPLOYEES;'

Nếu chạy query trên thì chết rồi. Nó sẽ xóa sạch bảng của chúng ta. Những gì nó làm rất khó kiểm soát, khó nhận biết nhưng hậu quả thì thật khủng khiếp.

Đó chính là SQL Inject, trong đó, kẻ tấn công sẽ chèn bất cứ thứ gì vào các câu truy vấn SQL để thực thi những hành động mà chúng muốn.

Cross Site Scripting (XSS)

Cross Site Scripting có ý tưởng tương tự như SQL Injection. Nếu một kẻ tấn công có thể nhúng mã JavaScript mà chúng muốn vào trang web của chúng ta, thì mã đó sẽ được thực thi trên trình duyệt của tất cả mọi người khi truy cập vào trang web đó. Nhờ đó, kẻ tấn công có thể làm mọi thứ, với quyền truy cập của những user khác nhau.

Ví dụ, kẻ tấn công comment vào 1 bài viết nào đó:

Great post!

Nhưng không chỉ vậy, hắn ta còn tranh thủ chèn thêm vài thứ:

Great post!<script> do some nefarious Javascript stuff </script>

Nếu trang web hiển thị comment này cho người dùng, mã JavaScript được nhúng ở comment trên cũng được truyền tải tới trình duyệt của họ, và nó sẽ được thực thi. Rất khó để biết được những mã JavaScript này đã bị chèn vào website, trừ khi những chuyên gia bảo mật của website đó.

Mã JavaScript này được chạy trong trang và trang web của chúng ta cung cấp cho người dùng. Nó có thể làm rất nhiều thứ nếu người dùng đang đăng nhập. Kẻ tấn công có thể lấy dữ liệu về rồi gửi chúng đi đâu đó. Hoặc nếu user có quyền hạn lớn hơn nữa, hắn có thể thực hiện các hành động phá hoại hệ thống, tạo dữ liệu giả mạo. Thậm chí hắn có thể tạo ra những user với có quyền hạn tương tự và giữ lại để dùng sau này. Nếu như vậy, dù lỗ hổng của chúng ta đã bị phát hiện và vá rồi, hắn ta vẫn có thể tiếp tục công việc phá hoại của mình.

Vì vậy, một yêu cầu rất rõ ràng, rằng website của chúng ta nhận dữ liệu từ người dùng, lưu trữ, xử lý rồi hiển thị chúng, thì chúng ta phải hết sức cẩn thận với những dữ liệu này.

Nhưng ngay cả khi trang web không lưu dữ liệu người dùng, nó vẫn có thể có lỗ hổng. Ví dụ một trang web với tính năng tìm kiếm thường người dùng sẽ được đưa cho một link kiểu như sau:

http://example.com/search?q=somethingtosearchfor

Đây là một điều rất bình thường, ngay cả Google cũng làm như vậy. Ở link này, trang web sẽ hiển thị các kết quả tìm kiếm được với đầu vào được cung cấp. Kẻ tấn công vẫn có thể những mã JavaScript vào yêu cầu tìm kiếm này, đưa nó cho người dùng khác. Khi người dùng click vào những link này, họ sẽ truy cập trang, vẫn thấy một số kết quả được tìm kiếm. Nhưng đi kèm với nó, những mã JavaScript bị nhúng trong link mà hiển thị trên trang (Google thì không làm vậy) được sẽ được thực thi trên trình duyệt người dùng.

Bởi vì cách thức tấn công này, kẻ tấn công có thể chạy script của chúng trên những trang web mà chúng hoàn toàn không có quyền quản trị, nên kiểu tấn công này được gọi là Cross-Site Scripting.

Cross Site Request Forgeries (CSRF)

Điều cốt yếu của kiểu tấn công này là một trang web sẽ gửi request đến những trang khác (nạn nhân), sử dụng quyền hạn của người dùng hiện tại.

Ví dụ cuối cùng trong mục XSS ở trên có thể coi là một kiểu CSRF.

Một ví dụ kinh điển hơn, nếu một trang web cho phép người dùng xóa tài khoản của mình bằng cách truy cập /delete-account bằng phương thức GET. Nếu một trang web có link tới victimsite.com/delete-account và khi người dùng click link đó, họ sẽ bị xóa tài khoản ở trang này.

Kiểu tấn công ác hơn nữa, đó là trang web độc hại kia tạo những request bằng AJAX đến trang nạn nhân. Làm như vậy, thì dù phương thức là gì (POST, DELETE, PUT) cũng không phải là cản trở. Mà người dùng chỉ cẩn truy cập trang web độc hại là mất tài khoản ngay.

Phòng chống trên ứng dụng, server

SQL Injection

Phần lớn các framework phát triển web đều sử dụng ORM (Object-Relational Mapping), và phần lớn các cơ sở dữ liệu đề có những phương thức rất đặc biệt để tạo ra các truy vấn lấy dữ liệu. Ít khi nào lập trình viên cần phải tự mình build các câu query SQL thuần cả. Và những công cụ đã tích hợp sẵn nhiều cách thức để phòng chống SQL Injection.

XSS

Các framework phát triển web thường sử dụng phương thức escape với dữ liệu được nhập vào. Mặc định, tính năng này sẽ được bật. Escape nghĩa là những ký tự đặc biệt, ví dụ < hoặc > sẽ được lưu trữ thành &lt;, &gt;. Khi hiển thị những dữ liệu này, nó sẽ được trình duyệt phiên dịch lại thành <>, trải nghiệm người dùng không bị ảnh hưởng, nhưng những kẻ tấn công khó có thể nhúng những thứ như <script>...</script> nữa. Bởi vì lúc này, những thứ đó sẽ không sinh ra tag HTML mà chỉ hiển thị như text thông thường.

CSRF

Chúng ta khó lòng mà có thể hủy bỏ toàn bộ link tới các trang web bên ngoài. Nhưng chúng ta vẫn có nhiều cách khác nhau để phòng chống CSRF. Chúng ta cần chắc chắn rằng, những trang web của bên thứ ba không thể build bất kỳ truy vấn nào đến trang của chúng ta mà có thể gây ra tai họa.

Bước đầu tiên của việc phòng chống CSRF đó là, đảm bảo rằng các truy vấn sử dụng phương thức GET sẽ chỉ lấy ra dữ liệu mà không thay đổi chúng. Ngoài ra, cần đảm bảo rằng, các dữ liệu được in ra phải được lọc cẩn thận. Cách làm này sẽ phòng chống được cách tấn công đơn giản nhất, khi các link đơn giản được gắn trên các trang web độc hại và lừa người dùng click vào.

Các trang web độc hại kia vẫn có thể tạo các truy vấn bằng AJAX sử dụng phương thức POST tới trang web của chúng ta. Vậy làm thế nào để phòng chống?

Phần lớn các framework đều đã tích hợp sẵn tính năng phòng chống tấn công CSRF. Phương pháp là mỗi người dùng truy cập trang web sẽ được cấp một token. Token này khác nhau với từng user, từng trình duyệt, chúng thường là những string được băm, được mã hóa và không thể hiểu được. Mỗi khi người dùng truy vấn đến trang web, token này sẽ được gửi kèm truy vấn. Trang web của chúng ta sẽ reject tất cả những request không có token chính xác.

Token đó được dùng để phòng chống CSRF nên được gọi là CSRF token. Với Django, bất cứ form nào sử dụng phương thức POST đều cần submit thêm token này. Các framework khác như Rails thậm chí còn chèn token này cho mọi trang của website. Những trang web độc hại không thể lấy được token, không thể build các truy vấn hợp lệ và chúng ta không cần quan tâm đến chúng.

Phòng chống từ trình duyệt người dùng

Những trình duyệt hiện đại đã cài đặt nhiều biện pháp khác nhau để phòng chống các kiểu tấn công trên.

Bạn có thể thắc mắc rằng, làm thế nào chúng ta có thể tin tưởng việc người dùng sẽ không tấn công website của chúng ta nhờ trình duyệt của họ. Câu trả lời là, những người phát triển web không cần đặt niềm tin vào các trình duyệt. Các biện pháp được cài đặt ở trình duyệt là để bảo vệ chính người dùng, chứ không phải website. Người dùng cũng là nạn nhân của các cuộc tấn công, họ bị người khác lợi dụng cho mục đích đen tối của chúng. Và những biện pháp được cài đặt trên trình duyệt là nhằm ngăn chặn kẻ tấn công có thể lợi dụng người dùng, tìm cách phá hoại trang web của bạn.

Same-Origin Policy

Tất cả các trình duyệt hiện này đều cài đặt các phương thức khác nhau cho phù hợp với Same Origin Policy (SOP). Nó phòng tránh trang này load dữ liệu từ trang web khác (những tài nguyên không cùng nguồn gốc).

Một điểm quan trọng của SOP là các truy vấn AJAX sẽ bị giới hạn. AJAX có thể gửi các truy vấn POST cùng rất nhiều những dữ liệu khác, mà chúng có thể dễ dàng bị giả mạo. Một truy vấn AJAX có thể được gửi đến trang web khác cùng với cookie của người dùng, cùng với những dữ liệu đăng nhập của họ. Same Origin Policy sẽ ngăn chặn điều đó.

SOP sẽ giúp phòng chống kiểu tấn công như thế nào? Giả sử kẻ tấn công tạo một trang web với rất nhiều hình ảnh bắt mắt nhằm thu hút người dùng. Khi người dùng truy cập trang web đó, không có SOP, các web có thể chạy JavaScript và gửi các truy vấn AJAX tới các trang khác mà người dùng không hề hay biết. Những truy vấn như vậy sẽ gửi kèm cookie cùng các dữ liệu khác trên trình duyệt của người dùng, mà những thao tác mà user thực hiện hoàn toàn hợp lệ. Nhưng SOP sẽ khiến trình duyệt không gửi những truy vấn như vậy thì các website không cùng nguồn gốc. Người dùng chỉ có thể thao tác với trang web của kẻ tấn công mà thôi, điều đó khiến chúng chẳng thu được lợi ích gì cả.

Content Security Policy (CSP)

CSP là một cơ chế mới mà trình duyệt cài đặt nó có thể có những biện pháp phòng chống tốt hơn.

Nếu kết quả trả về từ server có chưa CSP header, mặc định trình duyệt sẽ không chấp nhận JavaScript, CSS dạng inline, hoặc sử dụng hàm eval của JavaScript trên trang. Điều này sẽ giúp phòng chống rất nhiều dạng thức khác nhau của XSS. Ngay cả khi kẻ tấn công có thể nhúng JavaScript vào nội dung của trang web, chúng cũng không được thực thi.

Ví dụ, kẻ tấn công có thể comment vào bài viết với nội dụng như sau:

Great post!<script> do some nefarious Javascript stuff </script>

Một thẻ <script> đã được nhúng vào và sẽ được hiển thị cho người dùng. Mặc dù đây là một thẻ HTML hợp lệ, trình duyệt cũng không thực thi chúng.

Kết luận

Bài viết trình bày những điều cơ bản về bảo mật website. Bất cứ người phát triển web nào cũng nên có những hiểu biết nhất định về các lỗ hổng này.

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.