- 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: https://akkoma.dev/AkkomaGang/akkoma/pulls/294
Co-authored-by: @r3g_5z@plem.sapphic.site <june@terezi.dev>
Co-committed-by: @r3g_5z@plem.sapphic.site <june@terezi.dev>
13 files changed:
config :pleroma, :http_security,
enabled: true,
sts: false,
config :pleroma, :http_security,
enabled: true,
sts: false,
- sts_max_age: 31_536_000,
- ct_max_age: 2_592_000,
+ sts_max_age: 63_072_000,
referrer_policy: "same-origin"
config :cors_plug,
referrer_policy: "same-origin"
config :cors_plug,
label: "STS max age",
type: :integer,
description: "The maximum age for the Strict-Transport-Security header if sent",
label: "STS max age",
type: :integer,
description: "The maximum age for the Strict-Transport-Security header if sent",
- suggestions: [31_536_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]
+ suggestions: [63_072_000]
},
%{
key: :referrer_policy,
},
%{
key: :referrer_policy,
* ``enabled``: Whether the managed content security policy is enabled.
* ``sts``: Whether to additionally send a `Strict-Transport-Security` header.
* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent.
* ``enabled``: Whether the managed content security policy is enabled.
* ``sts``: Whether to additionally send a `Strict-Transport-Security` header.
* ``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"`.
* ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header.
* ``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.
* `X-Permitted-Cross-Domain-Policies: "none"`
* `X-Frame-Options: "DENY"`
* `X-Content-Type-Options: "nosniff"`
* `X-Permitted-Cross-Domain-Policies: "none"`
* `X-Frame-Options: "DENY"`
* `X-Content-Type-Options: "nosniff"`
-* `X-Download-Options: "noopen"`
A content security policy (CSP) will also be set:
```csp
content-security-policy:
default-src 'none';
A content security policy (CSP) will also be set:
```csp
content-security-policy:
default-src 'none';
frame-ancestors 'none';
img-src 'self' data: blob: https:;
media-src 'self' https:;
frame-ancestors 'none';
img-src 'self' data: blob: https:;
media-src 'self' https:;
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`
> Recommended value: `same-origin`
#### `referrer_policy`
> Recommended value: `same-origin`
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
- add_header X-Download-Options noopen;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
- add_header X-Download-Options noopen;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
match response header append "X-Frame-Options" value "DENY"
match response header append "X-Content-Type-Options" value "nosniff"
match response header append "Referrer-Policy" value "same-origin"
match response header append "X-Frame-Options" value "DENY"
match response header append "X-Content-Type-Options" value "nosniff"
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 '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 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 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
match response header append "Access-Control-Allow-Origin" value "*"
# 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 "*"
{"x-frame-options", "DENY"},
{"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy},
{"x-frame-options", "DENY"},
{"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy},
- {"x-download-options", "noopen"},
{"content-security-policy", csp_string()},
{"permissions-policy", "interest-cohort=()"}
]
{"content-security-policy", csp_string()},
{"permissions-policy", "interest-cohort=()"}
]
static_csp_rules = [
"default-src 'none'",
static_csp_rules = [
"default-src 'none'",
"frame-ancestors 'none'",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
"frame-ancestors 'none'",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
defp maybe_send_sts_header(conn, true) do
max_age_sts = Config.get([:http_security, :sts_max_age])
defp maybe_send_sts_header(conn, true) do
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"},
- {"expect-ct", "enforce, max-age=#{max_age_ct}"}
+ {"strict-transport-security", "max-age=#{max_age_sts}; includeSubDomains; preload"}
"What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`."
"What user agent to use. Must be a string or an atom `:default`. 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
#, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
#: lib/pleroma/docs/translator.ex:5
#, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr "User agent"
msgid "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
#, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
#: lib/pleroma/docs/translator.ex:5
#, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
msgid "What user agent to use. Must be a string or an atom `:default`. Default value is `:default`."
msgstr ""
msgid "What user agent to use. Must be a string or an atom `:default`. Default value is `:default`."
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
#: lib/pleroma/docs/translator.ex:5
msgctxt "config description at :pleroma-:http_security > :enabled"
#, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr ""
msgid "User agent"
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
#: lib/pleroma/docs/translator.ex:5
msgctxt "config label at :pleroma-:http_security > :enabled"
#, elixir-autogen, elixir-format
#: lib/pleroma/docs/translator.ex:5
msgctxt "config label at :pleroma-:http_security > :enabled"
"What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`."
"What user agent to use. Must be a string or an atom `:default`. 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
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr "User agent"
msgid "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
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
"What user agent to use. Must be a string or an atom `:default`. Default "
"value is `:default`."
"What user agent to use. Must be a string or an atom `:default`. 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
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config description at :pleroma-:http_security > :enabled"
msgid "User agent"
msgstr "User agent"
msgid "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
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
#: lib/pleroma/docs/translator.ex:5
#, elixir-autogen, elixir-format, fuzzy
msgctxt "config label at :pleroma-:http_security > :enabled"
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-content-type-options") == []
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-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, "content-security-policy") == []
end
refute Conn.get_resp_header(conn, "referrer-policy") == []
refute Conn.get_resp_header(conn, "content-security-policy") == []
end
conn = get(conn, "/api/v1/instance")
refute Conn.get_resp_header(conn, "strict-transport-security") == []
conn = get(conn, "/api/v1/instance")
refute Conn.get_resp_header(conn, "strict-transport-security") == []
- refute Conn.get_resp_header(conn, "expect-ct") == []
end
test "it does not send STS headers when disabled", %{conn: conn} do
end
test "it does not send STS headers when disabled", %{conn: conn} do
conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "strict-transport-security") == []
conn = get(conn, "/api/v1/instance")
assert Conn.get_resp_header(conn, "strict-transport-security") == []
- assert Conn.get_resp_header(conn, "expect-ct") == []
end
test "referrer-policy header reflects configured value", %{conn: conn} do
end
test "referrer-policy header reflects configured value", %{conn: conn} do
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-content-type-options") == []
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-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, "content-security-policy") == []
end
assert Conn.get_resp_header(conn, "referrer-policy") == []
assert Conn.get_resp_header(conn, "content-security-policy") == []
end