Skip to content

Commit

Permalink
Instead of a long string, webserver.headers should be a list of indiv…
Browse files Browse the repository at this point in the history
…idual headers. FTL will then take care of inserting the correct line endings itself

Signed-off-by: DL6ER <[email protected]>
  • Loading branch information
DL6ER committed Feb 22, 2025
1 parent a53a357 commit 7ce1ef5
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 13 deletions.
11 changes: 9 additions & 2 deletions src/api/docs/content/specs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,9 @@ components:
threads:
type: integer
headers:
type: string
type: array
items:
type: string
session:
type: object
properties:
Expand Down Expand Up @@ -746,7 +748,12 @@ components:
acl: "+0.0.0.0/0,::/0"
port: 80,[::]:80
threads: 0
headers: "Content-Security-Policy: default-src 'self' 'unsafe-inline';\r\nX-Frame-Options: DENY\r\nX-XSS-Protection: 0\r\nX-Content-Type-Options: nosniff\r\nReferrer-Policy: strict-origin-when-cross-origin"
headers:
- "Content-Security-Policy: default-src 'self' 'unsafe-inline';"
- "X-Frame-Options: DENY"
- "X-XSS-Protection: 0"
- "X-Content-Type-Options: nosniff"
- "Referrer-Policy: strict-origin-when-cross-origin"
session:
timeout: 300
restore: true
Expand Down
13 changes: 9 additions & 4 deletions src/config/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1014,11 +1014,16 @@ static void initConfig(struct config *conf)
conf->webserver.threads.c = validate_stub; // Only type-based checking

conf->webserver.headers.k = "webserver.headers";
conf->webserver.headers.h = "Additional HTTP headers added to the web server responses.\n The headers are separated by a carriage return and a line feed (\\r\\n). The headers are added to all responses, including those for the API.\n Note about the default additional headers:\n - Content-Security-Policy: [...] 'unsafe-inline' is both required by Chart.js styling some elements directly, and index.html containing some inlined Javascript code.\n - X-Frame-Options: DENY: The page can not be displayed in a frame, regardless of the site attempting to do so.\n - X-Xss-Protection: 0: Disables XSS filtering in browsers that support it. This header is usually enabled by default in browsers, and is not recommended as it can hurt the security of the site. (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection).\n - X-Content-Type-Options: nosniff: Marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed. This allows to opt-out of MIME type sniffing, or, in other words, it is a way to say that the webmasters knew what they were doing. Site security testers usually expect this header to be set.\n - Referrer-Policy: strict-origin-when-cross-origin: A referrer will be sent for same-site origins, but cross-origin requests will send no referrer information.\n The latter four headers are set as expected by https://securityheaders.io";
conf->webserver.headers.a = cJSON_CreateStringReference("<valid HTTP headers>");
conf->webserver.headers.t = CONF_STRING;
conf->webserver.headers.h = "Additional HTTP headers added to the web server responses.\n The headers are added to all responses, including those for the API.\n Note about the default additional headers:\n - Content-Security-Policy: [...] 'unsafe-inline' is both required by Chart.js styling some elements directly, and index.html containing some inlined Javascript code.\n - X-Frame-Options: DENY: The page can not be displayed in a frame, regardless of the site attempting to do so.\n - X-Xss-Protection: 0: Disables XSS filtering in browsers that support it. This header is usually enabled by default in browsers, and is not recommended as it can hurt the security of the site. (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection).\n - X-Content-Type-Options: nosniff: Marker used by the server to indicate that the MIME types advertised in the Content-Type headers should not be changed and be followed. This allows to opt-out of MIME type sniffing, or, in other words, it is a way to say that the webmasters knew what they were doing. Site security testers usually expect this header to be set.\n - Referrer-Policy: strict-origin-when-cross-origin: A referrer will be sent for same-site origins, but cross-origin requests will send no referrer information.\n The latter four headers are set as expected by https://securityheaders.io";
conf->webserver.headers.a = cJSON_CreateStringReference("array of HTTP headers");
conf->webserver.headers.t = CONF_JSON_STRING_ARRAY;
conf->webserver.headers.f = FLAG_RESTART_FTL;
conf->webserver.headers.d.s = (char*)"Content-Security-Policy: default-src 'self' 'unsafe-inline';\r\nX-Frame-Options: DENY\r\nX-XSS-Protection: 0\r\nX-Content-Type-Options: nosniff\r\nReferrer-Policy: strict-origin-when-cross-origin";
conf->webserver.headers.d.json = cJSON_CreateArray();
cJSON_AddItemReferenceToArray(conf->webserver.headers.d.json, cJSON_CreateStringReference("Content-Security-Policy: default-src 'self' 'unsafe-inline';"));
cJSON_AddItemReferenceToArray(conf->webserver.headers.d.json, cJSON_CreateStringReference("X-Frame-Options: DENY"));
cJSON_AddItemReferenceToArray(conf->webserver.headers.d.json, cJSON_CreateStringReference("X-XSS-Protection: 0"));
cJSON_AddItemReferenceToArray(conf->webserver.headers.d.json, cJSON_CreateStringReference("X-Content-Type-Options: nosniff"));
cJSON_AddItemReferenceToArray(conf->webserver.headers.d.json, cJSON_CreateStringReference("Referrer-Policy: strict-origin-when-cross-origin"));
conf->webserver.headers.c = validate_stub; // Only type-based checking

