Thiết lập Content Security Policy trong Ruby on Rails

Chúng tôi vui mừng chia sẻ kiến thức về từ khóa Csp la gi để tối ưu hóa nội dung trang web và chiến dịch tiếp thị trực tuyến. Bài viết cung cấp phương pháp tìm kiếm, phân tích và lựa chọn từ khóa phù hợp, cùng với chiến lược và công cụ hữu ích. Hy vọng thông tin này sẽ giúp bạn xây dựng chiến lược thành công và thu hút lưu lượng người dùng. Cảm ơn sự quan tâm và hãy tiếp tục theo dõi blog để cập nhật kiến thức mới nhất.

Content Security Policy (CSP) là một cách hữu hiệu để giảm thiểu hoặc loại bỏ hoàn toàn các lỗ hổng Cross Site Scripting (XSS). Với CSP, tất cả chúng ta có thể chặn inline script và các script từ những nguồn không đáng tin cậy. Tất cả chúng ta khái niệm policy thông qua một HTTP header chứa các rule giành cho tất cả những loại tài nguyên.

Bạn Đang Xem: Thiết lập Content Security Policy trong Ruby on Rails

Mặt khác, điều đó cũng tồn tại tức là tất cả chúng ta phải chuyển các đoạn inline script ra các file riêng biệt. Dù sao thì đó cũng là 1 trong điều nên làm và nó sẽ được chấp nhận tái sử dụng 1 lượng code to nhiều hơn.

Sau này là ví dụ của một HTTP header mà chỉ được chấp nhận trình duyệt load tài nguyên (scripts, CSS, fonts, ảnh…) từ cùng nguồn (same origin – self). Ngoài ra script từ Google Analytics cũng được được chấp nhận để các đoạn code tracking có thể chạy được. Mọi thứ khác sẽ bị chặn.

Content-Security-Policy: default-src ‘self’; script-src ‘self’ https://www.google-analytics.com;

List các directive và giá trị của chúng có thể được xem ở đây.

CSP 1.0 được tương trợ bởi khoảng chừng 80% trình duyệt hiện nay, gồm có cả trình duyệt trên di động (iOS, Android browser từ bản 4.4, Chrome trên Android). IE 10 và 11 chỉ tương trợ header kiểu cũ X-Content-Security-Policy và chỉ sandbox directive.

Không mong muốn là IE trước phiên bản Edgle 12 không tương trợ việc whitelist nguồn của đa số tài nguyên. Nếu khách hàng muốn làm điều đó bạn nên làm dùng header Content-Security-Policy chứ không phải header kiểu cũ X-Content-Security-Policy. Mặt khác, đừng bao giờ gửi cả hai header, điều này sẽ làm 1 số trình duyệt bị nhầm lẫn. CSP header có khả năng tương thích ngược nên so với 1 số trình duyệt rất cũ header này sẽ chỉ mất công dụng chứ không khiến ra tác động ảnh hưởng gì khác.

Phiên bản 1 của đa số tiêu chuẩn về CSP đã khái niệm rất nhiều những thứ cần có. CSP 2.0 ngày nay (và cả phiên bản trung gian 1.1) đã thêm một số directive, chủ yếu liên quan đến frame, nguồn cho endpoint của form , các plugin được được chấp nhận. Chúng cũng tương trợ nonce để sign các inline style và script với một mã hash duy nhất.

Việc tương trợ các directive mới vẫn còn chưa hoàn thiện, chỉ có Chrome là tương trợ tất cả những directive. Firefox vẫn không đủ directive plugin-types và child-src. Các trình duyệt khác thì còn chưa tương trợ các directive mới.

Điều gì sẽ xẩy ra nếu 1 directive nào đó không được tương trợ bởi trình duyệt? Sau này là thông tin của Safari khi thấy directive child-src mà nó đang chưa tương trợ

Tuy nhiên thì những directive khác vẫn hoạt động thường nhật.

Nói gì thì nói nhưng những thông tin như vậy trông có vẻ là không professional, sẽ tốt hơn nếu có thể nhận diện trình duyệt của người dùng và chỉ gửi những direcitve mà nó tương trợ. Ví dụ như GitHub chỉ gửi các directive child-src, form-action, frame-ancestors và plugin-types tới Chrome mà không gửi tới Safari. Firefox thì sẽ không còn được gửi child-src và plugin-types.

