Published on

CSP là gì và nó ảnh hưởng như thế nào đến website của bạn?

Authors

CSP (Content-Security-Policy) hay nói rõ hơn là Content Security Policy header hiện đang dần trở nên phổ biến hơn bao giờ hết. Tuy nhiên, hiện tại mình chỉ thấy Thế giới di động 🔗 là một trong những website lớn tại Việt Nam triển khai CSP trên website của mình, còn lại thì rất ít. Vậy CSP là gì và tại sao chúng ta nên dùng nó?

  • Cập nhật 31/01: mình thêm vào cách theo dõi các vi phạm CSP trên trang WordPress và các vấn đề với thuộc tính unsafe-inline

Việc triển khai CSP thường tốn kha khá thời gian bởi rất ít công cụ hỗ trợ tự động. Vì vậy, bạn hãy đọc sơ qua một lần trước khi bắt tay vào làm nhé.

CSP là gì?

CSP là một biện pháp bảo mật từ phía trình duyệt, giúp ngăn chặn những request xấu được gọi từ website của chúng ta. CSP là tập hợp một danh sách an toàn (whitelist) những domain hay kiểu script, style, image, frame mà trình duyệt được load trên website của chúng ta. Điều này đảm bảo chỉ có những script, style, image, frame được chúng ta chỉ định có thể được tải, những request không nằm trong whitelist sẽ bị chặn ngay tức khắc.

CSP header được trả về trình duyệt từ webserver hoặc mã nguồn, tùy cách chúng ta cài đặt.

Tuấn đã có một bài viết riêng giới thiệu cách làm thế nào để ngăn chặn malware thực thi, làm chuyển hướng website WordPress không mong muốn. Tất cả chỉ cần một dòng CSP duy nhất. Mời các bạn xem qua:
> Hạn chế malware chuyển hướng website WordPress với CSP 🔗

Dễ thấy nhất, nếu một hacker xâm nhập được vào hệ thống của bạn mà chèn một thẻ <script> dẫn tới một tập tin javascript có ý đồ xấu nằm ở tên miền của hắn, ví dụ xauxa.com/jquery.min.js. Đây là dạng tấn công XSS stored 🔗 rất phổ biến mà các website WordPress hay gặp phải. Nhờ CSP header, xauxa.com không nằm trong whitelist nên không cách nào tải và thực thi được, từ đó bảo vệ bạn và người truy cập của bạn.

true

Không chỉ bảo vệ khỏi những mã độc, CSP còn giúp bảo vệ chúng ta khỏi việc thu thập dữ liệu không mong muốn mà không hề hay biết. Trong hình là các request được gửi từ chính trình duyệt của người dùng, đây có thể là thu thập thông tin sử dụng, hoặc mã độc, hoặc mã quảng cáo thông qua các extension không an toàn.

Như ảnh chụp màn hình trên, một số trình duyệt liên tục thu thập thông tin người sử dụng, hoặc người dùng cài đặt những extension không rõ nguồn gốc dẫn tới việc bị chèn mã độc, quảng cáo trên trang mặc dù chủ website không cố ý. Nhờ CSP, người dùng sẽ truy cập website chúng ta với giao diện "sạch" nhất có thể vì trình duyệt đã chặn ngay từ đầu rồi.

Một ví dụ vui vẻ khác là tiện ích BeeCost - theo dõi giá cả không thể thực thi trên website có sử dụng CSP, ví dụ như thegioididong.com.

true

Từ đây dẫn tới các ý tưởng chặn các trình duyệt, extension thu thập thông tin và xử lý để tạo lợi thế dựa vào độ phổ biến như trường hợp CocCoc "gợi ý giá", BeeCost giúp khách hàng biết được lịch sử giá,...


Các "chỉ thị" của CSP

Không chỉ ngăn chặn tải Javascript, CSP cũng có tác dụng với hình ảnh, CSS, frame, webfont, media và rất nhiều chỉ thị (directive) khác. Điều này tạo nên một lớp bảo mật vừa đủ cho các website, giúp người truy cập chỉ tải và sử dụng đúng những thứ bạn mong muốn.

