Class: Tina4::SecurityHeadersMiddleware
- Inherits:
-
Object
- Object
- Tina4::SecurityHeadersMiddleware
- Defined in:
- lib/tina4/middleware.rb
Overview
SecurityHeadersMiddleware -- injects security headers on every response. Config via env:
TINA4_FRAME_OPTIONS — X-Frame-Options (default: SAMEORIGIN)
TINA4_HSTS — Strict-Transport-Security max-age (default: "" = off)
TINA4_CSP — Content-Security-Policy (default: "default-src 'self'")
TINA4_REFERRER_POLICY — Referrer-Policy (default: strict-origin-when-cross-origin)
TINA4_PERMISSIONS_POLICY — Permissions-Policy (default: camera=(), microphone=(), geolocation=())
Class Method Summary collapse
-
.attach ⇒ Object
Register this middleware in the default chain (secure-by-default).
- .before_security(request, response) ⇒ Object
-
.warn_csp_default_once ⇒ Object
Warn once per process that the default CSP is in force (TINA4_CSP unset).
Class Method Details
.attach ⇒ Object
Register this middleware in the default chain (secure-by-default).
Unlike CSRF (opt-in via TINA4_CSRF) this is UNCONDITIONAL: a default app ships the security headers with no opt-in -- the SECHDR-DEC-01 posture that closes the SECHDR-OFF-BY-DEFAULT gap (the middleware existed with good defaults but was never registered). Idempotent (Middleware.use de-dupes). The framework calls it once at boot (Tina4.initialize!). Returns true.
787 788 789 790 |
# File 'lib/tina4/middleware.rb', line 787 def attach Tina4::Middleware.use(self) true end |
.before_security(request, response) ⇒ Object
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 |
# File 'lib/tina4/middleware.rb', line 792 def before_security(request, response) response.headers["X-Frame-Options"] = ENV["TINA4_FRAME_OPTIONS"] || "SAMEORIGIN" response.headers["X-Content-Type-Options"] = "nosniff" # HSTS is HTTPS-only (SECHDR-DEC-02): a downgrade-protection header on a # plain-HTTP response is inert at best and ships a bad max-age on an # unencrypted scheme at worst. Emit it ONLY when TINA4_HSTS is set AND the # request is HTTPS -- Request.secure_scheme? honours x-forwarded-proto # (first hop) then rack.url_scheme, the same source of truth the session # cookie's Secure flag uses. Defensive env lookup keeps a non-Request from # turning every response into a 500 now that this runs on every request. hsts = ENV["TINA4_HSTS"] || "" env = request.respond_to?(:env) ? request.env : {} if !hsts.empty? && Tina4::Request.secure_scheme?(env) response.headers["Strict-Transport-Security"] = "max-age=#{hsts}; includeSubDomains" end warn_csp_default_once if ENV["TINA4_CSP"].nil? response.headers["Content-Security-Policy"] = ENV["TINA4_CSP"] || "default-src 'self'" response.headers["Referrer-Policy"] = ENV["TINA4_REFERRER_POLICY"] || "strict-origin-when-cross-origin" response.headers["X-XSS-Protection"] = "0" response.headers["Permissions-Policy"] = ENV["TINA4_PERMISSIONS_POLICY"] || "camera=(), microphone=(), geolocation=()" [request, response] end |
.warn_csp_default_once ⇒ Object
Warn once per process that the default CSP is in force (TINA4_CSP unset).
Secure-by-default keeps default-src 'self' (SECHDR-DEC-01), but that
default is invisible: it blocks runtime-injected inline styles, cross-origin
fonts/scripts/CDNs, data: URIs, and cross-origin WebSocket/XHR (a separate
API or LiveKit host) -- and the failure surfaces only in the browser at
runtime, long after a deploy has gone green. So the framework says so once,
naming the escape hatch. It NEVER fails the boot or a request -- logging a
heads-up must not be the reason the server or a request dies. Fires only when
TINA4_CSP is ABSENT; setting it (even to empty) is an explicit opt-in.
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 |
# File 'lib/tina4/middleware.rb', line 828 def warn_csp_default_once return if @csp_default_warned @csp_default_warned = true = "TINA4_CSP is not set, so Tina4 is serving the default Content-Security-Policy " \ "\"default-src 'self'\" on every response. That default blocks runtime-injected " \ "inline styles, cross-origin fonts/scripts/CDNs, data: URIs, and cross-origin " \ "WebSocket/XHR (e.g. a separate API or LiveKit host). If your app uses any of " \ "these, set TINA4_CSP to a policy that allows them (see https://tina4.com); to " \ "silence this notice without changing behaviour, set TINA4_CSP=\"default-src 'self'\"." begin Tina4::Log.warning() rescue StandardError # Logging must never break a request. warn() end end |