Hiểu sâu về CSRF, CORS và xử lý ở cấp độ core một cách an toàn

Bài viết này ở cấp độ nâng cao do đó mình không giới thiệu gì qua CSRF cũng như CORS, các bạn tự tìm hiểu sơ qua về nó trước. Trước khi viết bài này mình cũng đã đọc nhiều tài liệu giới thiệu về CSRF cũng như CORS thì hầu như chỉ là đi dịch lẫn nhau để viết lên kiếm view, còn hiểu tường tận cũng như lập trình hiệu quả thì không có.

Trước tiên bạn cũng nên hiểu CSRF thường được xem là "lỗi" còn CORS lại là "chức năng", tuy vậy hai cái này đều có điểm tương đồng đó là Cross-Site Request tức là truy vấn chéo hay truy vấn "khác domain". Vì thế, ứng dụng của bạn nên xử lý hai vấn đề này trong một.

Đầu tiên để xác định cấp độ bảo vệ, bạn phải xác định rõ các phương thức của website bạn đang xử lý: GET, POST, PUT hay là gì khác và cách thức tương tác. Thông thường cho tất cả các ứng dụng website, có hai phương thức GET và POST được sử dụng đại trà, các phương thức PUT, DELETE ít khi dùng nếu không phải bạn đang dùng để viết API server.

Tiếp theo bạn cần xác định rõ:
  • GET: Sử dụng với nhu cầu truy vấn thông thường, khi người dùng mở trang web xem, khi các BOT truy cập site. Đơn thuần đó là nhu cầu "Đọc", hãy cực kỳ hạn chế việc update, insert, delete dữ liệu ở phương thức này trừ khi bạn bảo vệ nó rất kỹ.
  • POST: Thường sử dụng khi cần thao tác thay đổi trong CSDL như thêm mới, xóa, sửa dữ liệu => Việc mất an toàn, tấn công sẽ nhằm vào đây.
  • Các phương thức khác nếu có dùng hãy xem nó thực hiện chức năng thao tác đọc đơn thuần hay ghi dữ liệu mà liệt kê nó vào cùng loại với GET hay POST.
Về CORS bạn cũng cần biết nó có 2 dạng:

- 1 dạng simple không có preflight request:

Ví dụ cụ thể là dùng ajax của Jquery hoặc bỏ đi cái xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"). Request này không gửi trước OPTIONS để thăm dò server có chấp nhận hay không mà gửi dữ liệu trực tiếp đi. Đối với dạng này không thể dùng Access-Control-Allow-Origin để chặn trình duyệt không gửi đi request được. 
Do đó ta thường thấy submit request dạng này thì trình duyệt báo lỗi:

Access to XMLHttpRequest at 'http://writeblabla/index.php' from origin 'http://localhost' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://dungpt.vinades.net' that is not equal to the supplied origin.

Tuy vậy, dữ liệu vẫn gửi đi, server vẫn cứ xử lý như thường.
Request này cũng không gửi đi X-Requested-With cho nên không biết luôn là có phải CORS request không. Tuy vậy nó sẽ có Origin header, đối với trường hợp này kiểm tra bằng luật Origin Header và Referer header (mời đọc phần cuối).

- 1 dạng full có preflight request:

Ví dụ new XMLHttpRequest có sử dụng xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
Dạng này thì trình duyệt gửi đi request OPTIONS để thăm dò server trước, nếu được phép thì mới gửi request thật.
Dạng này thì ngoài việc kiểm tra Origin, Referer cần kiểm tra preflight request nữa (phải xảy ra trước cái kiểm tra Origin, Referer)

Túm cái váy lại code phải kiểm tra cái Origin, Referer nếu có xuất hiện phải là domain được cấp phép hoặc IP được cấp phép.
Đối với cái kiểm tra preflight request luôn luôn chạy mặc dù cấu hình có chặn hay không chặn.

Bảo vệ như thế nào?

1. Hạn chế thiết kế ứng dụng của bạn thực hiện update, insert, delete dữ liệu thông qua phương thức get. Nếu có hãy đảm bảo bạn bảo vệ nó tốt, không để hacker có thể đánh lừa bạn tực thực hiện thao tác không mong muốn.
2. Đối các truy vấn chéo (Cross-Site Request) ví dụ Ajax từ các trang khác, form từ các trang khác hãy:

- Kiểm tra, nếu có Header Origin nó phải có giá trị hợp lệ theo dạng Origin: <scheme>://<hostname>[:<port>] nếu không hãy báo lỗi và dừng tiến trình xử lý.
- Khi có xuất hiện header Origin hãy lập trình để ứng dụng của bạn đáp trả header đầy đủ:

        'Access-Control-Allow-Origin' => 'http://domain.com',
        'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type', // Các Header được phép trong CORS
        'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS', // Các phương thước được phép trong CORS
        'Access-Control-Allow-Credentials' => 'true', // Cho phép gửi cookie trong truy vấn CORS
        'Access-Control-Max-Age' => 10 * 60 * 60, // 10 min, max age for Chrome. Thời gian cache preflight request (request OPTIONS kiểm tra)
        'Vary' => 'Origin' // Thông báo cho trình duyệt biết, mỗi Origin khác nhau sẽ có mỗi phản hồi khác nhau thay vì dùng *

Điều này giúp website của bạn chặn được các cuộc truy vấn CORS bất hợp pháp.

- Hãy đảm bảo nếu có một preflight request thì chỉ trả về header, không xử lý tiếp.
- Cuối cùng hãy kiểm tra nếu phương thức truy vấn là POST thì:

+ Kiểm tra Origin nếu có phải là same-domain hoặc thuộc domain whitelist
+ Hiểm tra Referer luôn luôn phải có và same-domain hoặc thuộc domain whitelist

Khi đủ hai yêu cầu trên mới thực hiện tiếp truy vấn, nếu không dừng và báo lỗi.
Bình luận (0)