Tên directiveGiải thích
script-srcChỉ định những nguồn được tải cho các tài nguyên Javascript
style-srcChỉ định những nguồn được tải cho các tài nguyên CSS
connect-srcChỉ định những nguồn được tải cho các request Ajax, WebSocket
img-srcChỉ định những nguồn được tải cho các tài nguyên là hình ảnh
font-srcChỉ định những nguồn được tải cho các tài nguyên là webfont
frame-srcChỉ định những nguồn được tải cho các tài nguyên là <iframe>

Đó là những chỉ thị cho từng loại tài nguyên khác nhau, còn cách dùng thì mời bạn đọc sang phần tiếp theo nhé.


Thuộc tính và giá trị của CSP directive

Mỗi chỉ thị có nhiều domain được thêm vào khác nhau, tuy nhiên có một số cách sử dụng nên biết để các bạn có thể dùng thoải mái hơn:

Giá trị của CSP directiveGiải thích
*Giá trị wildcard, tương đương allow all
'self'Là chính domain mà người dùng đang truy cập
'none'Không cho phép tải từ bất cứ nguồn nào, kể cả domain hiện tại
data:Cho phép những tài nguyên sử dụng data: được tải, ví dụ hình ảnh mã hóa base64
www.domain.com 🔗Cho phép tải từ www.domain.com 🔗. Tuy nhiên hãy lưu ý, www.domain.com 🔗 và domain.com được xem là hoàn toàn khác nhau
domain.comCho phép tải từ domain.com. Tuy nhiên không cho phép tải từ www.domain.com 🔗, sub.domain.com, cdn.domain.com,...
*.domain.comCho phép tải từ www.domain.com 🔗, sub.domain.com, cdn.domain.com,..
https://domain.com 🔗Chỉ cho phép tải từ domain.com sử dụng giao thức HTTPS
https:Chỉ cho phép tải từ nguồn sử dụng giao thức HTTPS

Hãy lấy ví dụ như bài viết mà mình có giới thiệu ở đầu trang, từ đó phân tích xem trình duyệt sẽ cho phép tải tài nguyên từ những nguồn nào nhé.

script-src 'self' 'unsafe-inline' 'unsafe-eval'
    www.googletagmanager.com connect.facebook.net
    www.googleadservices.com www.google-analytics.com
    googleads.g.doubleclick.net onesignal.com
    tpc.googlesyndication.com

Vậy thì trình duyệt sẽ cho phép tải Javascript từ các nguồn sau:

Làm thế nào để biết website chúng ta tải từ tên miền nào?

Thật vậy. Tưởng chừng chúng ta có thể nắm trong tay danh sách tên miền mà website tải tài nguyên về, tuy nhiên danh sách tên miền có thể lớn hơn nhiều.

Có thể cũng vì lí do này mà các website có đặt quảng cáo sẽ không thể hoặc rất hạn chế khi sử dụng CSP header. Đó là lí do vì sao hiện tại rất ít các trang web tại Việt Nam triển khai CSP.

Lấy ví dụ một website của mình có sử dụng Google Ads tracking code. Không chỉ một tên miền mà có 2 đến 3 tên miền khác được sử dụng. Vì thế việc kiểm tra trước khi triển khai là vô cùng quan trọng.

true

Công cụ mình hay sử dụng là WebPageTest.org 🔗 để lấy tất cả các tên miền mà website mình tải tài nguyên về, từ đó lập danh sách và tiếp tục thử - sai cho đến khi trình duyệt không còn báo một tên miền nào bị chặn bởi CSP nữa.


Cách sử dụng CSP trên WordPress

Mặc dù CSP nên được triển khai ở phía webserver (Apache, Nginx,...) vì lí do bảo mật. Tuy nhiên việc chèn CSP header vào website WordPress là khá đơn giản và có thể thực hiện được bằng nhiều cách.

Xin lưu ý các plugin hay đoạn code chỉ giúp bạn chèn CSP header vào website, còn việc nội dung như thế nào, giá trị ra sao phải tự do bạn quyết định. Hiện tại mình chưa thấy có công cụ nào tự động tạo ra giá trị CSP.

Sử dụng plugin

Đầu tiên, cách đơn giản nhất mà không phải động đến code là cài đặt plugin WP Content Security Plugin 🔗. Sau khi cài đặt và kích hoạt plugin xong, bạn có thể chuyển đến Settings > CSP Options để bắt đầu cấu hình CSP.

true