May mắn là với Rails tất cả chúng ta không phải đọc user agent rồi set các rule tương ứng cho từng trình duyệt vì đã có gem SecureHeaders làm điều đó một cách tự động hóa.

CSP đi kèm với một tính năng rất hữu ích, đó là văn bản báo cáo các trường hợp vi phạm policy. tin tức về các trường hợp vi phạm sẽ tiến hành hiển thị ở console của trình duyệt và cũng tồn tại thể được gửi tới 1 URL thông qua phương thức POST. tin tức khi đó có dạng JSON như dưới

{“csp-report”: {“document-uri”:”…”, “violated-directive”:”script-src ‘self’ https://ajax.googleapis.com”, “original-policy”:”…”, “blocked-uri”:”https://cdnjs.cloudflare.com”} }

Nếu tất cả chúng ta sử dụng header Content-Security-Policy-Report-Only thay cho header Content-Security-Policy thì trình duyệt sẽ chỉ gửi thông tin khi có vi phạm mà không chặn việc load bất kì tài nguyên nào. Cả hai header đều tương trợ directive report-uri để thông tư nơi mà văn bản báo cáo vi phạm được gửi tới.

Xem Thêm : M-TP là gì? Ý nghĩa nghệ danh của Sơn Tùng MTP

So với Rails, tất cả chúng ta cần có một controller để nhận và xử lí các văn bản báo cáo được gửi tới. Các bạn cũng có thể vào đây để xem 1 ví dụ.

CSP không phải là 1 trong tính năng plug-and-play nên tất cả chúng ta cần có một chiến lược để mang nó vào sử dụng thực tế.

  1. Chuyển các đoạn code inline script vào các file riêng biệt.
  2. Chuyển các tài nguyên lên CDN hoặc vào trong các subdomain. Nếu vậy tất cả chúng ta có thể chỉ việc dùng script-src cdn.example.com mà ko cần phải được chấp nhận các tài nguyên cùng nguồn self. Tuỳ thuộc vào ứng dụng của tất cả chúng ta mà những nội dung của người dùng (trong cùng nguồn) có thể chứa script, ví dụ các file được upload, các string được nhập để tìm kiếm, … Nếu có thể loại bỏ self khỏi script-src thì tất cả chúng ta sẽ có một sự bảo vệ tại mức mạnh hơn. Tuy vậy, cũng không phải là quá tệ nếu được chấp nhận thực thi script cùng nguồn khi tất cả chúng ta mới khai mạc vận dụng CSP.
  3. Khai mạc với style-src ‘unsafe-inline’ (có thể kèm theo CDN, subdomain, các file style cùng nguồn) để được chấp nhận inline style. Nhiều khả năng là sẽ sở hữu vấn đề với việc ẩn/hiện các thành phần HTML khi sử dụng 1 số thư viện JavaScript phổ quát.
  4. Sử dụng header Content-Security-Policy-Report-Only để chỉ nhận văn bản báo cáo về các trường hợp vi phạm policy của tất cả chúng ta mà chưa cần chặn cái gì hết. Khi tất cả chúng ta đã có đầy đủ các thông tin để xây dựng 1 policy hoàn chỉnh, hãy chuyển sang dùng header Content-Security-Policy.
  5. Directive default-src có thể được dùng để làm khái niệm nguồn mặc định cho hồ hết các directive *-src khác. Tất cả chúng ta có thể khai mạc với blacklist hoặc whitelist (xem phần sau để rõ hơn).

Tất cả chúng ta có thể khai mạc với default-src *, nó cũng giống với việc không có CSP. Một khi tất cả chúng ta rõ nguồn của đa số loại tài nguyên cấp thiết thì có thể chuyển qua sử dụng whitelist: chặn tất cả những nguồn với default-src ‘none’ và chỉ được chấp nhận những nguồn tất cả chúng ta muốn so với các directive khác. GitHub cũng sử dụng cách này vì họ có rất nhiều nội dung của người dùng và sẽ phải mất một thời gian để xác định nguồn nào có thể được được chấp nhận. Nếu website của tất cả chúng ta khá rõ ràng thì có thể khai mạc với default-src ‘none’ luôn.

Vì không thể có một cấu hình CSP nào phù phù hợp với mọi website nên mặc định thì Rails không gửi CSP header. Nếu tất cả chúng ta sử dụng gem SecureHeaders thì nó sẽ tự động hóa gửi những CSP directive được hỗ trở bởi trình duyệt của người dùng. Gem này cũng được chấp nhận thay đổi các directive theo từng controller và action một cách dễ dàng.

Sau này là ví dụ của một cách cấu hình CSP

# Gemfile gem ‘secure_headers’ # config/initializers/csp.rb: SecureHeaders::Configuration.default do |config| config.csp = { report_only: Rails.env.production?, preserve_schemes: true, default_src: %w(*), script_src: %w(‘self’ https://ajax.googleapis.com https://www.google-analytics.com), connect_src: %w(‘self’), style_src: %w(‘self’ ‘unsafe-inline’), report_uri: [“/csp_report?report_only=#{Rails.env.production?}”] } end

Với cấu hình này thì

  • Header Content-Security-Policy-Report-Only được gửi trên môi trường xung quanh production còn header Content-Security-Policy được gửi trên các môi trường xung quanh khác
  • Được cho phép load tài nguyên từ tất cả những nguồn một cách mặc định (default-src: *)
  • Chỉ được chấp nhận script và style từ là 1 số CDN và từ cùng nguồn (‘self’). Ngoài ra có thể sử dụng unsafe-inline trong các tính chất HTML.
  • Chỉ được chấp nhận gửi AJAX request đến cùng nguồn
  • Giải trình các trường hợp vi phạm sẽ tiến hành gửi đến CspReportsController#create với phương thức POST.

Directive style-src ở trên chứa ‘unsafe-inline’ vì những thư viện JavaScript phổ quát như jQuery hay Bootstrap thường thêm trực tiếp style cho những thành phần để ẩn/hiện chúng. Tất cả chúng ta có thể bỏ ‘unsafe-inline’ nếu không dùng các thư viện này mà dùng các class để ẩn/hiện nội dung.

Nói chung thì style cũng tồn tại thể là không an toàn vì có thể chứa JavaScript (URI có thể vẫn hoạt động trên 1 số trình duyệt). Tuy nhiên thì những trình duyệt văn minh có thể tự chặn các request đó. Vì thế unsafe-inline vẫn có thể được chấp thuận đồng ý sử dụng.

Nếu tất cả chúng ta sử dụng jQuery, Bootstrap hoặc các thư viện tương tự thì vô kể khả năng là chúng được load từ các CDN. Google CDN giành cho jQuery được đặt tại https://ajax.googleapis.com nên tất cả chúng ta sẽ thêm domain đó vào directive script-src.

Tất cả chúng ta có thể tiết kiệm chi phí được vài byte trong header bằng việc bỏ đi https:// nhưng như vậy sẽ được chấp nhận load từ phiên bản HTTP.

Tất cả chúng ta cũng tồn tại thể thêm đoạn code như sau để load jQuery từ server của tất cả chúng ta trong trường hợp không load được từ CDN.

<script src=”https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js”></scriptvàgt; <scriptvàgt; (function() { if (typeof jQuery === “undefined” || jQuery === null) { document.write(‘<%= javascript_include_tag ‘jquery’%>’); } }).call(this); </scriptvàgt;

Tuy nhiên như này thì lại vi phạm CSP vì inline script không được phép tự thi. Cách xử lý là chuyển đoạn code ở trên ra 1 file riêng biệt

app/assets/javascripts/jquery_loader.js.erb

(function() { if (typeof jQuery === “undefined” || jQuery === null) { document.write(‘<%= javascript_include_tag ‘jquery’%>’); } }).call(this);

app/assets/javascripts/application.js

Xem Thêm : Sony Z3 D6603 Quốc Tế.

//= require jquery_loader

Đến thời điểm này tất cả chúng ta đã biết phương pháp chuyển các đoạn code inline thành các file riêng biệt. Tuy nhiên tất cả chúng ta cũng không muốn có quá nhiều hoặc quá ít file như vậy, 1 file cho một controller có vẻ là thích hợp. Nếu vậy thì hãy thêm javascript_include_tag controller_name vào trong application layout và tạo 1 file JS trong app/assets cho từng controller.

Cách này hẳn sẽ hiệu quả so với những application không thực sự phức tạp. Những application to nhiều hơn sẽ sở hữu những cách khác để quản lý, sắp xếp các script. Sau này là 1 trong gợi ý mà cũng tồn tại thể hoạt động với Turbolinks:

  • Thêm scope cho từng page với <toàn thân class=”<%= controller_name %> <%= action_name %>”>
  • Được cho phép 1 application script luôn luôn được load, ví dụ như Bootstrap tooltips. Thêm các script cho từng controller hoặc từng chức năng (chart, cart, modal, …).
  • Ở đầu các đoạn code dành cho những page riêng biệt, thêm đoạn code như return unless $(“.posts.index”).length > 0 để nó chỉ được load trong trang PostsController#index.

Lưu ý là cách này sẽ load tất cả những script cùng 1 lúc và sẽ tăng thời kì load lúc đầu. Tuy nhiên thì sau đó tải trang sẽ trở lên nhanh hơn. Các bạn cũng có thể sử dụng cách khác nếu không thích có những file script quá to.

Nếu tất cả chúng ta có đoạn code như vậy này

<button class=’my-javascript-button’ onclick=”alert(‘hello’);”>

thì có thể chuyển nó ra 1 file riêng biệt như sau

$(document).ready(function () { $(‘.my-javascript-button’).on(‘click’, function() { alert(‘hello’); }); });

Ví dụ với đoạn code như dưới

<a href=”#” onclick=”paintIt(this, ‘#990000’)”>Paint it redvàlt;/avàgt;

tất cả chúng ta có thể dùng tính chất data-* và chuyển thành

<a href=”#” data-background-color=”#990000″>Paint it redvàlt;/avàgt;

Unobtrusive CoffeeScript

@paintIt = (element, backgroundColor, textColor) -> element.style.backgroundColor = backgroundColor if textColor? element.style.color = textColor $ -> $(“a[data-background-color]”).click (e) -> e.preventDefault() backgroundColor = $(this).data(“background-color”) textColor = $(this).data(“text-color”) paintIt(this, backgroundColor, textColor)

Policy ở trên có chứa connect-src ‘self’ và được chấp nhận thực hiện AJAX request đến cùng nguồn, điều này thường là sẽ ổn so với hồ hết các ứng dụng.

1 trong những nhiệm vụ khó khăn nhất lúc vận dụng CSP là chuyển các action trả về JavaScript thông qua các file view .js.erb mà thường được sử dụng cho AJAX response. AJAX response mà có chứa script thường được thực thi bằng eval(). Cách này sẽ không còn được chấp thuận đồng ý bởi CSP trừ khi tất cả chúng ta thêm script-src ‘unsafe-eval’. Nếu không muốn dùng tới eval() (mà cũng không nên dùng) tất cả chúng ta có thể làm như sau:

  • Thêm tính chất như data-behavior=”update-credit-card” vào những thành phần HTML mà sẽ trigger AJAX request
  • Xử lí việc click vào link trong một file JS riêng biệt $(document).on “click”, “[data-behavior~=update-credit-card]”
  • Thực hiện AJAX request nếu tất cả chúng ta cần tài liệu gì đó từ server
  • Thay đổi AJAX action để không trả về script mà trả về JSON, markup, …

Sẽ sở hữu nhiều việc phải làm hơn so với khi sử dụng link_to …, remote: true và 1 file *.js.erb nhưng làm vậy tất cả chúng ta sẽ tiến hành những đoạn code JavaScript được phân tích độc lập.

Kết quả của tất cả những việc này là lúc tất cả chúng ta đã cấu hình CSP mà website có lỗ hổng Cross-Site Scripting, trình duyệt sẽ tự động hóa chặn việc thực thi những đoạn code được inject

  • How to Get Started with a Content Security Policy
  • RUBY ON RAILS CONTENT-SECURITY-POLICY (CSP)

You May Also Like

About the Author: v1000