Nginx配置WordPress与HSTS

本文介绍HSTS基本概念,以及在nginx上配置HSTS的方法,配置站点基于WordPress。

本博客(https://blog.lyz810.com)已开启HSTS,读者可以尝试访问http协议,并观察浏览器的行为。
一、背景介绍
HSTS(HTTP Strict Transport Security)是HTTP严格传输安全,它是全站HTTPS时的一个更安全的策略,并且对网站性能有一定的优化。
全站HTTPS是一个必然的趋势,目前全站HTTPS通常的做法是同时开启80和443端口,当请求访问的是http协议时,通过rewrite将请求301重定向到https协议的对应URI。这么做的缺点是当用户访问http协议的页面时,总会有一次301重定向,增加网络请求以及服务器负担。
使用HSTS后,浏览器在第一次访问http协议时,会通过rewrite重定向到https协议,并根据https协议头中设定的相关响应头缓存下HSTS配置,下次用户再访问该站点下的任意http页面,浏览器会自动通过307跳转到https对应的URI上,不需要向服务器多发送一次请求。
HSTS配置方法如下:
1.当客户端通过HTTPS发出请求时,在服务器返回的超文本传输协议响应头中包含Strict-Transport-Security字段。非加密传输时设置的HSTS字段无效。
2.Strict-Transport-Security有3个参数,max-age表示有效期(如max-age=31536000表示未来的一年内,访问该域名会强制使用https,其中数字表示一年的秒数);includeSubDomains表示子域名也强制使用https;preload主要用于加入preload列表使用。
preload列表是一个站点的列表,他将会被通过硬编码写入 Chrome 浏览器中,列表中的站点将会默认使用 HTTPS 进行访问,此外,Firefox 、Safari 、IE 11 和 Edge 也同样一份 HSTS 站点列表,其中包括了 Chrome 的列表
加入preload列表的条件:

  • 有一张有效的证书
  • 重定向所有的 HTTP 流量到 HTTPS ( HTTPS ONLY )
  • 全部子域名的流量均通过 HTTPS ,如果子域名的 www 存在的话也同样需要通过 HTTPS 传输。
  • 为https协议添加的响应头Strict-Transport-Securit内容必须满足:max-age必须大于18周,必须指定includeSubDomains和preload,如果从当前https站点重定向到其他的站点,那个站点也必须启用HSTS

加入HSTS preload list:https://hstspreload.appspot.com/
二、Nginx配置

server {
  ......
  if ( $https != "on" ) {#https变量的值在https协议下为on,否则为空字符串
     rewrite ^(.*) https://$host$1 permanent;#301定向到https协议的相同URI,主要是首次访问本站的请求使用
     break;
  }

  location / {
     try_files $uri $uri/ /index.php$args;#此行是WordPress使用的,它会将请求都交给/index.php处理
     add_header Strict-Transport-Security max-age=86400000;#添加响应头,过期时间1000天,即1000天之内,浏览器会自动将本站http协议转为https协议
  }

  location ~ \.php$ {
     fastcgi_pass localhost:9000;#fastcgi监听9000端口,其他fastcgi配置写在了公用配置文件中
     add_header Strict-Transport-Security max-age=86400000;#由于正则的优先级更高,所以所有动态的页面均会走这里,需要添加响应头
   }
}

三、使用HSTS的弊端
用户只要访问一次站点,就会在max-age指定的时间内,强制访问https协议,在此期间,如果网站https出现了问题(如证书配置错误),用户就无法进行访问,没有配置HSTS时,证书错误用户可以选择忽略错误继续访问。
如果想要在过期时间内再次开启http访问,是无法做到的(对没有访问过站点的用户可行,只要访问过,过期时间前用户不能访问http协议),如果加入到HSTS Preload List中,就更没有办法启用http访问。
四、浏览器支持度
Chromium和Google Chrome从4.0.211.0版本开始支持HSTS
Firefox 4及以上版本
Opera 12及以上版本
Safari从OS X Mavericks起
Internet Explorer和Microsoft Edge从Windows 10开始支持

解决openssl升级到1.1.0后shadowsocks服务报错问题

本文适用于解决openssl升级到1.1.0以上版本,导致shadowsocks2.8.2启动报undefined symbol: EVP_CIPHER_CTX_cleanup错误。

最近将openssl升级到了1.1.0b版本,编译之后shadowsocks无法启动,报错如下:

Traceback (most recent call last):
  File "/usr/bin/ssserver", line 9, in 
    load_entry_point('shadowsocks==2.8.2', 'console_scripts', 'ssserver')()
  File "/usr/lib/python2.7/site-packages/shadowsocks/server.py", line 34, in main
    config = shell.get_config(False)
  File "/usr/lib/python2.7/site-packages/shadowsocks/shell.py", line 262, in get_config
    check_config(config, is_local)
  File "/usr/lib/python2.7/site-packages/shadowsocks/shell.py", line 124, in check_config
    encrypt.try_cipher(config['password'], config['method'])
  File "/usr/lib/python2.7/site-packages/shadowsocks/encrypt.py", line 44, in try_cipher
    Encryptor(key, method)
  File "/usr/lib/python2.7/site-packages/shadowsocks/encrypt.py", line 83, in __init__
    random_string(self._method_info[1]))
  File "/usr/lib/python2.7/site-packages/shadowsocks/encrypt.py", line 109, in get_cipher
    return m[2](method, key, iv, op)
  File "/usr/lib/python2.7/site-packages/shadowsocks/crypto/openssl.py", line 76, in __init__
    load_openssl()
  File "/usr/lib/python2.7/site-packages/shadowsocks/crypto/openssl.py", line 52, in load_openssl
    libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,)
  File "/usr/lib64/python2.7/ctypes/__init__.py", line 373, in __getattr__
    func = self.__getitem__(name)
  File "/usr/lib64/python2.7/ctypes/__init__.py", line 378, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/local/ssl/lib/libcrypto.so.1.1: undefined symbol: EVP_CIPHER_CTX_cleanup
