[Pound Mailing List] [Patch] HTTP Strict Transport Security option
"Frank Schmirler" <frank.schmirler-PfwzoFMxJyVWk0Htik3J/[email protected]>
| Newsgroups | gmane.comp.web.pound.general |
|---|---|
| Message-ID | <[email protected]> |
Hi there, HTTP Strict Transport Security (HSTS) uses an HTTPS response header, telling the browser to always use an encrypted connection for this hostname and to not let the user override any certificate warnings when it re-visits the site in the future. These restrictions apply for a certain period of time. The goal is to make man-in-the-middle attacks more difficult. Of the major browsers, only IE doesn't support it yet. * http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security * http://tools.ietf.org/html/rfc6797 Even though you could configure HSTS on the backend and have pound simply forward the header, I thought it would be a better idea to configure it in pound. The attached patch is against 2.7c and introduces the new service level instruction "StrictTransportSecurity <seconds>". It is only acceptable inside HTTPSListeners. The parameter tells the browser for how long it should remember HSTS. On production systems you should use something in the range of several months (for ssllabs.com you need to configure at least 180 days to have it improve your rating). The special value 0 instructs the browser to delete the HSTS entry. This of course requires the browser to re-visit your site. When HSTS is configured in a pound service it will override any HSTS headers received from the backend. Otherwise the backend's HSTS header is simply passed through. Before you enable HSTS, make sure you understand its implications: - Even though you can configure HSTS by service, it actually affects the (virtual) hostname, i.e. any service and even HTTPListeners or other web servers which can be addressed with the same hostname - So for clarity I recommend to configure HSTS with the same value in all affected pound services - Do not enable if an unencrypted (HTTP only) site is running on any (virtual) hostname your HTTPS service is listening to - Be aware that you will no longer be able to simply switch back to HTTP Some things the patch does not implement but could easily be added if you think this is necessary: - No HSTS header for Redirect backends - No support for the HSTS includeSubDomains option - No stripping of HSTS headers in HTTPListeners Regards, Frank
pound-2.7c-sts.diff
(application/x-download, 6.4 KB)
diff -ru Pound-2.7c.orig/config.c Pound-2.7c/config.c
--- Pound-2.7c.orig/config.c 2014-04-21 13:16:08.000000000 +0200
+++ Pound-2.7c/config.c 2014-09-18 15:24:27.261424603 +0200
@@ -76,7 +76,7 @@
static regex_t Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogLevel, Alive, SSLEngine, Control;
static regex_t ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client, CheckURL;
static regex_t Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination;
-static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr;
+static regex_t Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emergency, Priority, HAport, HAportAddr, StrictTransportSecurity;
static regex_t Redirect, RedirectN, TimeOut, Session, Type, TTL, ID, DynScale;
static regex_t ClientCert, AddHeader, DisableSSLv2, SSLAllowClientRenegotiation, SSLHonorCipherOrder, Ciphers;
static regex_t CAlist, VerifyList, CRLlist, NoHTTPS11, Grace, Include, ConnTO, IgnoreCase, HTTPS, HTTPSCert;
@@ -531,6 +531,7 @@
memset(res, 0, sizeof(SERVICE));
res->sess_type = SESS_NONE;
res->dynscale = dynscale;
+ res->sts = -1;
pthread_mutex_init(&res->mut, NULL);
if(svc_name)
strncpy(res->name, svc_name, KEY_SIZE);
@@ -592,6 +593,8 @@
lin[matches[1].rm_eo] = '\0';
if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED))
conf_err("HeadDeny bad pattern - aborted");
+ } else if(!regexec(&StrictTransportSecurity, lin, 4, matches, 0)) {
+ res->sts = atoi(lin + matches[1].rm_so);
} else if(!regexec(&Redirect, lin, 4, matches, 0)) {
if(res->backends) {
for(be = res->backends; be->next; be = be->next)
@@ -818,12 +821,16 @@
} else if(!regexec(&LogLevel, lin, 4, matches, 0)) {
res->log_level = atoi(lin + matches[1].rm_so);
} else if(!regexec(&Service, lin, 4, matches, 0)) {
- if(res->services == NULL)
+ if(res->services == NULL) {
res->services = parse_service(NULL);
- else {
+ if(res->services->sts >= 0)
+ conf_err("StrictTransportSecurity not allowed in HTTP listener - aborted");
+ } else {
for(svc = res->services; svc->next; svc = svc->next)
;
svc->next = parse_service(NULL);
+ if(svc->next->sts >= 0)
+ conf_err("StrictTransportSecurity not allowed in HTTP listener - aborted");
}
} else if(!regexec(&ServiceName, lin, 4, matches, 0)) {
lin[matches[1].rm_eo] = '\0';
@@ -1404,6 +1411,7 @@
|| regcomp(&URL, "^[ \t]*URL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
|| regcomp(&HeadRequire, "^[ \t]*HeadRequire[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
|| regcomp(&HeadDeny, "^[ \t]*HeadDeny[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+ || regcomp(&StrictTransportSecurity, "^[ \t]*StrictTransportSecurity[ \ t]+([0-9]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
|| regcomp(&BackEnd, "^[ \t]*BackEnd[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
|| regcomp(&Emergency, "^[ \t]*Emergency[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
|| regcomp(&Priority, "^[ \t]*Priority[ \t]+([1-9])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
@@ -1566,6 +1574,7 @@
regfree(&URL);
regfree(&HeadRequire);
regfree(&HeadDeny);
+ regfree(&StrictTransportSecurity);
regfree(&BackEnd);
regfree(&Emergency);
regfree(&Priority);
diff -ru Pound-2.7c.orig/http.c Pound-2.7c/http.c
--- Pound-2.7c.orig/http.c 2014-04-21 13:16:08.000000000 +0200
+++ Pound-2.7c/http.c 2014-09-18 15:57:35.779295210 +0200
@@ -1379,6 +1379,8 @@
if(!no_cont && !regexec(&RESP_IGN, response, 0, NULL, 0))
no_cont = 1;
+ for(n = 0; n < MAXHEADERS; n++)
+ headers_ok[n] = 1;
for(chunked = 0, cont = -1L, n = 1; n < MAXHEADERS && headers[n]; n++) {
switch(check_header(headers[n], buf)) {
case HEADER_CONNECTION:
@@ -1429,6 +1431,11 @@
}
}
break;
+ case HEADER_STRICT_TRANSPORT_SECURITY:
+ /* enforce pound's STS header */
+ if(svc->sts >= 0)
+ headers_ok[n] = 0;
+ break;
}
}
@@ -1438,6 +1445,8 @@
/* send the response */
if(!skip)
for(n = 0; n < MAXHEADERS && headers[n]; n++) {
+ if(!headers_ok[n])
+ continue;
if(BIO_printf(cl, "%s\r\n", headers[n]) <= 0) {
if(errno) {
addr2str(caddr, MAXBUF - 1, &from_host, 1);
@@ -1449,6 +1458,8 @@
}
}
free_headers(headers);
+ if(!skip && ssl && svc->sts >= 0)
+ BIO_printf(cl, "Strict-Transport-Security: max-age=%d\r\n", svc->sts);
/* final CRLF */
if(!skip)
diff -ru Pound-2.7c.orig/pound.h Pound-2.7c/pound.h
--- Pound-2.7c.orig/pound.h 2014-04-21 13:16:08.000000000 +0200
+++ Pound-2.7c/pound.h 2014-09-18 15:58:30.597645409 +0200
@@ -370,6 +370,7 @@
#endif
int dynscale; /* true if the back-ends should be dynamically rescaled */
int disabled; /* true if the service is disabled */
+ int sts; /* strict transport security */
struct _service *next;
} SERVICE;
@@ -441,6 +442,7 @@
#define HEADER_URI 9
#define HEADER_DESTINATION 10
#define HEADER_EXPECT 11
+#define HEADER_STRICT_TRANSPORT_SECURITY 12
/* control request stuff */
typedef enum {
diff -ru Pound-2.7c.orig/svc.c Pound-2.7c/svc.c
--- Pound-2.7c.orig/svc.c 2014-04-21 13:16:08.000000000 +0200
+++ Pound-2.7c/svc.c 2014-09-18 15:58:57.444755396 +0200
@@ -391,6 +391,7 @@
{ "User-agent", 10, HEADER_USER_AGENT },
{ "Destination", 11, HEADER_DESTINATION },
{ "Expect", 6, HEADER_EXPECT },
+ { "Strict-Transport-Security", 25, HEADER_STRICT_TRANSPORT_SECURITY },
{ "", 0, HEADER_OTHER },
};
int i;