V1.3: Content Delivery Network (CDN)
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.
# apps/web/application.rb
module Web
class Application < Hanami::Application
# ...
configure :production do
scheme 'https'
host 'bookshelf.org'
port 443
assets do
# ...
fingerprint true
# CDN settings
scheme 'https'
host '123.cloudfront.net'
port 443
end
end
end
end
Once CDN mode is on, all the asset helpers will return absolute URLs.
<%= stylesheet 'application' %>
<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:
<%= javascript 'https://code.jquery.com/jquery-3.1.0.min.js', integrity: 'sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=' %>
The output will be:
<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:
security.content_security_policy %{
…
script-src 'self' \
https://code.jquery.com \
https://cdnjs.cloudflare.com \
https://maxcdn.bootstrapcdn.com;
…
}
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>
.
<%= javascript 'application' %>
<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
module Web
class Application < Hanami::Application
configure :production do
assets do
# ...
subresource_integrity :sha256
end
end
end
end
By removing or commenting that line, the feature is turned off.
We can choose one or more checksum algorithms:
subresource_integrity :sha256, :sha512
With this setting, Hanami will render integrity
HTML attribute with two values: one for SHA256
and one for SHA512
.
<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
).