description: > Basic HTTPS response headers security in Ruby.
Check out secure web development{:rel="noopener"} and secure response body{:rel="noopener"} for more notes.
This document assumes HTTPS has been enabled. Ruby samples focus on Roda. Check its documentation for further details regarding where/how to use the code samples.
Table of Contents
response["Strict-Transport-Security"] = "max-age=31536000; includeSubdomains; preload"
response["X-Frame-Options"] = "deny"
response["Referrer-Policy"] = "no-referrer"
response["X-Permitted-Cross-Domain-Policies"] = none
response["X-XSS-Protection"] = "1; mode=block"
response["X-Content-Type-Options"] = "nosniff"
Restrict where the browser can load resources from to prevent the execution of unknown scripts.
# Web apps customize these according to their needs
cdns = %w[trusted-cdn.com other-cdn.com]
response["Content-Security-Policy"] = "default-src 'none'; script-src 'self' #{cdns}; connect-src 'self'; img-src 'self'; style-src 'self';"
# For pure JSON APIs
response["Content-Security-Policy"] = "default-src 'none'"
To help prevent web cache deception attacks add the require-sri-for
directive.
response["Content-Security-Policy"] = "require-sri-for script style; ..."
The secure response body{:rel="nofollow noreferrer noopener"} cheat sheet has some details on how to add scripts, and styles using the sub-resource integrity (SRI) check.
Secure
flag.HttpOnly
.securerandom
number as a fallback.require "roda"
class Web < Roda
use Rack::Session::Cookie, {
key: "_app_session",
path: "/",
httponly: true,
secure: true,
expire_after: nil, # Session cookie
secret: ENV.fetch("COOKIE_KEY") { SecureRandom.hex 64 }
}
end
CSRF attacks affect end-points which change state on the server. Which has more to do with a verb's safety than its idempotency.
According to OWASP, there are numerous ways to defend against CSRF. Yet, a session cookie by itself is not enough to prevent this type of attack. Although, when combined with token synchronization it makes a sound defence.
To avoid hidden pitfalls rely in a gem, toolkit, or framework that takes
care of CSRF protection. Make sure any non GET
requests always require
a valid token, though.
require "roda"
class WebSiteWithAjax < Roda
# cookie configuration here
plugin :type_routing,
default_type: :html,
exclude: :xml,
types: {
json: "application/json; charset=utf-8",
html: "text/html; charset=utf-8"
}
plugin :csrf,
raise: false
end
CORS enables communication and resource sharing between trusted partners.
These are dangerous since they allow connections from anywhere...
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Origin"] = "null"
and neutralize credentials.
response["Access-Control-Allow-Credentials"] = true # actually behaves like false
Wildcards aren't partials. They aren't safe to use:
# Invalid
response["Access-Control-Allow-Origin"] = "https://*.example.com"
# Valid but UNSAFE. Accepts connections from http://evil.example.com
response["Access-Control-Allow-Origin"] = "*.example.com"
By default CORS only works with a single origin. Sub-domains aren't included.
response["Access-Control-Allow-Origin"] = "https://www.example.com/"
response["Access-Control-Allow-Credentials"] = true
response["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE"
A common way of dealing with the single-origin limitation is to dynamically consume the request header. Since that technique depends on input:
Vary
header with Origin
value.Gems:
Secure headers tools: