🍪Same Origin Policy (SOP) - Lab

Lab 1-4

Mở đầu

Đáng nhẽ ra bài này được research trong khoảng 3-4 tháng trước nhưng lúc đó đang trong thời gian học nên thời gian nghỉ đông bây giờ mình mới research được...

What is SOP

SOP hay còn gọi là Same-origin policy là một cơ chế bảo mật trình duyệt web nhằm mục đích bảo vệ websites từ một website khác(Ví dụ như website chứa mã độc).

Khi một người dùng xem một trang Web trên trình duyệt của họ, các đoạn mã chạy trên trang Web đó sẽ chỉ có thể được đọc hoặc định nghĩa nội dung của một trang Web khác nếu chúng có cùng một “nguồn gốc — Origin”. Và Origin ở đây được hiểu là sự kết hợp của cả 3 yếu tố: giao thức ứng dụng Web (HTTP/HTTPS), cổng giao tiếp (Port, thường là 80 với HTTP và 443 với HTTPS), và tên miền (vâng, www.google.com, www.facebook.com...). Nếu mà khác 1 trong 3 thứ này thì có nghĩ nó nằm ở một Origin khác.

Ví dụ như: Chúng ta đang mở 2 tab, 1 tab là facebook, tab kia là 1 trang web nào đó có chứa mã độc. Sẽ rất nguy hiểm nếu như các đoạn script ở bên tab chứa mã độc có thể tự do thao tác lên tab facebook phía bên kia, và SOP sinh ra với nhiệm vụ ngăn chặn các hành động này.

So, what is an origin❓

Hai URLs có cùng origin nếu protocol, port, and domain (not subdomains) khớp nhau.

Ví dụ, hãy xem xét URL này:

https://shang.io

URL này bao gồm:

  • protocol: https

  • domain: shang.io

  • default port number: 443

Chúng ta sẽ xem xét bảng sau:

URLSame Origin?

http://shang.io

❌Different protocol and different port

https://www.shang.io

❌Different domain

https://csrf.shang.io

❌Different domain

https://shang.io:8080

❌Different port

https://shang.io/login

✅Same protocol, domain, and port

⚡ Lab 1 : Why do we need SOP?

Không có SOP, nếu bạn truy cập một trang web độc hại, nó sẽ có thể đọc hoặc sửa đổi nội dung của tài khoản ngân hàng của bạn, hãy đọc email của bạn từ Gmail, tin nhắn riêng tư từ Facebook, v.v.

Hãy xem SOP chặn cuộc tấn công như nào:

  1. Giả sử một kẻ tấn công đã lừa bạn truy cập trang này.

  2. Trang chứa một trang đăng nhập, giả sử rằng nó trông giống hệt với trang đăng nhập ngân hàng của bạn!

  3. Nhấp chuột phải vào bất cứ nơi nào trong trang và view page source , chúng ta sẽ thấy:

    <iframe id="CSRFlogin" src="https://csrf.secure-cookie.io/login"...></iframe>

  4. Sử dụng iframe element, kẻ tấn công có thể nest trang HTML vào trang web Attacker. Ở đây kẻ tấn công đang nhúng trang đăng nhập từ

    https://csrf.secure-cookie.io/login

💡Trong thế giới thực, kẻ tấn công sẽ sử dụng URL đăng nhập tài khoản ngân hàng thực của bạn.

  1. Khi chúng ta view source code thì chúng ta thấy đoạn JS như sau:

    <script>
        window.onload = function() {
            login_form = document.getElementById('CSRFlogin').contentWindow.document.forms[0]
            username = login_form[0].value
            password = login_form[1].value
            console.log(username,password)
        };
    </script>

Đoạn code trên sẽ try cập HTML FORM và lấy username/password và sau đó print chúng ở console. Kẻ tấn công có thể gửi dữ liệu về một cái server nào đó mà chúng điều kiển

  1. Ngay khi bạn load trang này, trình duyệt sẽ ném security violation ra console

Uncaught DOMException: Permission denied to access property “document” on cross-origin object

Cái exception này có nghĩa là SOP ngăn chặn đoạn JS độc hại ở Origin

Từ truy cập dữ liệu ở Origin

Trong lab này, chúng ta sử dụng iframe, tuy nhiên SOP áp dụng trên iframe cũng như AJAX (một thuật ngữ cũ và nó chỉ có nghĩa là thực hiện các yêu cầu HTTP từ JavaScript).

🔎 Deep dive - SOP Rules

