Ngăn chặn lỗ hổng tập lệnh trên nhiều trang web dựa trên DOM với Loại đáng tin cậy

Krzysztof Kotowicz
Krzysztof Kotowicz

Hỗ trợ trình duyệt

  • 83
  • 83
  • x
  • x

Nguồn

Tập lệnh trên nhiều trang web dựa trên DOM (DOM XSS) xảy ra khi dữ liệu từ một nguồn do người dùng kiểm soát (như tên người dùng hoặc URL chuyển hướng lấy từ mảnh URL) đến một bếp, là một hàm như eval() hoặc một hàm setter có thể thực thi mã JavaScript tuỳ ý..innerHTML

DOM XSS là một trong những lỗ hổng bảo mật web phổ biến nhất và các nhóm nhà phát triển thường vô tình đưa loại lỗ hổng này vào ứng dụng của họ. Các kiểu đáng tin cậy cung cấp cho bạn các công cụ để viết, đánh giá tính bảo mật và đảm bảo các ứng dụng không gặp phải lỗ hổng bảo mật DOM XSS bằng cách đảm bảo an toàn cho các hàm API web nguy hiểm theo mặc định. Các Loại đáng tin cậy có sẵn dưới dạng polyfill cho các trình duyệt chưa hỗ trợ các loại này.

Thông tin khái quát

Trong nhiều năm, DOM XSS đã là một trong những lỗ hổng bảo mật web nguy hiểm và phổ biến nhất.

Có hai loại tập lệnh trên nhiều trang web. Một số lỗ hổng XSS do mã phía máy chủ tạo ra mã HTML tạo thành trang web một cách không an toàn. Một số khác lại có nguyên nhân gốc rễ từ máy khách, trong đó mã JavaScript gọi các hàm nguy hiểm có nội dung do người dùng kiểm soát.

Để ngăn chặn XSS phía máy chủ, đừng tạo HTML bằng cách nối các chuỗi. Thay vào đó, hãy sử dụng các thư viện tạo mẫu tự động thoát theo bối cảnh an toàn, cùng với Chính sách bảo mật nội dung dựa trên một lần duy nhất để giảm thiểu lỗi bổ sung.

Giờ đây, các trình duyệt cũng có thể giúp ngăn chặn XSS dựa trên DOM phía máy khách bằng cách sử dụng Loại đáng tin cậy.

Giới thiệu về API

Loại đáng tin cậy hoạt động bằng cách khoá các chức năng bồn lưu trữ dữ liệu rủi ro sau đây. Có thể bạn đã nhận ra một vài tính năng trong số đó, vì các nhà cung cấp trình duyệt và khung web đã ngăn bạn sử dụng các tính năng này vì lý do bảo mật.

Các loại đáng tin cậy yêu cầu bạn xử lý dữ liệu trước khi truyền dữ liệu đó vào các hàm bồn rửa này. Việc chỉ sử dụng chuỗi sẽ không thành công vì trình duyệt không biết liệu dữ liệu có đáng tin cậy hay không:

Không nên
anElement.innerHTML  = location.href;
Khi bật tính năng Loại đáng tin cậy, trình duyệt sẽ gửi TypeError và ngăn việc sử dụng bồn lưu trữ dữ liệu DOM XSS bằng một chuỗi.

Để biểu thị rằng dữ liệu đã được xử lý an toàn, hãy tạo một đối tượng đặc biệt – Loại đáng tin cậy.

Nên
anElement.innerHTML = aTrustedHTML;
  
Khi bật tính năng Loại đáng tin cậy, trình duyệt sẽ chấp nhận đối tượng TrustedHTML cho các bồn lưu trữ dữ liệu dự kiến có đoạn mã HTML. Ngoài ra, còn có các đối tượng TrustedScriptTrustedScriptURL cho các bồn lưu trữ dữ liệu nhạy cảm khác.

Các loại đáng tin cậy làm giảm đáng kể khu vực tấn công DOM XSS của ứng dụng. Công cụ này giúp đơn giản hoá việc đánh giá bảo mật và cho phép bạn thực thi các quy trình kiểm tra bảo mật dựa trên loại được thực hiện khi biên dịch, tìm lỗi mã nguồn hoặc gói mã trong thời gian chạy trên trình duyệt.

Cách sử dụng Loại đáng tin cậy

Chuẩn bị cho các báo cáo vi phạm Chính sách bảo mật nội dung

Bạn có thể triển khai một trình thu thập báo cáo, chẳng hạn như reporting-api-processor nguồn mở hoặc go-csp-collector, hoặc sử dụng một trong các trình thu thập thương mại tương đương. Bạn cũng có thể thêm tính năng ghi nhật ký tuỳ chỉnh và gỡ lỗi vi phạm trong trình duyệt bằng cách sử dụng ReportingObserver:

const observer = new ReportingObserver((reports, observer) => {
    for (const report of reports) {
        if (report.type !== 'csp-violation' ||
            report.body.effectiveDirective !== 'require-trusted-types-for') {
            continue;
        }

        const violation = report.body;
        console.log('Trusted Types Violation:', violation);

        // ... (rest of your logging and reporting logic)
    }
}, { buffered: true });

observer.observe();

hoặc bằng cách thêm một trình nghe sự kiện:

document.addEventListener('securitypolicyviolation',
    console.error.bind(console));

