HTTP header improvements (#294)

- Drop Expect-CT

Expect-CT has been redundant since 2018 when Certificate Transparency became mandated and required for all CAs and browsers. This header is only implemented in Chrome and is now deprecated. HTTP header analysers do not check this anymore as this is enforced by default. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT

- Raise HSTS to 2 years and explicitly preload

The longer age for HSTS, the better. Header analysers prefer 2 years over 1 year now as free TLS is very common using Let's Encrypt.
For HSTS to be fully effective, you need to submit your root domain (domain.tld) to https://hstspreload.org. However, a requirement for this is the "preload" directive in Strict-Transport-Security. If you do not have "preload", it will reject your domain.

- Drop X-Download-Options

This is an IE8-era header when Adobe products used to use the IE engine for making outbound web requests to embed webpages in things like Adobe Acrobat (PDFs). Modern apps are using Microsoft Edge WebView2 or Chromium Embedded Framework. No modern browser checks or header analyser check for this.

- Set base-uri to 'none'

This is to specify the domain for relative links (`<base>` HTML tag). pleroma-fe does not use this and it's an incredibly niche tag.

I use all of these myself on my instance by rewriting the headers with zero problems. No breakage observed.

I have not compiled my Elixr changes, but I don't see why they'd break.

Co-authored-by: r3g_5z <june@terezi.dev>
Reviewed-on: AkkomaGang/akkoma#294
Co-authored-by: @r3g_5z@plem.sapphic.site <june@terezi.dev>
Co-committed-by: @r3g_5z@plem.sapphic.site <june@terezi.dev>
This commit is contained in:
@r3g_5z@plem.sapphic.site 2022-11-20 21:20:06 +00:00 committed by floatingghost
parent 6453297e9c
commit 0e4c201f8d
13 changed files with 7 additions and 79 deletions

View file

@ -487,8 +487,7 @@
config :pleroma, :http_security, config :pleroma, :http_security,
enabled: true, enabled: true,
sts: false, sts: false,
sts_max_age: 31_536_000, sts_max_age: 63_072_000,
ct_max_age: 2_592_000,
referrer_policy: "same-origin" referrer_policy: "same-origin"
config :cors_plug, config :cors_plug,

View file

@ -1750,14 +1750,7 @@
label: "STS max age", label: "STS max age",
type: :integer, type: :integer,
description: "The maximum age for the Strict-Transport-Security header if sent", description: "The maximum age for the Strict-Transport-Security header if sent",
suggestions: [31_536_000] suggestions: [63_072_000]
},
%{
key: :ct_max_age,
label: "CT max age",
type: :integer,
description: "The maximum age for the Expect-CT header if sent",
suggestions: [2_592_000]
}, },
%{ %{
key: :referrer_policy, key: :referrer_policy,

View file

@ -453,7 +453,6 @@ This will make Akkoma listen on `127.0.0.1` port `8080` and generate urls starti
* ``enabled``: Whether the managed content security policy is enabled. * ``enabled``: Whether the managed content security policy is enabled.
* ``sts``: Whether to additionally send a `Strict-Transport-Security` header. * ``sts``: Whether to additionally send a `Strict-Transport-Security` header.
* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent. * ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent.
* ``ct_max_age``: The maximum age for the `Expect-CT` header if sent.
* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`. * ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`.
* ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header. * ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header.

View file

@ -27,14 +27,13 @@ This will send additional HTTP security headers to the clients, including:
* `X-Permitted-Cross-Domain-Policies: "none"` * `X-Permitted-Cross-Domain-Policies: "none"`
* `X-Frame-Options: "DENY"` * `X-Frame-Options: "DENY"`
* `X-Content-Type-Options: "nosniff"` * `X-Content-Type-Options: "nosniff"`
* `X-Download-Options: "noopen"`
A content security policy (CSP) will also be set: A content security policy (CSP) will also be set:
```csp ```csp
content-security-policy: content-security-policy:
default-src 'none'; default-src 'none';
base-uri 'self'; base-uri 'none';
frame-ancestors 'none'; frame-ancestors 'none';
img-src 'self' data: blob: https:; img-src 'self' data: blob: https:;
media-src 'self' https:; media-src 'self' https:;
@ -52,10 +51,6 @@ content-security-policy:
An additional “Strict transport security” header will be sent with the configured `sts_max_age` parameter. This tells the browser, that the domain should only be accessed over a secure HTTPs connection. An additional “Strict transport security” header will be sent with the configured `sts_max_age` parameter. This tells the browser, that the domain should only be accessed over a secure HTTPs connection.
#### `ct_max_age`
An additional “Expect-CT” header will be sent with the configured `ct_max_age` parameter. This enforces the use of TLS certificates that are published in the certificate transparency log. (see [Expect-CT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT))
#### `referrer_policy` #### `referrer_policy`
> Recommended value: `same-origin` > Recommended value: `same-origin`

View file

@ -160,7 +160,6 @@ server {
add_header X-Frame-Options DENY; add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff; add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin; add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;

View file

@ -104,7 +104,6 @@ server {
add_header X-Frame-Options DENY; add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff; add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin; add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;

View file

@ -165,10 +165,9 @@ http protocol plerup { # Protocol for upstream akkoma server
match response header append "X-Frame-Options" value "DENY" match response header append "X-Frame-Options" value "DENY"
match response header append "X-Content-Type-Options" value "nosniff" match response header append "X-Content-Type-Options" value "nosniff"
match response header append "Referrer-Policy" value "same-origin" match response header append "Referrer-Policy" value "same-origin"
match response header append "X-Download-Options" value "noopen" match response header append "Content-Security-Policy" value "default-src 'none'; base-uri 'none'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self' wss://CHANGEME.tld; upgrade-insecure-requests;" # Modify "CHANGEME.tld" and set your instance's domain here
match response header append "Content-Security-Policy" value "default-src 'none'; base-uri 'self'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self' wss://CHANGEME.tld; upgrade-insecure-requests;" # Modify "CHANGEME.tld" and set your instance's domain here
match request header append "Connection" value "upgrade" match request header append "Connection" value "upgrade"
#match response header append "Strict-Transport-Security" value "max-age=31536000; includeSubDomains" # Uncomment this only after you get HTTPS working. #match response header append "Strict-Transport-Security" value "max-age=63072000; includeSubDomains; preload" # Uncomment this only after you get HTTPS working.
# If you do not want remote frontends to be able to access your Akkoma backend server, comment these lines # If you do not want remote frontends to be able to access your Akkoma backend server, comment these lines
match response header append "Access-Control-Allow-Origin" value "*" match response header append "Access-Control-Allow-Origin" value "*"

View file

@ -47,7 +47,6 @@ def headers do
{"x-frame-options", "DENY"}, {"x-frame-options", "DENY"},
{"x-content-type-options", "nosniff"}, {"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy}, {"referrer-policy", referrer_policy},
{"x-download-options", "noopen"},
{"content-security-policy", csp_string()}, {"content-security-policy", csp_string()},
{"permissions-policy", "interest-cohort=()"} {"permissions-policy", "interest-cohort=()"}
] ]
@ -76,7 +75,7 @@ def headers do
static_csp_rules = [ static_csp_rules = [
"default-src 'none'", "default-src 'none'",
"base-uri 'self'", "base-uri 'none'",
"frame-ancestors 'none'", "frame-ancestors 'none'",
"style-src 'self' 'unsafe-inline'", "style-src 'self' 'unsafe-inline'",
"font-src 'self'", "font-src 'self'",
@ -237,11 +236,9 @@ def warn_if_disabled do
defp maybe_send_sts_header(conn, true) do defp maybe_send_sts_header(conn, true) do
max_age_sts = Config.get([:http_security, :sts_max_age]) max_age_sts = Config.get([:http_security, :sts_max_age])
max_age_ct = Config.get([:http_security, :ct_max_age])
merge_resp_headers(conn, [ merge_resp_headers(conn, [
{"strict-transport-security", "max-age=#{max_age_sts}; includeSubDomains"}, {"strict-transport-security", "max-age=#{max_age_sts}; includeSubDomains; preload"}
{"expect-ct", "enforce, max-age=#{max_age_ct}"}
]) ])
end end

View file

@ -1936,12 +1936,6 @@ msgstr ""
"What user agent to use. Must be a string or an atom `:default`. Default " "What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`." "value is `:default`."
#: lib/pleroma/docs/translator.ex:5
#, fuzzy
msgctxt "config description at :pleroma-:http_security > :ct_max_age"
msgid "The maximum age for the Expect-CT header if sent"
msgstr "The maximum age for the Expect-CT header if sent"
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
#, fuzzy #, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled" msgctxt "config description at :pleroma-:http_security > :enabled"
@ -4993,12 +4987,6 @@ msgctxt "config label at :pleroma-:http > :user_agent"
msgid "User agent" msgid "User agent"
msgstr "User agent" msgstr "User agent"
#: lib/pleroma/docs/translator.ex:5
#, fuzzy
msgctxt "config label at :pleroma-:http_security > :ct_max_age"
msgid "CT max age"
msgstr "CT max age"
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
#, fuzzy #, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled" msgctxt "config label at :pleroma-:http_security > :enabled"

View file

@ -1612,12 +1612,6 @@ msgctxt "config description at :pleroma-:http > :user_agent"
msgid "What user agent to use. Must be a string or an atom `:default`. Default value is `:default`." msgid "What user agent to use. Must be a string or an atom `:default`. Default value is `:default`."
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5
msgctxt "config description at :pleroma-:http_security > :ct_max_age"
msgid "The maximum age for the Expect-CT header if sent"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
msgctxt "config description at :pleroma-:http_security > :enabled" msgctxt "config description at :pleroma-:http_security > :enabled"
@ -4048,12 +4042,6 @@ msgctxt "config label at :pleroma-:http > :user_agent"
msgid "User agent" msgid "User agent"
msgstr "" msgstr ""
#, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5
msgctxt "config label at :pleroma-:http_security > :ct_max_age"
msgid "CT max age"
msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
msgctxt "config label at :pleroma-:http_security > :enabled" msgctxt "config label at :pleroma-:http_security > :enabled"

View file

@ -1759,12 +1759,6 @@ msgstr ""
"What user agent to use. Must be a string or an atom `:default`. Default " "What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`." "value is `:default`."
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :ct_max_age"
msgid "The maximum age for the Expect-CT header if sent"
msgstr "The maximum age for the Expect-CT header if sent"
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled" msgctxt "config description at :pleroma-:http_security > :enabled"
@ -4420,12 +4414,6 @@ msgctxt "config label at :pleroma-:http > :user_agent"
msgid "User agent" msgid "User agent"
msgstr "User agent" msgstr "User agent"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :ct_max_age"
msgid "CT max age"
msgstr "CT max age"
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled" msgctxt "config label at :pleroma-:http_security > :enabled"

View file

@ -1759,12 +1759,6 @@ msgstr ""
"What user agent to use. Must be a string or an atom `:default`. Default " "What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`." "value is `:default`."
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :ct_max_age"
msgid "The maximum age for the Expect-CT header if sent"
msgstr "The maximum age for the Expect-CT header if sent"
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled" msgctxt "config description at :pleroma-:http_security > :enabled"
@ -4420,12 +4414,6 @@ msgctxt "config label at :pleroma-:http > :user_agent"
msgid "User agent" msgid "User agent"
msgstr "User agent" msgstr "User agent"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :ct_max_age"
msgid "CT max age"
msgstr "CT max age"
#: lib/pleroma/docs/translator.ex:5 #: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled" msgctxt "config label at :pleroma-:http_security > :enabled"

View file

@ -17,7 +17,6 @@ test "it sends CSP headers when enabled", %{conn: conn} do
refute Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] refute Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == []
refute Conn.get_resp_header(conn, "x-frame-options") == [] refute Conn.get_resp_header(conn, "x-frame-options") == []
refute Conn.get_resp_header(conn, "x-content-type-options") == [] refute Conn.get_resp_header(conn, "x-content-type-options") == []
refute Conn.get_resp_header(conn, "x-download-options") == []
refute Conn.get_resp_header(conn, "referrer-policy") == [] refute Conn.get_resp_header(conn, "referrer-policy") == []
refute Conn.get_resp_header(conn, "content-security-policy") == [] refute Conn.get_resp_header(conn, "content-security-policy") == []
end end
@ -28,7 +27,6 @@ test "it sends STS headers when enabled", %{conn: conn} do
conn = get(conn, "/api/v1/instance") conn = get(conn, "/api/v1/instance")
refute Conn.get_resp_header(conn, "strict-transport-security") == [] refute Conn.get_resp_header(conn, "strict-transport-security") == []
refute Conn.get_resp_header(conn, "expect-ct") == []
end end
test "it does not send STS headers when disabled", %{conn: conn} do test "it does not send STS headers when disabled", %{conn: conn} do
@ -37,7 +35,6 @@ test "it does not send STS headers when disabled", %{conn: conn} do
conn = get(conn, "/api/v1/instance") conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "strict-transport-security") == [] assert Conn.get_resp_header(conn, "strict-transport-security") == []
assert Conn.get_resp_header(conn, "expect-ct") == []
end end
test "referrer-policy header reflects configured value", %{conn: conn} do test "referrer-policy header reflects configured value", %{conn: conn} do
@ -155,7 +152,6 @@ test "it does not send CSP headers when disabled", %{conn: conn} do
assert Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] assert Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == []
assert Conn.get_resp_header(conn, "x-frame-options") == [] assert Conn.get_resp_header(conn, "x-frame-options") == []
assert Conn.get_resp_header(conn, "x-content-type-options") == [] assert Conn.get_resp_header(conn, "x-content-type-options") == []
assert Conn.get_resp_header(conn, "x-download-options") == []
assert Conn.get_resp_header(conn, "referrer-policy") == [] assert Conn.get_resp_header(conn, "referrer-policy") == []
assert Conn.get_resp_header(conn, "content-security-policy") == [] assert Conn.get_resp_header(conn, "content-security-policy") == []
end end