shadowsocks start failed

这个问题是由于在openssl1.1.0版本中,废弃了EVP_CIPHER_CTX_cleanup函数,如官网中所说:

EVP_CIPHER_CTX was made opaque in OpenSSL 1.1.0. As a result, EVP_CIPHER_CTX_reset() appeared and EVP_CIPHER_CTX_cleanup() disappeared. EVP_CIPHER_CTX_init() remains as an alias for EVP_CIPHER_CTX_reset().

EVP_CIPHER_CTX_reset函数替代了EVP_CIPHER_CTX_cleanup函数
EVP_CIPHER_CTX_reset函数说明:

EVP_CIPHER_CTX_reset() clears all information from a cipher context and free up any allocated memory associate with it, except the ctx itself. This function should be called anytime ctx is to be reused for another EVP_CipherInit() / EVP_CipherUpdate() / EVP_CipherFinal() series of calls.

EVP_CIPHER_CTX_cleanup函数说明:

EVP_CIPHER_CTX_cleanup() clears all information from a cipher context and free up any allocated memory associate with it. It should be called after all operations using a cipher are complete so sensitive information does not remain in memory.

可以看出,二者功能基本上相同,都是释放内存,只是应该调用的时机稍有不同,所以用reset代替cleanup问题不大。

修改方法:

  1. 用vi打开文件:vi /usr/lib/python2.7/site-packages/shadowsocks/crypto/openssl.py
  2. 跳转到52行(shadowsocks2.8.2版本,其他版本搜索一下cleanup)
  3. 进入编辑模式
  4. 将第52行libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p,)
    改为libcrypto.EVP_CIPHER_CTX_reset.argtypes = (c_void_p,)
  5. 再次搜索cleanup(全文件共2处,此处位于111行),将libcrypto.EVP_CIPHER_CTX_cleanup(self._ctx)
    改为libcrypto.EVP_CIPHER_CTX_reset(self._ctx)
  6. 保存并推出
  7. 启动shadowsocks服务:service shadowsocks start

之后我们就可以继续愉快的、科学的畅游互联网了。
提示:openssl1.1.0目前兼容性很不好,大部分的软件都不支持
目前支持的有nginx-1.11.5、curl-7.50.3
不支持的有PHP-7.0.12、openssh-7.3p1
所以如果决定使用openssl1.1.0需要考虑很多兼容问题,必须保留1.0.2或1.0.1(不推荐,存在一些已知漏洞,最重要的是如果服务器要开http2,由于新版chrome必须使用ALPN的限制,只有1.0.2版本支持ALPN,所以必须升级到1.0.2)版本以便编译其他程序。