Tìm hiểu CORS

CORS (Cross-origin resource sharing) là cơ chế cho phép các tài nguyên của trang web (font, javascript …) có thể được truy vấn từ domain khác.

Same Origin Policy là gì?

Same Origin Policy (SOP) là chính sách bảo mật trên trình duyệt nhằm mục đích ngăn chặn javascript có thể tạo ra những request đến những nguồn khác với nguồn mà nó được trả về. Ba tiêu chí so sánh request bao gồm:

  • Domain
  • Protocol (giao thức)
  • Port (cổng)

Ví dụ site origin là http://www.example.com, các request sau vi phạm SOP:

  • http://www.example.net (khác domain)
  • https://example.com (khác protocol)
  • http://example.com:8080 (khác port)

Trong 1 số trường hợp SOP gây khó khăn cho dev. Ví dụ store.example.com cần xác thực tại login.example.com, nhưng việc gửi request không khả thi vì vi phạm SOP. Cho nên cần CORS để giải quyết vấn đề này.

Ví dụ:

  • Bạn đang đăng nhập facebook.
  • Bạn truy cập vào 1 website chứa mã độc. Trang web này sử dụng javascript để truy cập tin nhắn của bạn ở facebook.com/messages.
  • Nếu không có same-origin policy, trang web kia có thể thoải mái lấy dữ liệu của bạn và thao tác thay đổi dữ liệu.

Bạn có thể thử trên console và sẽ nhận được lỗi ngay:

JavaScript
$.get('https://facebook.com/messages')
Access to XMLHttpRequest at 'https://facebook.com/messages' from
origin 'xxx' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.

Trường hợp cần truy vấn tài nguyên từ domain khác, ví dụ API, bạn cần đến CORS. CORS sử dụng các HTTP Header để thông báo cho trình duyệt rằng 1 ứng dụng chạy ở origin này có thể truy cập được các tài nguyên ở origin khác. Một truy vấn được gọi là cross-origin nếu nó yêu cầu đến các tài nguyên ở origin khác (khác về giao thức, domain hoặc port).

Các trình duyệt cài đặt same-origin policy và tuân thủ nó chặt chẽ. Cài đặt XMLHttpRequest và Fetch API cũng đều tuân thủ chính sách này. Do vậy để truy vấn có thể nhận kết quả trả về, cần set http header phù hợp.

Lưu ý: các truy vấn dùng CORS:

  • XMLHttpRequest hoặc Fetch API đến domain khác.
  • WebGL Texture.
  • Ảnh, video được vẽ vào canvas sử dụng drawImage.
  • Web fonts truy vấn đến domain khác thông qua @fontface của CSS.

Tạo truy vấn CORS bằng XMLHttpRequest:

JavaScript
const createCORSRequest = (method, url) => {
    let xhr = new XMLHttpRequest();
    if ('withCredentials' in xhr) {
        // Kiểm tra XMLHttpRequest object có thuộc tính
        // withCredentials hay không
        // Thuộc tính này chỉ có ở XMLHttpRequest2
        xhr.withCredentials = true;
        
        xhr.open(method, url, true);
          
        request.onload = () => {
        const responseText = request.responseText;
        console.log(responseText);
}
request.onerror = () => {
    console.log('Error');
}
    } else {
        xhr = null;
    }
    return xhr;
}

const request = createCORSRequest('GET',
    'https://jsonplaceholder.typicode.com/posts/1');
if (!request) {
    throw new Error('CORS is not supported');
}

request.onload = () => {
    const responseText = request.responseText;
    console.log(responseText);
}
request.onerror = () => {
    console.log('Error');
}

request.send();

withCredentials

Mặc định các truy vấn CORS không gửi hoặc thiết lập bất cứ cookie nào trên trình duyệt. Nếu muốn sử dụng cookie trong truy vấn đó, ta phải đặt thuộc tính withCredentials = true. Khi đó cookie sẽ được tự động thêm vào request, cũng như tự động thiết lập nếu có phản hồi từ máy chủ. Nó được xử lý hoàn toàn tự động bởi trình duyệt.

Còn ở phía máy chủ, cần set HTTP Header Access-Control-Allow-Credentials là true.

Tạo truy vấn CORS bằng jQuery

Hàm $.ajax của jQuery hỗ trợ cả truy vấn CORS (cookie cũng được hỗ trợ mặc định).

Lưu ý: giá trị $.support.cors sẽ được gán là true nếu trình duyệt hỗ trợ CORS.

JavaScript
$.ajax({
    type: 'GET',
    url: 'https://jsonplaceholder.typicode.com/posts/1',
    success: data => {
        console.log(data);
    },
    error: () => {
        console.log('Error');
    }
})

Cấu hình máy chủ hỗ trợ CORS

Có 2 loại truy vấn, “đơn giản” và “không đơn giản”.

Một truy vấn được gọi là đơn giản nếu nó thỏa mãn những điều kiện sau:

  • Phương thức truy vấn là GET, HEAD, POST.
  • Giá trị của Content-Type là application/x-www-form-urlencoded, multipart/form-data, text/plain.
  • Không có event handler nào với event XMLHttpRequest.upload.
  • Không sử dụng đối tượng ReadableStream trong truy vấn.
  • Các http header sau phải khớp: Accept, Accept-Language, Content-Language, Last-Event-ID.

Những truy vấn này gọi là truy vấn “đơn giản” vì nó được coi như truy vấn thông thường từ trình duyệt mà không cần đến CORS, ví dụ submit 1 form html.

Những truy vấn “không đơn giản” cần CORS preflight. Có nghĩa là trước khi truy vấn được gửi, nó cần phải gửi 1 truy vấn trước bằng phương thức OPTIONS. Mục đích của truy vấn “preflight” này là nhằm kiểm tra truy vấn có thực sự an toàn để gửi và nhận hay không.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *