Skip to content

Commit

Permalink
Add middleware for Referrer-Policy
Browse files Browse the repository at this point in the history
  • Loading branch information
treagod committed Aug 8, 2024
1 parent c1afb28 commit 9b741d6
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 0 deletions.
19 changes: 19 additions & 0 deletions docs/docs/development/reference/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,25 @@ Default: `true`

A boolean indicating whether multiple processes can bind to the same HTTP server port.

### `referrer_policy`

Default: `"strict-origin-when-cross-origin"`

The value to use for the Referrer-Policy header when the associated middleware is used. This header controls the amount of referrer information sent along with requests from your site to other origins, enhancing user privacy and security.

Possible values for the Referrer-Policy header include:

- `no-referrer`: The Referer header will be omitted entirely. No referrer information is sent with requests.
- `no-referrer-when-downgrade`: The Referer header will not be sent to less secure destinations (e.g., from HTTPS to HTTP), but will be sent to same or more secure destinations.
- `origin`: Only the origin of the document is sent as the referrer.
- `origin-when-cross-origin`: The full URL is sent as the referrer when performing a same-origin request, but only the origin is sent for cross-origin requests.
- `same-origin`: The Referer header is sent with same-origin requests, but not with cross-origin requests.
- `strict-origin`: Only the origin is sent as the referrer, and only for same-origin requests.
- `strict-origin-when-cross-origin`: The full URL is sent as the referrer when performing a same-origin request, but only the origin is sent for cross-origin requests. No referrer information is sent to less secure destinations.
- `unsafe-url`: The full URL is always sent as the referrer, regardless of the request's security.

This setting will be used by the [`Marten::Middleware::ReferrerPolicy`](../../handlers-and-http/reference/middlewares.md#referrer-policy-middleware) middleware when inserting the Referrer-Policy header in HTTP responses. By configuring this setting, you can control how much referrer information is included with requests from your site to other origins.

### `request_max_parameters`

Default: `1000`
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/handlers-and-http/reference/middlewares.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ With the `MethodOverride` middleware, the form submission would effectively be t
The middleware should be placed as far as possible at the beginning of the array of the [`middlewares`](../../development/reference/settings.md#middleware) setting so that other middlewares already recognise the overridden method.
:::

## Referrer-Policy middleware

**Class:** [`Marten::Middleware::ReferrerPolicy`](pathname:///api/dev/Marten/Middleware/ReferrerPolicy.html)

Sets the Referrer-Policy header in the response if it wasn't already set.

When this middleware is used, a Referrer-Policy header will be inserted into the HTTP response. The value for this header is configurable via the [`referrer_policy`](../../development/reference/settings.md#referrer_policy) setting. This header controls the amount of referrer information sent along with requests from your site to other origins, enhancing user privacy and security.

## Session middleware

**Class:** [`Marten::Middleware::Session`](pathname:///api/dev/Marten/Middleware/Session.html)
Expand Down
21 changes: 21 additions & 0 deletions spec/marten/conf/global_settings_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,27 @@ describe Marten::Conf::GlobalSettings do
end
end

describe "#referrer_policy" do
it "returns strict-origin-when-cross-origin by default" do
global_settings = Marten::Conf::GlobalSettings.new
global_settings.referrer_policy.should eq "strict-origin-when-cross-origin"
end

it "returns the specified Referrer-Policy if explicitely set" do
global_settings = Marten::Conf::GlobalSettings.new
global_settings.referrer_policy = "origin"
global_settings.referrer_policy.should eq "origin"
end
end

describe "#referrer_policy=" do
it "allows to configure the Referrer-Policy" do
global_settings = Marten::Conf::GlobalSettings.new
global_settings.referrer_policy = "origin"
global_settings.referrer_policy.should eq "origin"
end
end

describe "#request_max_parameters" do
it "returns 1000 by default" do
global_settings = Marten::Conf::GlobalSettings.new
Expand Down
65 changes: 65 additions & 0 deletions spec/marten/middleware/referrer_policy_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require "./spec_helper"

describe Marten::Middleware::ReferrerPolicy do
describe "#call" do
it "returns the default Referrer-Policy header if not modified early" do
request = Marten::HTTP::Request.new(
::HTTP::Request.new(
method: "GET",
resource: "/test/xyz",
headers: HTTP::Headers{"Host" => "example.com"},
)
)

middleware = Marten::Middleware::ReferrerPolicy.new
response = middleware.call(
request, ->{ Marten::HTTP::Response.new("It works!", content_type: "text/plain", status: 200) }
)

response.headers[:"Referrer-Policy"].should eq "strict-origin-when-cross-origin"
end

it "returns the response early if it already contains the Referrer-Policy header" do
request = Marten::HTTP::Request.new(
::HTTP::Request.new(
method: "GET",
resource: "/test/xyz",
headers: HTTP::Headers{"Host" => "example.com"},
)
)

middleware = Marten::Middleware::ReferrerPolicy.new
response = middleware.call(
request,
->{
r = Marten::HTTP::Response.new("It works!", content_type: "text/plain", status: 200)
r[:"Referrer-Policy"] = "origin"
r
}
)

response.headers[:"Referrer-Policy"].should eq "origin"
end

it "inserts the right Referrer-Policy header value based on the related setting" do
request = Marten::HTTP::Request.new(
::HTTP::Request.new(
method: "GET",
resource: "/test/xyz",
headers: HTTP::Headers{"Host" => "example.com"},
)
)

middleware = Marten::Middleware::ReferrerPolicy.new

with_overridden_setting("referrer_policy", "origin") do
response = middleware.call(
request,
->{ Marten::HTTP::Response.new("It works!", content_type: "text/plain", status: 200) }
)

response.headers[:"Referrer-Policy"].should eq "origin"
end
end
end
end
13 changes: 13 additions & 0 deletions src/marten/conf/global_settings.cr
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ module Marten
# Returns a boolean indicating whether multiple processes can bind to the same HTTP server port.
getter port_reuse

# Returns the value to use for the Referrer-Policy header.
#
# The value of this setting will be used by the `Marten::Middleware::ReferrerPolicy` middleware when inserting the
# Referrer-Policy header in HTTP responses.
getter referrer_policy

# Returns the maximum number of allowed parameters per request (such as GET or POST parameters).
getter request_max_parameters

Expand Down Expand Up @@ -126,6 +132,12 @@ module Marten
# Allows to indicate whether multiple processes can bind to the same HTTP server port.
setter port_reuse

# Allows to set the value to use for the Referrer-Policy header.
#
# This value will be used by the `Marten::Middleware::ReferrerPolicy` middleware when inserting the
# Referrer-Policy header in HTTP responses.
setter referrer_policy

# Allows to set the maximum number of allowed parameters per request (such as GET or POST parameters).
#
# This maximum limit is used to prevent large requests that could be used in the context of DOS attacks. Setting
Expand Down Expand Up @@ -198,6 +210,7 @@ module Marten
@middleware = Array(Marten::Middleware.class).new
@port = 8000
@port_reuse = true
@referrer_policy = "strict-origin-when-cross-origin"
@request_max_parameters = 1000
@secret_key = ""
@time_zone = Time::Location.load("UTC")
Expand Down
39 changes: 39 additions & 0 deletions src/marten/middleware/referrer_policy.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Marten
abstract class Middleware
# Sets the Referrer-Policy header in the response if it wasn't already set.
#
# When this middleware is used, a Referrer-Policy header will be inserted into the HTTP response. The value for this
# header is configurable in the `referrer_policy` setting. This header controls how much referrer information should
# be included with requests made from your website to other origins. By setting this header, you can enhance the
# privacy and security of your users by limiting the amount of information that is sent with outbound requests.
#
# The possible values for the Referrer-Policy header include:
# - no-referrer: The Referer header will be omitted entirely. No referrer information is sent with requests.
# - no-referrer-when-downgrade: The Referer header will not be sent to less secure destinations
# (e.g., from HTTPS to HTTP), but will be sent to same or more secure destinations.
# - origin: Only the origin of the document is sent as the referrer.
# - origin-when-cross-origin: The full URL is sent as the referrer when performing a same-origin request,
# but only the origin is sent for cross-origin requests.
# - same-origin: The Referer header is sent with same-origin requests, but not with cross-origin requests.
# - strict-origin: Only the origin is sent as the referrer, and only for same-origin requests.
# - strict-origin-when-cross-origin: The full URL is sent as the referrer when performing a same-origin request,
# but only the origin is sent for cross-origin requests.
# No referrer information is sent to less secure destinations.
# - unsafe-url: The full URL is always sent as the referrer, regardless of the request's security.
#
# You can configure the desired policy in the `referrer_policy` setting in your application's configuration.
class ReferrerPolicy < Middleware
def call(request : Marten::HTTP::Request, get_response : Proc(Marten::HTTP::Response)) : Marten::HTTP::Response
response = get_response.call

# Don't change the Referrer-Policy if it is already set
return response if response.headers[:"Referrer-Policy"]?

# Set the Referrer-Policy according to settings
response.headers[:"Referrer-Policy"] = Marten.settings.referrer_policy

response
end
end
end
end

0 comments on commit 9b741d6

Please sign in to comment.