Content Delivery Network (CDN)

A Hanami application can serve assets from a Content Delivery Network (CDN). This feature is useful in production environment, where we want to speed up static assets serving.

In order to take advantage of this feature, we need to specify CDN settings.

  1. # apps/web/application.rb
  2. module Web
  3. class Application < Hanami::Application
  4. # ...
  5. configure :production do
  6. scheme 'https'
  7. host 'bookshelf.org'
  8. port 443
  9. assets do
  10. # ...
  11. fingerprint true
  12. # CDN settings
  13. scheme 'https'
  14. host '123.cloudfront.net'
  15. port 443
  16. end
  17. end
  18. end
  19. end

Once CDN mode is on, all the asset helpers will return absolute URLs.

  1. <%= stylesheet 'application' %>
  1. <link href="https://123.cloudfront.net/assets/application-9ab4d1f57027f0d40738ab8ab70aba86.css" type="text/css" rel="stylesheet">

Subresource Integrity

A CDN can dramatically improve page speed, but it can potentially open a security breach. If the CDN that we’re using is compromised and serves evil javascript or stylesheet files, we’re exposing our users to security attacks like Cross Site Scripting (XSS).

To solve this problem, browsers vendors introduced a defense called Subresource Integrity.

When enabled, the browser verifies that the checksum of the downloaded file, matches with the declared one.

From A CDN

If we’re using jQuery from their CDN, we should find the checksum of the .js file on their website and write:

  1. <%= javascript 'https://code.jquery.com/jquery-3.1.0.min.js', integrity: 'sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=' %>

The output will be:

  1. <script integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" src="https://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript" crossorigin="anonymous"></script>

Content Security Policy (CSP)

By default, Hanami sets a Content-Security-Policy header which does not allow for the execution of external JavaScript code.

Let’s say we want to use Bootstrap in our web application, we have to explicitly allow for the use of the relevant CDNs in app/web/application.rb by appending them in the script-src field:

  1. security.content_security_policy %{
  2. script-src 'self' \
  3. https://code.jquery.com \
  4. https://cdnjs.cloudflare.com \
  5. https://maxcdn.bootstrapcdn.com;
  6. }

Read more about the CSP header in the security guide.

Local Assets

The security problem described above doesn’t concern only CDNs, but local files too. Imagine we have a compromised file system and someone was able to replace our javascripts with evil files: we would be vulnerable to the same kind of attack.

As a defense against this security problem, Hanami enables Subresource Integrity by default in production. When we precompile assets at deploy time, Hanami calculates the checksum of all our assets and it adds a special HTML attribute integrity to our asset tags like <script>.

  1. <%= javascript 'application' %>
  1. <script src="/assets/application-92cab02f6d2d51253880cd98d91f1d0e.js" type="text/javascript" integrity="sha256-WB2pRuy8LdgAZ0aiFxLN8DdfRjKJTc4P4xuEw31iilM=" crossorigin="anonymous"></script>

Settings

To turn off this feature, or to configure it, please have a look at the production block in apps/web/application.rb

  1. module Web
  2. class Application < Hanami::Application
  3. configure :production do
  4. assets do
  5. # ...
  6. subresource_integrity :sha256
  7. end
  8. end
  9. end
  10. end

By removing or commenting that line, the feature is turned off.

We can choose one or more checksum algorithms:

  1. subresource_integrity :sha256, :sha512

With this setting, Hanami will render integrity HTML attribute with two values: one for SHA256 and one for SHA512.

  1. <script src="/assets/application-92cab02f6d2d51253880cd98d91f1d0e.js" type="text/javascript" integrity="sha256-WB2pRuy8LdgAZ0aiFxLN8DdfRjKJTc4P4xuEw31iilM= sha512-4gegSER1uqxBvmlb/O9CJypUpRWR49SniwUjOcK2jifCRjFptwGKplFWGlGJ1yms+nSlkjpNCS/Lk9GoKI1Kew==" crossorigin="anonymous"></script>

Please note that checksum calculations are CPU intensive, so adding an additional subresource_integrity scheme will extend the time it takes to precompile assests, and therefore deploy. We suggest leaving the default setting (:sha256).