Như chúng ta đã thấy, same-origin policy kiểm soát các tương tác giữa hai Origin khác nhau. Các nhà phát triển frontend thường sử dụng JavaScript API như XMLHttpRequest hoặc fetch để make HTTP request tới backend APIs.

Nếu HTTP request là cùng Origin, thì SOP sẽ cho phép nó và nó sẽ hoạt động.

Nếu request là cross-origin (với origin khác), thì tùy thuộc vào loại request, SOP không cho phép một số yêu cầu có cross-origin nhất định.

Hãy xem xét hai URL trên: (Cross-origin types)

Interaction typeResult

read: reading data from an endpoint, such as json data.

❌ Disallowed

write: sending data to an endpoint, such as password reset request.

✅ Allowed (as long as it’s a simple request)

embedding: embedding resource, such as image/video using HTML element.

✅ Allowed (as long as crossorigin attribute is not set)

⚡ Lab 2 : cross-origin embedding: allowed

Chúng ta sẽ đi sau vào 2 rule SOP đó là embedding và reading, trong lab này chúng ta sẽ nói rõ về embediding.

Embedding resources,như là images/videos/Javascripts từ một origin khác, nó không vi phạm SOP nên chúng được cho phép.

Chúng ta sẽ confirm rule này:

  1. Mở network monitor ở developer tool trên trình duyệt

  2. Lưu ý rằng chúng ta có thể tải/nhúng một số tài nguyên(resource) như course_image.png

Ngoài ra còn có một số tệp JS đã được tải thành công (readJSON.js và PrintSecureCookie.js).

Những tài nguyên này được load từ origin này:

Tới origin này:

  1. Chúng ta bật console lên:

Lưu ý rằng việc thực thi tệp JS (PrintSecureCookie.js) sẽ đưa ra một message ra console.

Hello secure-cookie!!

