Subresource Integrity (SRI): ‘Lớp Giáp’ Chống Mã Độc Khi Dùng CDN

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Vấn đề: Bạn có thực sự tin tưởng CDN mình đang dùng?

Hầu hết chúng ta đều có thói quen nhúng các thư viện như jQuery, Bootstrap hay FontAwesome qua các CDN lớn như Cloudflare, Google hay cdnjs. Lợi ích rất rõ ràng: tốc độ tải nhanh, tiết kiệm băng thông server và tận dụng được cache trình duyệt của người dùng.

Tuy nhiên, sau khi audit bảo mật cho nhiều dự án, mình nhận thấy một lỗ hổng chí mạng. Đa số developer chỉ copy-paste link script mà quên mất việc xác thực nội dung file.

Thực tế đã chứng minh đây không phải nỗi lo hão huyền. Hãy nhìn vào vụ tấn công Polyfill.io gần đây, nơi hơn 100.000 website đã vô tình tải mã độc vì domain CDN bị đổi chủ. Khi server CDN bị chiếm quyền, hacker có thể thay file jquery.min.js xịn bằng một bản “độ” thêm mã độc để đánh cắp thông tin thẻ tín dụng hoặc cookie. Trình duyệt vẫn thực thi đoạn mã này vì nó hoàn toàn tin tưởng vào domain của CDN. Đây chính là kiểu tấn công Supply Chain Attack (tấn công chuỗi cung ứng) cực kỳ nguy hiểm.

Để chặn đứng nguy cơ này, Subresource Integrity (SRI) chính là lớp giáp bảo vệ cuối cùng cho website của bạn.

Cơ chế hoạt động của Subresource Integrity (SRI)

SRI cho phép trình duyệt kiểm tra xem file tải về có bị chỉnh sửa trái phép hay không thông qua mã băm (hash).

Khi bạn nhúng một file kèm mã hash, trình duyệt sẽ thực hiện quy trình 4 bước:

  1. Tải file từ CDN về bộ nhớ tạm.
  2. Tự tính toán mã hash của file đó bằng thuật toán bạn chỉ định (thường là SHA-384).
  3. So sánh mã hash vừa tính được với mã hash bạn đã khai báo trong thuộc tính integrity.
  4. Nếu khớp, trình duyệt mới thực thi file. Nếu sai lệch dù chỉ một dấu phẩy, trình duyệt sẽ chặn đứng file đó và báo lỗi đỏ rực trong console.

Nhờ cơ chế này, ngay cả khi server CDN bị hack, mã độc cũng không có cơ hội chạy trên máy khách hàng của bạn.

Cách triển khai SRI vào dự án thực tế

1. Tạo mã Hash thủ công

Với các dự án nhỏ chỉ dùng vài thư viện, bạn có thể tạo mã băm nhanh chóng qua terminal. Giả sử cần tạo mã SHA-384 cho file app.js.

Trên Linux hoặc macOS, bạn dùng lệnh:

cat app.js | openssl dgst -sha384 -binary | openssl base64 -A

Kết quả sẽ là một chuỗi Base64 dài. Nếu ngại gõ lệnh, hãy dùng srihash.org. Bạn chỉ cần dán link CDN, công cụ này sẽ tự sinh đoạn code HTML hoàn chỉnh.

2. Cấu hình HTML với thuộc tính integrity

Khi đã có mã hash, bạn thêm nó vào thẻ <script> hoặc <link>. Đừng quên thuộc tính crossorigin="anonymous". Thiếu nó, trình duyệt sẽ bỏ qua việc kiểm tra SRI vì lý do bảo mật liên tên miền.

Ví dụ nhúng Bootstrap chuẩn chỉnh:

<!-- Nhúng CSS -->
<link rel="stylesheet" 
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" 
      integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" 
      crossorigin="anonymous">

<!-- Nhúng JS -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" 
        integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" 
        crossorigin="anonymous"></script>

3. Tự động hóa với Webpack

Việc tạo mã hash thủ công cho hàng chục file mỗi khi update là điều bất khả thi. Nếu dùng Webpack, hãy để plugin webpack-subresource-integrity lo liệu.

Cài đặt plugin:

npm install webpack-subresource-integrity --save-dev

Cấu hình trong webpack.config.js:

const { SubresourceIntegrityPlugin } = require("webpack-subresource-integrity");

module.exports = {
  output: {
    crossOriginLoading: "anonymous",
  },
  plugins: [
    new SubresourceIntegrityPlugin({
      hashFuncNames: ["sha384"],
      enabled: process.env.NODE_ENV === "production",
    }),
  ],
};

Plugin này sẽ tự tính toán và chèn mã hash vào file HTML mỗi khi bạn build production.

4. Chiến lược Fallback (Dự phòng khi CDN lỗi)

Điểm yếu của SRI là nếu mã hash không khớp, trình duyệt sẽ chặn file hoàn toàn. Website của bạn có thể bị vỡ giao diện nếu CSS không load được.

Giải pháp tốt nhất là luôn chuẩn bị một bản copy nội bộ (local). Dưới đây là mẹo nhỏ bằng JavaScript để tự động load lại file từ server nhà nếu CDN gặp sự cố:

<script src="https://code.jquery.com/jquery-3.7.0.min.js" 
        integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" 
        crossorigin="anonymous"></script>

<script>
  if (window.jQuery === undefined) {
    document.write('<script src="/js/vendor/jquery-3.7.0.min.js"><\/script>');
  }
</script>

Những lưu ý không thể bỏ qua

  • CORS là bắt buộc: Server CDN phải hỗ trợ CORS. Hầu hết các ông lớn như Cloudflare hay JSDelivr đều đã bật sẵn tính năng này.
  • Cẩn thận khi nâng cấp: Khi đổi version thư viện (ví dụ từ v3.6.0 lên v3.7.0), mã hash chắc chắn sẽ thay đổi. Nếu quên cập nhật integrity, trình duyệt sẽ chặn file ngay lập tức.
  • Ưu tiên SHA-384: Đây là tiêu chuẩn vàng hiện nay, vừa bảo mật cao vừa không gây gánh nặng cho hiệu năng trình duyệt.

Lời kết

Triển khai SRI tốn rất ít công sức nhưng giá trị bảo mật mang lại cực kỳ lớn. Đừng để một sơ suất nhỏ khiến hàng ngàn người dùng của bạn gặp rủi ro. Hãy kiểm tra lại các thẻ script trên website của mình ngay hôm nay!

Share: