Skip to content
Browse files

Implements "HTTP Strict Transport Security" (HSTS).

git-svn-id: svn://cherokee-project.com/cherokee/trunk@6885 5dc97367-97f1-0310-9951-d761b3857238
  • Loading branch information...
1 parent acd3cbb commit 231252b135cc21afea2f48cdb23e15e61a41ea42 @alobbs alobbs committed Oct 6, 2011
View
18 admin/PageVServer.py
@@ -73,6 +73,9 @@
NOTE_UTC_TIME = N_('Time standard to use in the log file entries.')
NOTE_INDEX_USAGE = N_('Remember that only "File Exists" rules and "List & Send" handlers use the Directory Indexes setting.')
NOTE_MATCH_NICK = N_('Use this nickname as an additional host name for this virtual server (Default: yes)')
+NOTE_HSTS = N_('Enforce HTTPS by using the HTTP Strict Transport Security.')
+NOTE_HSTS_MAXAGE = N_("How long the client's browser should remember the forced HTTPS (in seconds).")
+NOTE_HSTS_SUBDOMAINS = N_("Should HSTS be used in all the subdomains of this virtual verser (Default: yes).")
DEFAULT_HOST_NOTE = N_("<p>The 'default' virtual server matches all the domain names.</p>")
@@ -677,6 +680,21 @@ def __init__ (self, vsrv_num, refreshable):
self += CTK.RawHTML ('<h2>%s</h2>' % (_('Advanced Options')))
self += CTK.Indenter (submit)
+ # HSTS
+ table = CTK.PropsTable()
+ table.Add (_('Enable HSTS'), CTK.CheckCfgText ('%s!hsts'%(pre), False, _('Accept')), _(NOTE_HSTS))
+
+ if int(CTK.cfg.get_val('%s!hsts' %(pre), "0")):
+ table.Add (_('HSTS Max-Age'), CTK.TextCfg ('%s!hsts!max_age'%(pre), True, {'optional_string':_("One year")}), _(NOTE_HSTS_MAXAGE))
+ table.Add (_('Include Subdomains'), CTK.CheckCfgText ('%s!subdomains'%(pre), True, _('Include all')), _(NOTE_HSTS_SUBDOMAINS))
+
+ submit = CTK.Submitter (url_apply)
+ submit.bind ('submit_success', refreshable.JS_to_refresh())
+ submit += table
+
+ self += CTK.RawHTML ('<h2>%s</h2>' % (_('HTTP Strict Transport Security (HSTS)')))
+ self += CTK.Indenter (submit)
+
class SecurityWidget (CTK.Container):
def __init__ (self, vsrv_num):
View
1 cherokee/connection-protected.h
@@ -274,6 +274,7 @@ ret_t cherokee_connection_recv (cherokee_connection_t *conn, c
ret_t cherokee_connection_create_handler (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);
ret_t cherokee_connection_create_encoder (cherokee_connection_t *conn, cherokee_avl_t *accept_enc);
ret_t cherokee_connection_setup_error_handler (cherokee_connection_t *conn);
+ret_t cherokee_connection_setup_hsts_handler (cherokee_connection_t *conn);
ret_t cherokee_connection_check_authentication (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);
ret_t cherokee_connection_check_ip_validation (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);
ret_t cherokee_connection_check_only_secure (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);
View
37 cherokee/connection.c
@@ -555,6 +555,43 @@ cherokee_connection_setup_error_handler (cherokee_connection_t *conn)
}
+ret_t
+cherokee_connection_setup_hsts_handler (cherokee_connection_t *conn)
+{
+ ret_t ret;
+
+ /* Redirect to:
+ * "https://" + host + request + query_string
+ */
+ cherokee_buffer_clean (&conn->redirect);
+ cherokee_buffer_add_str (&conn->redirect, "https://");
+
+ cherokee_connection_build_host_port_string (conn, &conn->redirect);
+ cherokee_buffer_add_buffer (&conn->redirect, &conn->request);
+
+ if (conn->query_string.len > 0) {
+ cherokee_buffer_add_char (&conn->redirect, '?');
+ cherokee_buffer_add_buffer (&conn->redirect, &conn->query_string);
+ }
+
+ /* 301 response: Move Permanetly
+ */
+ conn->error_code = http_moved_permanently;
+
+ /* Instance the handler object
+ */
+ ret = cherokee_handler_error_new (&conn->handler, conn, NULL);
+ if (unlikely (ret != ret_ok)) {
+ return ret_error;
+ }
+
+ TRACE (ENTRIES, "HSTS redirection handler set. Phase is '%s' now.\n", "init");
+ conn->phase = phase_init;
+
+ return ret_ok;
+}
+
+
static void
build_response_header_authentication (cherokee_connection_t *conn, cherokee_buffer_t *buffer)
{
View
17 cherokee/handler_error.c
@@ -275,6 +275,23 @@ cherokee_handler_error_add_headers (cherokee_handler_error_t *hdl, cherokee_buff
cherokee_buffer_add_str (buffer, CRLF);
}
+ /* HSTS support
+ */
+ if ((conn->socket.is_tls != TLS) &&
+ (CONN_VSRV(conn)->hsts.enabled) &&
+ (conn->error_code == http_moved_permanently))
+ {
+ cherokee_buffer_add_str (buffer, "Strict-Transport-Security: ");
+ cherokee_buffer_add_str (buffer, "max-age=");
+ cherokee_buffer_add_ulong10 (buffer, (culong_t) CONN_VSRV(conn)->hsts.max_age);
+
+ if (CONN_VSRV(conn)->hsts.subdomains) {
+ cherokee_buffer_add_str (buffer, "; includeSubdomains");
+ }
+
+ cherokee_buffer_add_str (buffer, CRLF);
+ }
+
/* Usual headers
*/
cherokee_buffer_add_str (buffer, "Content-Type: text/html"CRLF);
View
10 cherokee/thread.c
@@ -944,7 +944,6 @@ process_active_connections (cherokee_thread_t *thd)
cherokee_collector_log_request (THREAD_SRV(thd)->collector);
}
-
conn->phase = phase_setup_connection;
/* fall down */
@@ -957,6 +956,15 @@ process_active_connections (cherokee_thread_t *thd)
*/
conn_set_mode (thd, conn, socket_writing);
+ /* HSTS support
+ */
+ if ((conn->socket.is_tls != TLS) &&
+ (CONN_VSRV(conn)->hsts.enabled))
+ {
+ cherokee_connection_setup_hsts_handler (conn);
+ continue;
+ }
+
/* Is it already an error response?
*/
if (http_type_300 (conn->error_code) ||
View
25 cherokee/virtual_server.c
@@ -65,6 +65,10 @@ cherokee_virtual_server_new (cherokee_virtual_server_t **vserver, void *server)
n->match_nick = true;
n->flcache = NULL;
+ n->hsts.enabled = false;
+ n->hsts.subdomains = true;
+ n->hsts.max_age = 365 * 24 * 60 * 60;
+
/* Virtual entries
*/
ret = cherokee_rule_list_init (&n->rules);
@@ -948,6 +952,22 @@ add_error_writer (cherokee_config_node_t *config,
static ret_t
+add_hsts (cherokee_config_node_t *config,
+ cherokee_virtual_server_t *vserver)
+{
+ ret_t ret;
+
+ ret = cherokee_atob (config->val.buf, &vserver->hsts.enabled);
+ if (ret != ret_ok) return ret_error;
+
+ cherokee_config_node_read_int (config, "max_age", &vserver->hsts.max_age);
+ cherokee_config_node_read_bool (config, "subdomains", &vserver->hsts.subdomains);
+
+ return ret_ok;
+}
+
+
+static ret_t
add_logger (cherokee_config_node_t *config,
cherokee_virtual_server_t *vserver)
{
@@ -1093,6 +1113,11 @@ configure_virtual_server_property (cherokee_config_node_t *conf, void *data)
if (ret != ret_ok)
return ret;
+ } else if (equal_buf_str (&conf->key, "hsts")) {
+ ret = add_hsts (conf, vserver);
+ if (ret != ret_ok)
+ return ret;
+
} else if (equal_buf_str (&conf->key, "directory_index")) {
cherokee_config_node_read_list (conf, NULL, add_directory_index, vserver);
View
6 cherokee/virtual_server.h
@@ -77,6 +77,12 @@ typedef struct {
cherokee_buffer_t ciphers;
cherokee_cryptor_vserver_t *cryptor;
+ struct {
+ cherokee_boolean_t enabled;
+ cherokee_boolean_t subdomains;
+ cuint_t max_age;
+ } hsts;
+
} cherokee_virtual_server_t;
#define VSERVER(v) ((cherokee_virtual_server_t *)(v))
View
29 qa/292-HSTS1.py
@@ -0,0 +1,29 @@
+from base import *
+
+NICK = "test-2920"
+MAX_AGE = 123456
+
+CONF = """
+vserver!2920!nick = %(NICK)s
+vserver!2920!document_root = %(droot)s
+vserver!2920!hsts = 1
+vserver!2920!hsts!max_age = %(MAX_AGE)s
+vserver!2920!rule!1!match = default
+vserver!2920!rule!1!handler = dirlist
+"""
+
+class Test (TestBase):
+ def __init__ (self):
+ TestBase.__init__ (self, __file__)
+ self.name = "HSTS: Error code and Header"
+ self.request = "HTTP / HTTP/1.0\r\n" + \
+ "Host: %s\r\n" %(NICK)
+ self.expected_error = 301
+ self.expected_content = ["Strict-Transport-Security:", "max-age=%d"%(MAX_AGE)]
+
+ def Prepare (self, www):
+ droot = self.Mkdir (www, "%s_droot"%(NICK))
+
+ vars = globals()
+ vars.update(locals())
+ self.conf = CONF %(vars)
View
3 qa/Makefile.am
@@ -312,7 +312,8 @@ run-tests.py \
288-GZip-IE16.py \
289-Connection_TE.py \
290-Question-mark-in-name.py \
-291-Redir-keepalive.py
+291-Redir-keepalive.py \
+292-HSTS1.py
test:
python -m compileall .

0 comments on commit 231252b

Please sign in to comment.
Something went wrong with that request. Please try again.