Skip to content

Commit

Permalink
Implements "HTTP Strict Transport Security" (HSTS).
Browse files Browse the repository at this point in the history
git-svn-id: svn://cherokee-project.com/cherokee/trunk@6885 5dc97367-97f1-0310-9951-d761b3857238
  • Loading branch information
alobbs committed Oct 6, 2011
1 parent acd3cbb commit 231252b
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 2 deletions.
18 changes: 18 additions & 0 deletions admin/PageVServer.py
Expand Up @@ -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>")

Expand Down Expand Up @@ -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):
Expand Down
1 change: 1 addition & 0 deletions cherokee/connection-protected.h
Expand Up @@ -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);
Expand Down
37 changes: 37 additions & 0 deletions cherokee/connection.c
Expand Up @@ -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)
{
Expand Down
17 changes: 17 additions & 0 deletions cherokee/handler_error.c
Expand Up @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion cherokee/thread.c
Expand Up @@ -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 */
Expand All @@ -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) ||
Expand Down
25 changes: 25 additions & 0 deletions cherokee/virtual_server.c
Expand Up @@ -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);
Expand Down Expand Up @@ -947,6 +951,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)
Expand Down Expand Up @@ -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);

Expand Down
6 changes: 6 additions & 0 deletions cherokee/virtual_server.h
Expand Up @@ -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))
Expand Down
29 changes: 29 additions & 0 deletions 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)
3 changes: 2 additions & 1 deletion qa/Makefile.am
Expand Up @@ -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 .
Expand Down

0 comments on commit 231252b

Please sign in to comment.