1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
From: Tara Mallesh <taramallesh@microsoft.com>
Date: Mon, 14 Mar 2022 05:25:11 +0000
Subject: Allow HTTP headers to be configurable
---
doc/sample.config | 15 +++++++++++++++
src/config.c | 6 ++++++
src/vpn.h | 4 ++++
src/worker-http.c | 22 ++++++++--------------
tests/test-owasp-headers | 20 ++++++++++++++++++--
5 files changed, 51 insertions(+), 16 deletions(-)
diff --git a/doc/sample.config b/doc/sample.config
index 0e33484..2fb1985 100644
--- a/doc/sample.config
+++ b/doc/sample.config
@@ -734,3 +734,18 @@ ipv4-network = 192.168.2.0
ipv4-netmask = 255.255.255.0
cert-user-oid = 0.9.2342.19200300.100.1.1
+
+# HTTP headers
+included-http-headers = Strict-Transport-Security: max-age=31536000 ; includeSubDomains
+included-http-headers = X-Frame-Options: deny
+included-http-headers = X-Content-Type-Options: nosniff
+included-http-headers = Content-Security-Policy: default-src 'none'
+included-http-headers = X-Permitted-Cross-Domain-Policies: none
+included-http-headers = Referrer-Policy: no-referrer
+included-http-headers = Clear-Site-Data: "cache","cookies","storage"
+included-http-headers = Cross-Origin-Embedder-Policy: require-corp
+included-http-headers = Cross-Origin-Opener-Policy: same-origin
+included-http-headers = Cross-Origin-Resource-Policy: same-origin
+included-http-headers = X-XSS-Protection: 0
+included-http-headers = Pragma: no-cache
+included-http-headers = Cache-control: no-store, no-cache
diff --git a/src/config.c b/src/config.c
index d8e1a01..7195d07 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1078,6 +1078,12 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
READ_MULTI_LINE(config->custom_header, config->custom_header_size);
} else if (strcmp(name, "split-dns") == 0) {
READ_MULTI_LINE(config->split_dns, config->split_dns_size);
+ } else if (strcmp(name, "included-http-headers") == 0) {
+ // Don't use sanitized input since http header values can contain optional trailing blanks and double quotes
+ if (_add_multi_line_val(pool, &(config->included_http_headers), &(config->included_http_headers_size), _value) < 0) {
+ fprintf(stderr, ERRSTR"memory\n");
+ exit(1);
+ }
} else if (strcmp(name, "route") == 0) {
READ_MULTI_LINE(config->network.routes, config->network.routes_size);
} else if (strcmp(name, "no-route") == 0) {
diff --git a/src/vpn.h b/src/vpn.h
index 3b737ab..3e09987 100644
--- a/src/vpn.h
+++ b/src/vpn.h
@@ -259,6 +259,10 @@ struct cfg_st {
char **split_dns;
size_t split_dns_size;;
+ /* http headers to include */
+ char **included_http_headers;
+ size_t included_http_headers_size;
+
unsigned int append_routes; /* whether to append global routes to per-user config */
unsigned restrict_user_to_routes; /* whether the firewall script will be run for the user */
unsigned deny_roaming; /* whether a cookie is restricted to a single IP */
diff --git a/src/worker-http.c b/src/worker-http.c
index 4294119..7049106 100644
--- a/src/worker-http.c
+++ b/src/worker-http.c
@@ -873,21 +873,15 @@ void http_req_deinit(worker_st * ws)
*/
int add_owasp_headers(worker_st * ws)
{
- if (cstp_puts(ws, "Strict-Transport-Security: max-age=31536000 ; includeSubDomains\r\n") < 0 ||
- cstp_puts(ws, "X-Frame-Options: deny\r\n") < 0 ||
- cstp_puts(ws, "X-Content-Type-Options: nosniff\r\n") < 0 ||
- cstp_puts(ws, "Content-Security-Policy: default-src \'none\'\r\n") < 0 ||
- cstp_puts(ws, "X-Permitted-Cross-Domain-Policies: none\r\n") < 0 ||
- cstp_puts(ws, "Referrer-Policy: no-referrer\r\n") < 0 ||
- cstp_puts(ws, "Clear-Site-Data: \"cache\",\"cookies\",\"storage\"\r\n") < 0 ||
- cstp_puts(ws, "Cross-Origin-Embedder-Policy: require-corp\r\n") < 0 ||
- cstp_puts(ws, "Cross-Origin-Opener-Policy: same-origin\r\n") < 0 ||
- cstp_puts(ws, "Cross-Origin-Resource-Policy: same-origin\r\n") < 0 ||
- cstp_puts(ws, "X-XSS-Protection: 0\r\n") < 0 ||
- cstp_puts(ws, "Pragma: no-cache\r\n") < 0 ||
- cstp_puts(ws, "Cache-control: no-store\r\n") < 0)
+ unsigned i;
+
+ for(i=0; i < GETCONFIG(ws)->included_http_headers_size; i++)
{
- return -1;
+ if (cstp_printf(ws, "%s", GETCONFIG(ws)->included_http_headers[i]) < 0 ||
+ cstp_puts(ws, "\r\n") < 0)
+ {
+ return -1;
+ }
}
return 0;
}
diff --git a/tests/test-owasp-headers b/tests/test-owasp-headers
index 3c2d958..ed9cedf 100755
--- a/tests/test-owasp-headers
+++ b/tests/test-owasp-headers
@@ -29,6 +29,22 @@ eval "${GETPORT}"
echo "Testing ocserv owasp headers... "
update_config test-user-cert.config
+
+# Add HTTP headers to the config file
+echo "included-http-headers = Strict-Transport-Security: max-age=31536000 ; includeSubDomains" >> ${CONFIG}
+echo "included-http-headers = X-Frame-Options: deny" >> ${CONFIG}
+echo "included-http-headers = X-Content-Type-Options: nosniff" >> ${CONFIG}
+echo "included-http-headers = Content-Security-Policy: default-src \'none\'" >> ${CONFIG}
+echo "included-http-headers = X-Permitted-Cross-Domain-Policies: none" >> ${CONFIG}
+echo "included-http-headers = Referrer-Policy: no-referrer" >> ${CONFIG}
+echo "included-http-headers = Clear-Site-Data: \"cache\",\"cookies\",\"storage\"" >> ${CONFIG}
+echo "included-http-headers = Cross-Origin-Embedder-Policy: require-corp" >> ${CONFIG}
+echo "included-http-headers = Cross-Origin-Opener-Policy: same-origin" >> ${CONFIG}
+echo "included-http-headers = Cross-Origin-Resource-Policy: same-origin" >> ${CONFIG}
+echo "included-http-headers = X-XSS-Protection: 0" >> ${CONFIG}
+echo "included-http-headers = Pragma: no-cache" >> ${CONFIG}
+echo "included-http-headers = Cache-control: no-cache, no-store" >> ${CONFIG}
+
launch_simple_sr_server -d 1 -f -c ${CONFIG}
PID=$!
@@ -49,7 +65,7 @@ function CheckHeaders
[[ "$1" =~ .*"X-XSS-Protection".* ]] || fail $PID "Missing HTTP header (X-XSS-Protection)"
[[ "$1" =~ .*"Pragma".* ]] || fail $PID "Missing HTTP header (Pragma)"
[[ "$1" =~ .*"Cache-control".* ]] || fail $PID "Missing HTTP header (Cache-control)"
-
+
while IFS=':' read name value; do
case "$name" in
Strict-Transport-Security)
@@ -77,7 +93,7 @@ function CheckHeaders
Pragma)
[[ "$value" =~ "no-cache" ]] || fail $PID "Unexpected HTTP header value ($name: $value)";;
Cache-control)
- [[ "$value" =~ "no-store" ]] || fail $PID "Unexpected HTTP header value ($name: $value)";;
+ [[ "$value" =~ "no-cache, no-store" ]] || fail $PID "Unexpected HTTP header value ($name: $value)";;
esac
done < <(echo "$1")
}
|