Mặt khác, việc thực thi JS (readJSON.js) đang gây ra sự cố và gây ra warning msg

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/sample.json (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

Chúng ta tự hỏi tại sao lại có message này và tại sao lại xảy ra.

Mặc dù SOP cho phép nhúng tài nguyên, nhưng nó không cho phép đọc tài nguyên.

Để làm rõ vấn đề này chúng ta sẽ tới phần tiếp theo...

Trước khi qua phần tiếp theo chúng ta sẽ tới với một bài tập thú vị:

Exercise: discover crossorigin attribute in html

  1. Nhấp chuột phải và view source code của page lab trước

<body>
<script src="https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/readJSON.js"></script>
<script src="https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/PrintSecureCookie.js"></script>
<img src="https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/course_image.png">
</body>
  1. Copy html và paste chúng vào một text editor và add thêm thuộc tính crossorigin="anonymous", nó sẽ trông như này:

<body>
<script src="https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/readJSON.js" crossorigin="anonymous"></script>
<script src="https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/PrintSecureCookie.js" crossorigin="anonymous"></script>
<img src="https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/course_image.png" crossorigin="anonymous">
</body>
  1. Lưu file thành temp.html và mở nó ra trên trình duyệt(hãy sử dụng private browsing / incognito mode) khi đó chúng ta sẽ không load được hết vì:

Thuộc tính này sẽ tắt quy tắc SOP cho phép nhúng tài nguyên theo mặc định. Nó yêu cầu trình duyệt nhúng tài nguyên chỉ khi Cross-Origin Resource Sharing (CORS) cho phép làm như vậy.

Nếu CORS nghe có vẻ hơi phức tạp, tôi có một bài đăng dành riêng cho CORS, hãy xem! 😉

⚡ Lab 3 : cross-origin read : disallowed

Hãy xem hình này, chúng ta thấy rằng trang web trả về STATUS 200 bằng cách fetch toàn bộ tài nguyên web như images và JS

Mặt khác, ở ảnh này chúng ta thấy rằng same origin policy đã chặn một JS(readJSON.js) từ reading tài nguyên(sample.json)

Oke chúng ta sẽ nói về sự khác nhau giữa reading và embedding

Và trang html đã load đoạn JS (readJSON.js) từ orgin(https://s3.eu-central-1.amazonaws.com) using <script> tag 👇.

<script src="https://s3.eu-central-1.amazonaws.com/public-secure-cookie.io/readJSON.js"></script>

Như chúng ta đã nói, SOP cho phép embedding cross-origin resource. Bởi vậy fetch code JS(readJSON.js) đã xảy ra mà không có sự cố nào -> status 200✅

Bậy giờ thực thi/execute code JS(readJSON.js) sau khi load nó thì đây là một câu chuyện khác😅

Chúng ta sẽ xem code của đoạn JS(readJSON.js):

url = "https://s3.eu-central-1.amazonaws.com/
public-secure-cookie.io/sample.json" (1)

fetch(url,{method: 'GET'}).then(response =>
response.json()).then(function (data) {
  console.log(data) (2)
});
  1. GET sample.json file từ https://s3.eu-central-1.amazonaws.com origin. Điều này được cho phép✅ bởi SOP và ngoài ra chúng ta cũng thấy 200 status code .

  2. Reads nội dung của file json và hiện thị nó ở console.Điều này bị cấm ❌ bởi SOP và chúng ta thấy exception bị vứt ra ở console

Vì vậy, JS (readJSON.js) thực sự đang thực hiện một cross-origin HTTP request, để fetch sample.json từ https://s3.eu-central-1.amazonaws.com.

Trình duyệt cho phép fetch sample.json nhưng nó không cho phép JS (readJSON.js) reading nó do quy tắc reading SOP.

Then, how to read cross origin resources❓

Chúng ta đang sống trong thời đại của microservices. Nhiều dịch vụ và APIs cho cùng một ứng dụng được deploy trong các domain khác nhau và điều đó có nghĩa là các origin khác nhau.

Để đọc các cross-origin resources, chúng ta cần sử dụng Cross Origin Resource Sharing (CORS) policy

CORS là một chính sách cho phép một/các danh sách origin được xác định trước để đọc một/các tài nguyên/resource.

⚡ Lab 4 : cross-origin write : allowed

Ở lab 3 và lab 4 chúng ta có thể thấy rằng same origin policy (SOP) cho phép embedding resource nhưng không thể reading chúng.

Ở lab này chúng ta sẽ xem SOP cũng cho phép writing(sending) requests...

  1. Sepup nhanh một domain ở requestcatcher. Nó là một HTTPs listener(webhook), bạn có thể dùng requestrepo....

  2. Trong khi bạn đang đọc page này thì mở browser Console

  3. Sau đó page đoạn JS phía dưới và thay thế AAAAAAAAAAAAAAAAAA bằng domain của bạn

url = "https://AAAAAAAAAAAAAAAAAA.requestcatcher.com/admin "
fetch(url,{method: 'POST', body: "yaaay!"}).then(response =>
response.text()).then(function (data) {
console.log(data)
});
  1. Đoạn code này sẽ tạo HTTP POST tới một endpoint giả /admin với payload "yaaay!". Nhấn enter để gửi.

  2. Chúng ta thấy rằng chúng đã có thể thực hiện một request thành công. Hình bên dưới show ra status 200 từ server. Có cảnh báo ở console về block reading nội dung của response(như cái đã được giải thích ở lab 3)

  3. Đi tới endpoint được request, chúng ta có thể thấy request của chúng ta gửi cùng với payload

Chúng ta có thể kết luận rằng same origin policy đã cho phép một JS từ Origin: https://secure-cookie-io

Để write HTTP request cross-origin: https://WHATEVER.requestcatcher.com/

Why is this important❓

Có một sự lầm tưởng lớn rằng same origin policu và CORS có thể bảo vệ lại tấn công CSRF.

Trong CSRF, kẻ tấn công có thể thực hiện các cuộc gọi HTTP thay mặt cho nạn nhân(HTTP giả mạo). Hiểu rằng yêu cầu giả mạo của kẻ tấn công sẽ đi tới endpoint là rất nguy hiểm.

Điều gì xảy ra nếu một requestcatcher origin như này:

https://transfer.your-bank.com/sendMoney

Sau đó, kẻ tấn công có thể giả mạo một code JS tương tự như code mà chúng ta đã làm ở trên và thay vì gửi “yaaay!”, kẻ tấn công sẽ chuyển tiền vào tài khoản của nó.

Đó sẽ là một cuộc tấn công CSRF thành công, vì request sẽ thành công tới endpoint. Kẻ tấn công thể read response từ endpoint do SOP, nhưng điều đó không còn quan trọng nữa vì endpoint đã nhận được request và sẽ xử lý khoản thanh toán.

Ở đây tôi có viết 2 bài về CSRF lab và một bài reasearch CSRF

Tổng kết

Cảm ơn mọi người đã đọc bài research của mình, mình sẽ cố gắng vừa research thêm những bài mới, một phần dành cho mình hiểu hơn các lỗ hổng ở web, một phần phục vụ bạn đọc...

Last updated