From 7b0ed5d9bd8b40ce9ae7da9d7f719cb1ef81df0a Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 10 Jun 2014 22:47:22 +0900 Subject: [PATCH] nghttpx: Only allow DHE, ECDHE + AEAD ciphers for HTTP/2 Cipher suites are chosen by DHE and ECDHE ciphers + GCM (AEAD). Now default cipher list is the one recommended by Mozilla web site. The --honor-cipher-order option is removed and now it is always assumed. --- mkcipherlist.py | 30 ++++++++++++++++ src/shrpx.cc | 13 +------ src/shrpx_config.cc | 7 ---- src/shrpx_config.h | 2 -- src/shrpx_ssl.cc | 88 ++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 111 insertions(+), 29 deletions(-) create mode 100755 mkcipherlist.py diff --git a/mkcipherlist.py b/mkcipherlist.py new file mode 100755 index 00000000..349ba5d2 --- /dev/null +++ b/mkcipherlist.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +import re +import sys +import csv + +pat = re.compile(r'\ATLS_(?:ECDHE|DHE)_.*_GCM') + +ciphers = [] +for hl, name, _, _ in csv.reader(sys.stdin): + if not pat.match(name): + continue + + high, low = hl.split(',') + + id = high + low[2:] + 'u' + ciphers.append((id, name)) + +print '''\ +enum {''' + +for id, name in ciphers: + print '{} = {},'.format(name, id) + +print '''\ +}; +''' + +for id, name in ciphers: + print '''\ +case {}:'''.format(name) diff --git a/src/shrpx.cc b/src/shrpx.cc index 4ff3c0d8..0b407965 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -407,7 +407,6 @@ void fill_default_config() // Default accept() backlog mod_config()->backlog = -1; mod_config()->ciphers = nullptr; - mod_config()->honor_cipher_order = false; mod_config()->http2_proxy = false; mod_config()->http2_bridge = false; mod_config()->client_proxy = false; @@ -616,12 +615,7 @@ Timeout: SSL/TLS: --ciphers= Set allowed cipher list. The format of the - string is described in OpenSSL ciphers(1). If - this option is used, --honor-cipher-order is - implicitly enabled. - --honor-cipher-order - Honor server cipher order, giving the ability to - mitigate BEAST attacks. + string is described in OpenSSL ciphers(1). -k, --insecure Don't verify backend server's certificate if -p, --client or --http2-bridge are given and @@ -859,7 +853,6 @@ int main(int argc, char **argv) {"backend-no-tls", no_argument, &flag, 27}, {"frontend-no-tls", no_argument, &flag, 29}, {"backend-tls-sni-field", required_argument, &flag, 31}, - {"honor-cipher-order", no_argument, &flag, 32}, {"dh-param-file", required_argument, &flag, 33}, {"read-rate", required_argument, &flag, 34}, {"read-burst", required_argument, &flag, 35}, @@ -1051,10 +1044,6 @@ int main(int argc, char **argv) // --backend-tls-sni-field cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD, optarg); break; - case 32: - // --honor-cipher-order - cmdcfgs.emplace_back(SHRPX_OPT_HONOR_CIPHER_ORDER, "yes"); - break; case 33: // --dh-param-file cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, optarg); diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 7af4dca9..54dbe004 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -93,7 +93,6 @@ const char SHRPX_OPT_SYSLOG[] = "syslog"; const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility"; const char SHRPX_OPT_BACKLOG[] = "backlog"; const char SHRPX_OPT_CIPHERS[] = "ciphers"; -const char SHRPX_OPT_HONOR_CIPHER_ORDER[] = "honor-cipher-order"; const char SHRPX_OPT_CLIENT[] = "client"; const char SHRPX_OPT_INSECURE[] = "insecure"; const char SHRPX_OPT_CACERT[] = "cacert"; @@ -572,12 +571,6 @@ int parse_config(const char *opt, const char *optarg) return 0; } - if(util::strieq(opt, SHRPX_OPT_HONOR_CIPHER_ORDER)) { - mod_config()->honor_cipher_order = util::strieq(optarg, "yes"); - - return 0; - } - if(util::strieq(opt, SHRPX_OPT_CLIENT)) { mod_config()->client = util::strieq(optarg, "yes"); diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 06243978..3287d3f1 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -84,7 +84,6 @@ extern const char SHRPX_OPT_SYSLOG[]; extern const char SHRPX_OPT_SYSLOG_FACILITY[]; extern const char SHRPX_OPT_BACKLOG[]; extern const char SHRPX_OPT_CIPHERS[]; -extern const char SHRPX_OPT_HONOR_CIPHER_ORDER[]; extern const char SHRPX_OPT_CLIENT[]; extern const char SHRPX_OPT_INSECURE[]; extern const char SHRPX_OPT_CACERT[]; @@ -245,7 +244,6 @@ struct Config { bool syslog; // This member finally decides syslog is used or not bool use_syslog; - bool honor_cipher_order; bool client; // true if --client or --client-proxy are enabled. bool client_mode; diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 186eebeb..115f9f1e 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -250,14 +250,14 @@ SSL_CTX* create_ssl_context(const char *private_key_file, const char *ciphers; if(get_config()->ciphers) { ciphers = get_config()->ciphers.get(); - // If ciphers are given, honor its order unconditionally - SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); } else { - ciphers = "HIGH:!aNULL:!eNULL"; - if(get_config()->honor_cipher_order) { - SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - } + // Recommended general purpose cipher by mozilla. + // https://wiki.mozilla.org/Security/Server_Side_TLS + ciphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK"; } + + SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + if(SSL_CTX_set_cipher_list(ssl_ctx, ciphers) == 0) { LOG(FATAL) << "SSL_CTX_set_cipher_list " << ciphers << " failed: " << ERR_error_string(ERR_get_error(), nullptr); @@ -907,6 +907,40 @@ bool in_proto_list(const std::vector& protos, return false; } +// This enum was generated by mkcipherlist.py +enum { + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009Eu, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009Fu, + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2u, + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3u, + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AAu, + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00ABu, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02Bu, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02Cu, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02Fu, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030u, + TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC052u, + TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC053u, + TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC056u, + TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC057u, + TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05Cu, + TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05Du, + TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC060u, + TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC061u, + TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06Cu, + TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06Du, + TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07Cu, + TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07Du, + TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC080u, + TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC081u, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC086u, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC087u, + TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08Au, + TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08Bu, + TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC090u, + TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC091u, +}; + bool check_http2_requirement(SSL *ssl) { auto tls_ver = SSL_version(ssl); @@ -921,8 +955,46 @@ bool check_http2_requirement(SSL *ssl) return false; } - // TODO Figure out that ECDHE or DHE was negotiated and their key - // size. SSL_get_server_tmp_key() cannot be used on server side. + auto cipher = SSL_get_current_cipher(ssl); + + switch(SSL_CIPHER_get_id(cipher) & 0xffffu) { + // This case labels were generated by mkcipherlist.py + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384: + case TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256: + case TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384: + case TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256: + case TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384: + case TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + break; + default: + return false; + } + + // TODO Check number of bits return true; }