Vì Content Security Policy thật sự là một tính năng mới, không thể cứ click click rồi hoàn thành cài đặt được nên trông phần cấu hình có vẻ hơi rối. Tuy nhiên nếu bạn đã đọc qua phần đầu bài viết rồi thì sẽ cảm thấy ổn thôi, có thể tự tin chuyển qua tab Content Security Policies để cấu hình cho các directive.

Chèn code trực tiếp

Thường các trang cần thử nhanh một cấu hình cho website nào đó thì mình sẽ dùng cách này, chèn trực tiếp vào tập tin functions.php của theme đang dùng là được. Tuy xấu và khó nhìn nhưng rất hiệu quả.

function header_csp_generate(){  
     header("Content-Security-Policy:  
          default-src 'self';  
          script-src 'self' 'unsafe-inline' www.googletagmanager.com www.google-analytics.com tagmanager.google.com;  
          style-src 'self' 'unsafe-inline' tagmanager.google.com;  
          img-src 'self' data: www.googletagmanager.com www.google-analytics.com www.google.com www.google.com.vn i.ytimg.com https://*.gravatar.com https://images.dmca.com;  
          frame-src www.googletagmanager.com www.google.com www.youtube.com;  
          font-src 'self';  
          connect-src 'self' \*.google.com google.com google.com.vn www.google-analytics.com;  
          object-src 'none'");  
}  
add_filter('wp_head', 'header_csp_generate');

Sử dụng webserver để cấu hình

Đây là cách mà mình khuyên dùng để đảm bảo website vẫn được bảo mật nhất có thể kể cả khi có thể truy cập và chỉnh sửa các tập tin mã nguồn của website. Tuy nhiên chúng sẽ gặp khó với việc cấu hình máy chủ dẫn tới CSP header của chúng ta vẫn được bảo vệ một cách tuyệt đối.

Nginx

Chèn vào tập tin /etc/nginx/nginx.conf, đặt trong block http, sau đó restart lại Nginx.

http {  
    # ....  
    add_header Content-Security-Policy "script-src 'self' 'unsafe-inline' 'unsafe-eval' www.googletagmanager.com connect.facebook.net www.googleadservices.com www.google-analytics.com googleads.g.doubleclick.net onesignal.com tpc.googlesyndication.com;";  
    # ....  
}

Apache

Với apache có thể đơn giản hơn, bạn chỉ việc chèn vào tập tin .htaccess (vẫn khuyên không nên vì hacker có thể can thiệp, nếu không còn cách nào khác, hãy chmod hợp lý trước khi dùng) là được, hoặc chèn vào /etc/httpd/conf/httpd.conf. Sau đó restart lại Apache.

Header set Content-Security-Policy "script-src 'self' 'unsafe-inline' 'unsafe-eval' www.googletagmanager.com connect.facebook.net www.googleadservices.com www.google-analytics.com googleads.g.doubleclick.net onesignal.com tpc.googlesyndication.com;";

Kiểm tra CSP đã được thực thi chưa

Có rất nhiều website giúp chúng ta kiểm tra việc cấu hình CSP đã hợp lý hay chưa. Mình khuyên dùng công cụ CSP Evaluator 🔗 của chính Google phát triển để kiểm tra. Chỉ cần nhập tên miền vào ô bên dưới và nhấn Check CSP thì mọi directive sẽ hiển thị ra kèm với đánh giá từ Google.

true

Một công cụ khác mà mình hay sử dụng để kiểm tra tổng thể mức độ bảo mật của website dựa vào các response header là Security Headers 🔗. Ngoài CSP, họ còn kiểm tra rất nhiều header khác giúp tăng cường bảo mật cho website như X-XSS-Protection giúp bảo vệ người dùng khỏi tấn công XSS, X-Frame-Options bảo vệ website khỏi việc bị nhúng từ một trang khác,...

true


Theo dõi vi phạm CSP

Một tính năng rất hay của CSP là Report URI giúp trình duyệt báo cáo lại cho chúng ta những vi phạm CSP xảy ra ngay trên trang của mình, từ đó biết được hacker đang nhắm tới dạng tấn công nào tương tự ảnh chụp ở đầu bài. Từ những báo cáo này, bạn có thể kiểm tra lại mức độ bảo mật của CSP hoặc nới lỏng một số domain an toàn mà bạn chưa cho vào whitelist.

Cách sử dụng rất đơn giản, mỗi khi có một vi phạm CSP trên trang, trình duyệt sẽ gửi thông tin kèm các dạng vi phạm này đến một dịch vụ định sẵn để chúng ta có thể theo dõi lại. Một trong những dịch vụ miễn phí giúp chúng ta theo dõi vi phạm CSP là https://report-uri.com 🔗. Phiên bản miễn phí chỉ hỗ trợ 10,000 report / tháng nhưng theo mình thấy thì đã quá đủ rồi.

Sau khi đã đăng nhập được vào trang quản trị của Report URI, hãy nhấp vào trang Setup 🔗, tiến hành điền 1 subdomain theo ý thích của bạn để định danh với các trang khác.

true

Khi đã điền subdomain bạn muốn, hãy nhìn qua cột trái, sẽ có luôn một đường dẫn theo subdomain đã tạo. Chúng ta không cần truy cập vào đó mà hãy chèn đường dẫn này vào cấu hình CSP theo cú pháp:

report-uri https://datuan.report-uri.com/r/d/csp/reportOnly

Và cấu hình Report URI cho CSP cho trang WordPress trở thành:

function header_csp_generate(){  
     header("Content-Security-Policy:  
          default-src 'self';  
          script-src 'self' 'unsafe-inline' www.googletagmanager.com www.google-analytics.com tagmanager.google.com;  
          style-src 'self' 'unsafe-inline' tagmanager.google.com;  
          img-src 'self' data: www.googletagmanager.com www.google-analytics.com www.google.com www.google.com.vn i.ytimg.com https://*.gravatar.com https://images.dmca.com;  
          frame-src www.googletagmanager.com www.google.com www.youtube.com;  
          font-src 'self';  
          connect-src 'self' \*.google.com google.com google.com.vn www.google-analytics.com;  
          object-src 'none';  
          report-uri https://datuan.report-uri.com/r/d/csp/reportOnly");
}  
add_filter('wp_head', 'header_csp_generate');

Đợi khoảng 30 phút và bạn sẽ nhận được các báo cáo này. Tuy nhiên không có báo cáo gì là tốt nhất vì điều đó chứng minh website không tải các tài nguyên từ nguồn xấu.


Vấn đề gặp phải

Hiện tại, việc triển khai CSP sẽ dễ dàng nếu chúng ta không quá nghiêm túc về vấn đề bảo mật. Thật vậy, hai thuộc tính unsafe-inlineunsafe-eval sẽ bị đánh dấu là không an toàn cho website kể cả khi chúng ta đã triển khai CSP.

Nếu trong đoạn ví dụ ở đầu bài viết, hacker khôn ngoan hơn và chèn trực tiếp đoạn mã Javascript vào website thay vì tải từ domain xauxa.com, thật sự không gì ngăn chặn được nếu chúng ta vẫn sử dụng unsafe-inline.

Vậy tại sao không... bỏ unsafe-inline ra khỏi cấu hình CSP?

Vấn đề cũng không đơn giản như vậy. WordPress là nền tảng mở, có thể cài đặt, tùy biến rất nhiều và chuyện các plugin tự động chèn các đoạn Javascript inline (an toàn) vào website là hết sức bình thường.

Bằng cách gỡ unsafe-inline, chúng ta đã vô tình vô hiệu hóa luôn tất cả đoạn Javascript inline (an toàn) trên website, dẫn tới một hoặc nhiều chức năng không thể thực hiện được nữa.

Mình có nghe nói về CSP nonce, có thể cho phép xử lý các đoạn Javascript inline?

Câu chuyện tiếp tục không dễ dàng. Một số plugin luôn luôn có mã Javascript thay đổi ở mỗi trang, mỗi bài viết. Ví dụ như Contact Form 7 hoặc các plugin review bài viết, việc chèn ID bài viết vào các biến vào các đoạn Javascript inline là hết sức bình thường. Mà như vậy, khiến chúng ta không thể tạo nonce dùng chung ở tất cả bài viết này.


Kết

Việc triển khai CSP header cho website hiện nay theo mình là tối quan trọng. Có CSP càng sớm càng tốt, người dùng và chúng ta luôn là người được lợi.

Dù rằng việc cấu hình CSP không thể tự động, hoặc có những thuộc tính 'unsafe-inline' có thể làm CSP không hiệu quả. Tuy nhiên, mình đã chứng thực rất nhiều trường hợp mà CSP bảo vệ được website của chúng ta.