conf->webserver.tls.cert.k = "webserver.tls.cert";
Expand Down
26 changes: 25 additions & 1 deletion src/webserver/webserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,30 @@ void http_init(void)
return;
}

// Construct additional headers
char *webheaders = strdup("");
cJSON *header;
cJSON_ArrayForEach(header, config.webserver.headers.v.json)
{
if(!cJSON_IsString(header))
{
log_err("Invalid header in webserver.headers!");
continue;
}

// Get header value
const char *h = cJSON_GetStringValue(header);

// Allocate memory for the new header
webheaders = realloc(webheaders, strlen(webheaders) + strlen(h) + 3);
if (webheaders == NULL) {
log_err("Failed to allocate memory for webheaders!");
return;
}
strcat(webheaders, h);
strcat(webheaders, "\r\n");
}

// Prepare options for HTTP server (NULL-terminated list)
const char *options[] = {
"document_root", config.webserver.paths.webroot.v.s,
Expand All @@ -432,7 +456,7 @@ void http_init(void)
"enable_directory_listing", "no",
"num_threads", num_threads,
"authentication_domain", config.webserver.domain.v.s,
"additional_header", config.webserver.headers.v.s,
"additional_header", webheaders,
"index_files", "index.html,index.htm,index.lp",
NULL, NULL,
NULL, NULL, // Leave slots for access control list (ACL) and TLS configuration at the end
Expand Down
17 changes: 11 additions & 6 deletions test/pihole.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Pi-hole configuration file (v6.0.1-3-g03cc1d34-dirty)
# Pi-hole configuration file (v6.0.2-18-g392eaa08)
# Encoding: UTF-8
# This file is managed by pihole-FTL
# Last updated on 2025-02-21 10:58:04 UTC
# Last updated on 2025-02-22 18:05:05 UTC

[dns]
# Array of upstream DNS servers used by Pi-hole
Expand Down Expand Up @@ -678,8 +678,7 @@
threads = 0

# Additional HTTP headers added to the web server responses.
# The headers are separated by a carriage return and a line feed (\r\n). The headers
# are added to all responses, including those for the API.
# The headers are added to all responses, including those for the API.
# Note about the default additional headers:
# - Content-Security-Policy: [...] 'unsafe-inline' is both required by Chart.js
# styling some elements directly, and index.html containing some inlined Javascript
Expand All @@ -700,8 +699,14 @@
# The latter four headers are set as expected by https://securityheaders.io
#
# Possible values are:
# <valid HTTP headers>
headers = "Content-Security-Policy: default-src 'self' 'unsafe-inline';\r\nX-Frame-Options: DENY\r\nX-XSS-Protection: 0\r\nX-Content-Type-Options: nosniff\r\nReferrer-Policy: strict-origin-when-cross-origin"
# array of HTTP headers
headers = [
"Content-Security-Policy: default-src 'self' 'unsafe-inline';",
"X-Frame-Options: DENY",
"X-XSS-Protection: 0",
"X-Content-Type-Options: nosniff",
"Referrer-Policy: strict-origin-when-cross-origin"
]

[webserver.session]
# Session timeout in seconds. If a session is inactive for more than this time, it will
Expand Down

0 comments on commit 7ce1ef5

Please sign in to comment.