Thêm tiêu đề CSP chỉ để báo cáo

Thêm tiêu đề Phản hồi HTTP sau đây vào tài liệu mà bạn muốn di chuyển sang Loại đáng tin cậy:

Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Giờ đây, tất cả các lỗi vi phạm đều được báo cáo cho //my-csp-endpoint.example, nhưng trang web vẫn tiếp tục hoạt động. Phần tiếp theo giải thích cách hoạt động của //my-csp-endpoint.example.

Xác định các lỗi vi phạm về Loại đáng tin cậy

Từ nay trở đi, mỗi khi phát hiện lỗi vi phạm, trình duyệt sẽ gửi báo cáo đến report-uri đã định cấu hình. Ví dụ: khi ứng dụng của bạn truyền một chuỗi đến innerHTML, trình duyệt sẽ gửi báo cáo sau đây:

{
"csp-report": {
    "document-uri": "https://my.url.example",
    "violated-directive": "require-trusted-types-for",
    "disposition": "report",
    "blocked-uri": "trusted-types-sink",
    "line-number": 39,
    "column-number": 12,
    "source-file": "https://my.url.example/script.js",
    "status-code": 0,
    "script-sample": "Element innerHTML <img src=x"
}
}

Tức là trong https://my.url.example/script.js trên dòng 39, innerHTML được gọi bằng chuỗi bắt đầu bằng <img src=x. Thông tin này sẽ giúp bạn thu hẹp những phần mã có thể đang giới thiệu DOM XSS và cần thay đổi.

Khắc phục lỗi vi phạm

Có một số cách để khắc phục lỗi vi phạm về Loại đáng tin cậy. Bạn có thể xoá mã vi phạm, dùng thư viện, tạo chính sách Loại đáng tin cậy hoặc tạo chính sách mặc định trong phương án cuối cùng.

Viết lại mã vi phạm

Có thể mã không phù hợp không cần thiết nữa hoặc có thể được viết lại mà không cần các hàm gây ra lỗi vi phạm:

Nên
el.textContent = '';
const img = document.createElement('img');
img.src = 'xyz.jpg';
el.appendChild(img);
Không nên
el.innerHTML = '<img src=xyz.jpg>';

Sử dụng thư viện

Một số thư viện đã tạo các Loại đáng tin cậy mà bạn có thể truyền đến các hàm bồn lưu trữ dữ liệu. Ví dụ: bạn có thể sử dụng DOMPurify để dọn dẹp một đoạn mã HTML, xoá các phần tải trọng XSS.

import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(html, {RETURN_TRUSTED_TYPE: true});

DOMPurify hỗ trợ các Loại đáng tin cậy và trả về HTML đã dọn dẹp được gói trong đối tượng TrustedHTML để trình duyệt không tạo ra lỗi vi phạm.

Tạo chính sách về Kiểu đáng tin cậy

Đôi khi, bạn không thể xoá mã gây ra lỗi vi phạm và không có thư viện để dọn dẹp giá trị cũng như tạo Loại đáng tin cậy cho bạn. Trong những trường hợp đó, bạn có thể tự tạo đối tượng Loại đáng tin cậy.

Trước tiên, hãy tạo một chính sách. Chính sách là nhà máy dành cho các Loại đáng tin cậy nhằm thực thi một số quy tắc bảo mật nhất định đối với dữ liệu đầu vào của chúng:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  const escapeHTMLPolicy = trustedTypes.createPolicy('myEscapePolicy', {
    createHTML: string => string.replace(/\</g, '&lt;')
  });
}

Mã này tạo một chính sách có tên là myEscapePolicy. Chính sách này có thể tạo các đối tượng TrustedHTML bằng cách sử dụng hàm createHTML(). Các quy tắc < thoát HTML đã xác định để ngăn việc tạo các phần tử HTML mới.

Hãy sử dụng chính sách như sau:

const escaped = escapeHTMLPolicy.createHTML('<img src=x onerror=alert(1)>');
console.log(escaped instanceof TrustedHTML);  // true
el.innerHTML = escaped;  // '&lt;img src=x onerror=alert(1)>'

Sử dụng chính sách mặc định

Đôi khi, bạn không thể thay đổi mã vi phạm, chẳng hạn như trong trường hợp bạn đang tải thư viện của bên thứ ba từ một CDN. Trong trường hợp đó, hãy sử dụng chính sách mặc định:

if (window.trustedTypes && trustedTypes.createPolicy) { // Feature testing
  trustedTypes.createPolicy('default', {
    createHTML: (string, sink) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true})
  });
}

Chính sách có tên default được dùng bất cứ khi nào một chuỗi được sử dụng trong bồn lưu trữ dữ liệu chỉ chấp nhận Loại đáng tin cậy.

Chuyển sang thực thi Chính sách bảo mật nội dung

Khi ứng dụng của bạn không còn vi phạm nữa, bạn có thể bắt đầu thực thi các Loại đáng tin cậy:

Content-Security-Policy: require-trusted-types-for 'script'; report-uri //my-csp-endpoint.example

Giờ đây, cho dù ứng dụng web của bạn có phức tạp đến mức nào, điều duy nhất có thể gây ra lỗ hổng bảo mật DOM XSS là mã ở một trong các chính sách của bạn. Bạn có thể khoá mã đó hơn nữa bằng cách giới hạn việc tạo chính sách.

Tài liệu đọc thêm