Skip to content

Commit

Permalink
Lowercase headers (#533)
Browse files Browse the repository at this point in the history
Rack 3 `Response header keys can no longer include uppercase
characters.`

https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md#rack-3-upgrade-guide

Rack Lint error out when using `secure_headers` because its adding
headers with uppercase
```
Rack::Lint::LintError: uppercase character in header name: X-Frame-Options (Rack::Lint::LintError)
```

---------

Co-authored-by: Rahul Zhade <[email protected]>
  • Loading branch information
arashnd and rzhade3 authored Dec 16, 2024
1 parent b91d1d9 commit 5ff1d1a
Show file tree
Hide file tree
Showing 19 changed files with 94 additions and 78 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ The gem will automatically apply several headers that are related to security.
- X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. [X-Frame-Options Specification](https://tools.ietf.org/html/rfc7034)
- X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](https://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
- X-Content-Type-Options - [Prevent content type sniffing](https://msdn.microsoft.com/library/gg622941\(v=vs.85\).aspx)
- X-Download-Options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
- X-Permitted-Cross-Domain-Policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html)
- Referrer-Policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
- Expect-CT - Only use certificates that are present in the certificate transparency logs. [Expect-CT draft specification](https://datatracker.ietf.org/doc/draft-stark-expect-ct/).
- Clear-Site-Data - Clearing browser data for origin. [Clear-Site-Data specification](https://w3c.github.io/webappsec-clear-site-data/).
- x-download-options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
- x-permitted-cross-domain-policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html)
- referrer-policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
- expect-ct - Only use certificates that are present in the certificate transparency logs. [expect-ct draft specification](https://datatracker.ietf.org/doc/draft-stark-expect-ct/).
- clear-site-data - Clearing browser data for origin. [clear-site-data specification](https://w3c.github.io/webappsec-clear-site-data/).

It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes. This is on default but can be turned off by using `config.cookies = SecureHeaders::OPT_OUT`.

Expand Down Expand Up @@ -99,13 +99,13 @@ end
All headers except for PublicKeyPins and ClearSiteData have a default value. The default set of headers is:

```
Content-Security-Policy: default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
Strict-Transport-Security: max-age=631138519
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: sameorigin
X-Permitted-Cross-Domain-Policies: none
X-Xss-Protection: 0
content-security-policy: default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
strict-transport-security: max-age=631138519
x-content-type-options: nosniff
x-download-options: noopen
x-frame-options: sameorigin
x-permitted-cross-domain-policies: none
x-xss-protection: 0
```

## API configurations
Expand Down
2 changes: 1 addition & 1 deletion docs/hashes.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ console.log(1)
```

```
Content-Security-Policy: ...
content-security-policy: ...
script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ;
style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...;
```
6 changes: 3 additions & 3 deletions docs/per_action_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ body {

```
Content-Security-Policy: ...
content-security-policy: ...
script-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
style-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
```
Expand All @@ -118,13 +118,13 @@ You can clear the browser cache after the logout request by using the following.

``` ruby
class ApplicationController < ActionController::Base
# Configuration override to send the Clear-Site-Data header.
# Configuration override to send the clear-site-data header.
SecureHeaders::Configuration.override(:clear_browser_cache) do |config|
config.clear_site_data = SecureHeaders::ClearSiteData::ALL_TYPES
end


# Clears the browser's cache for browsers supporting the Clear-Site-Data
# Clears the browser's cache for browsers supporting the clear-site-data
# header.
#
# Returns nothing.
Expand Down
2 changes: 1 addition & 1 deletion docs/upgrading-to-4-0.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The default CSP has changed to be more universal without sacrificing too much se

Previously, the default CSP was:

`Content-Security-Policy: default-src 'self'`
`content-security-policy: default-src 'self'`

The new default policy is:

Expand Down
2 changes: 1 addition & 1 deletion lib/secure_headers/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def csp=(new_csp)
end
end

# Configures the Content-Security-Policy-Report-Only header. `new_csp` cannot
# Configures the content-security-policy-report-only header. `new_csp` cannot
# contain `report_only: false` or an error will be raised.
#
# NOTE: if csp has not been configured/has the default value when
Expand Down
8 changes: 4 additions & 4 deletions lib/secure_headers/headers/clear_site_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
module SecureHeaders
class ClearSiteDataConfigError < StandardError; end
class ClearSiteData
HEADER_NAME = "Clear-Site-Data".freeze
HEADER_NAME = "clear-site-data".freeze

# Valid `types`
CACHE = "cache".freeze
Expand All @@ -12,7 +12,7 @@ class ClearSiteData
ALL_TYPES = [CACHE, COOKIES, STORAGE, EXECUTION_CONTEXTS]

class << self
# Public: make an Clear-Site-Data header name, value pair
# Public: make an clear-site-data header name, value pair
#
# Returns nil if not configured, returns header name and value if configured.
def make_header(config = nil, user_agent = nil)
Expand All @@ -39,8 +39,8 @@ def validate_config!(config)
end
end

# Public: Transform a Clear-Site-Data config (an Array of Strings) into a
# String that can be used as the value for the Clear-Site-Data header.
# Public: Transform a clear-site-data config (an Array of Strings) into a
# String that can be used as the value for the clear-site-data header.
#
# types - An Array of String of types of data to clear.
#
Expand Down
4 changes: 2 additions & 2 deletions lib/secure_headers/headers/content_security_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def initialize(config = nil)
end

##
# Returns the name to use for the header. Either "Content-Security-Policy" or
# "Content-Security-Policy-Report-Only"
# Returns the name to use for the header. Either "content-security-policy" or
# "content-security-policy-report-only"
def name
@config.class.const_get(:HEADER_NAME)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/secure_headers/headers/content_security_policy_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def write_attribute(attr, value)

class ContentSecurityPolicyConfigError < StandardError; end
class ContentSecurityPolicyConfig
HEADER_NAME = "Content-Security-Policy".freeze
HEADER_NAME = "content-security-policy".freeze

ATTRS = Set.new(PolicyManagement::ALL_DIRECTIVES + PolicyManagement::META_CONFIGS + PolicyManagement::NONCES)
def self.attrs
Expand Down Expand Up @@ -107,7 +107,7 @@ def make_report_only
end

class ContentSecurityPolicyReportOnlyConfig < ContentSecurityPolicyConfig
HEADER_NAME = "Content-Security-Policy-Report-Only".freeze
HEADER_NAME = "content-security-policy-report-only".freeze

def report_only?
true
Expand Down
4 changes: 2 additions & 2 deletions lib/secure_headers/headers/expect_certificate_transparency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ module SecureHeaders
class ExpectCertificateTransparencyConfigError < StandardError; end

class ExpectCertificateTransparency
HEADER_NAME = "Expect-CT".freeze
HEADER_NAME = "expect-ct".freeze
INVALID_CONFIGURATION_ERROR = "config must be a hash.".freeze
INVALID_ENFORCE_VALUE_ERROR = "enforce must be a boolean".freeze
REQUIRED_MAX_AGE_ERROR = "max-age is a required directive.".freeze
INVALID_MAX_AGE_ERROR = "max-age must be a number.".freeze

class << self
# Public: Generate a Expect-CT header.
# Public: Generate a expect-ct header.
#
# Returns nil if not configured, returns header name and value if
# configured.
Expand Down
4 changes: 2 additions & 2 deletions lib/secure_headers/headers/policy_management.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ def self.included(base)

# Experimental directives - these vary greatly in support
# See MDN for details.
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/content-security-policy/trusted-types
TRUSTED_TYPES = :trusted_types
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/content-security-policy/require-trusted-types-for
REQUIRE_TRUSTED_TYPES_FOR = :require_trusted_types_for

DIRECTIVES_EXPERIMENTAL = [
Expand Down
2 changes: 1 addition & 1 deletion lib/secure_headers/headers/referrer_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
module SecureHeaders
class ReferrerPolicyConfigError < StandardError; end
class ReferrerPolicy
HEADER_NAME = "Referrer-Policy".freeze
HEADER_NAME = "referrer-policy".freeze
DEFAULT_VALUE = "origin-when-cross-origin"
VALID_POLICIES = %w(
no-referrer
Expand Down
2 changes: 1 addition & 1 deletion lib/secure_headers/headers/strict_transport_security.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module SecureHeaders
class STSConfigError < StandardError; end

class StrictTransportSecurity
HEADER_NAME = "Strict-Transport-Security".freeze
HEADER_NAME = "strict-transport-security".freeze
HSTS_MAX_AGE = "631138519"
DEFAULT_VALUE = "max-age=" + HSTS_MAX_AGE
VALID_STS_HEADER = /\Amax-age=\d+(; includeSubdomains)?(; preload)?\z/i
Expand Down
2 changes: 1 addition & 1 deletion lib/secure_headers/headers/x_content_type_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module SecureHeaders
class XContentTypeOptionsConfigError < StandardError; end

class XContentTypeOptions
HEADER_NAME = "X-Content-Type-Options".freeze
HEADER_NAME = "x-content-type-options".freeze
DEFAULT_VALUE = "nosniff"

class << self
Expand Down
4 changes: 2 additions & 2 deletions lib/secure_headers/headers/x_download_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
module SecureHeaders
class XDOConfigError < StandardError; end
class XDownloadOptions
HEADER_NAME = "X-Download-Options".freeze
HEADER_NAME = "x-download-options".freeze
DEFAULT_VALUE = "noopen"

class << self
# Public: generate an X-Download-Options header.
# Public: generate an x-download-options header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
Expand Down
2 changes: 1 addition & 1 deletion lib/secure_headers/headers/x_frame_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
module SecureHeaders
class XFOConfigError < StandardError; end
class XFrameOptions
HEADER_NAME = "X-Frame-Options".freeze
HEADER_NAME = "x-frame-options".freeze
SAMEORIGIN = "sameorigin"
DENY = "deny"
ALLOW_FROM = "allow-from"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
module SecureHeaders
class XPCDPConfigError < StandardError; end
class XPermittedCrossDomainPolicies
HEADER_NAME = "X-Permitted-Cross-Domain-Policies".freeze
HEADER_NAME = "x-permitted-cross-domain-policies".freeze
DEFAULT_VALUE = "none"
VALID_POLICIES = %w(all none master-only by-content-type by-ftp-filename)

class << self
# Public: generate an X-Permitted-Cross-Domain-Policies header.
# Public: generate an x-permitted-cross-domain-policies header.
#
# Returns a default header if no configuration is provided, or a
# header name and value based on the config.
Expand Down
2 changes: 1 addition & 1 deletion lib/secure_headers/headers/x_xss_protection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
module SecureHeaders
class XXssProtectionConfigError < StandardError; end
class XXssProtection
HEADER_NAME = "X-XSS-Protection".freeze
HEADER_NAME = "x-xss-protection".freeze
DEFAULT_VALUE = "0".freeze
VALID_X_XSS_HEADER = /\A[01](; mode=block)?(; report=.*)?\z/

Expand Down
10 changes: 5 additions & 5 deletions lib/secure_headers/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
module SecureHeaders
class Railtie < Rails::Railtie
isolate_namespace SecureHeaders if defined? isolate_namespace # rails 3.0
conflicting_headers = ["X-Frame-Options", "X-XSS-Protection",
"X-Permitted-Cross-Domain-Policies", "X-Download-Options",
"X-Content-Type-Options", "Strict-Transport-Security",
"Content-Security-Policy", "Content-Security-Policy-Report-Only",
"Public-Key-Pins", "Public-Key-Pins-Report-Only", "Referrer-Policy"]
conflicting_headers = ["x-frame-options", "x-xss-protection",
"x-permitted-cross-domain-policies", "x-download-options",
"x-content-type-options", "strict-transport-security",
"content-security-policy", "content-security-policy-report-only",
"public-key-pins", "public-key-pins-report-only", "referrer-policy"]

initializer "secure_headers.middleware" do
Rails.application.config.middleware.insert_before 0, SecureHeaders::Middleware
Expand Down
Loading

0 comments on commit 5ff1d1a

Please sign in to comment.