工具与配置

HTTPS Tools

OpenSSL

OpenSSL 是用 C 写的一套 SSL 和 TLS 开源实现。这也就意味着人人都可以基于这个构建属于自己的认证机构,然后给自己的颁发服务器证书。不过然并卵,其证书不可在互联网上作为证书使用。这种自认证机构给自己颁发的证书,叫做自签名证书。自己给自己作证,自然是算不得数的。所以浏览器在访问这种服务器时,会显示 “ 无法确认连接安全性 ” 等警告消息。 OpenSSL 在 2014 年 4 月,被爆出一个内存溢出引出的 BUG,骇客利用这点能拿到服务器很多信息,其中就包括私钥,也就使得 HTTPS 形同虚设。当时全世界大概有一百万左右的服务器有受到此漏洞的影响。由于 OpenSSL 举足轻重的作用,再加上足够致命的问题,使得这个 BUG 被形容为 “ 互联网心脏出血 ”。这是近年来互联网最严重的安全事件。

记得 OpenSSL 的 Heartbleed 漏洞才出来的时候,笔者所在的安全公司忙成了一团糟,到处帮忙修补漏洞。

Let's Encrypt: 免费 SSL

Let’s Encrypt 是由 ISRG(Internet Security Research Group )提供的免费 SSL 项目,现由 Linux 基金会托管,他的来头很大,由 Mozilla、思科、Akamai 、 IdenTrust 和 EFF 等组织 发起,现在已经得到 Google、Facebook 等大公司的支持和赞助,目的就是向网站免费签发和管理证书,并且通过其自身的自动化过程,消除了购买、 安装证书的复杂性,只需几行命令,就可以完成证书的生成并投入使用,甚至十几分钟就可以让自己的 http 站点华丽转变成 Https 站点。

Installation

( 1)执行以下命令

git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
./letsencrypt-auto certonly --email xxx@xx.com

提示: 1 、如果提示 git 命令无效的话,需要安装一下 GIt,直接执行命令 yum install git-all 完成安装, 2 、如果是 RedHat/CentOs6 系统的话,需要提前安装 EPEL( Extra Packages for Enterprise Linux ),执行命令 yum install epel-release 3、 整个过程需要主机连接外网,否则会导致报以下错误

IMPORTANT NOTES:
- The following errors were reported by the server:
Domain: on-img.com
Type: urn:acme:error:connection
Detail: Failed to connect to host for DVSNI challenge
Domain: www.on-img.com
Type: urn:acme:error:connection
Detail: Failed to connect to host for DVSNI challenge

4、Let's encrypt 是由 python 编写的开源项目,基于 python2.7 环境,如果系统安装的是 python2.6,会提示升级 。也可以执行以下命令(官方不推荐) ./letsencrypt-auto certonly --email xxx@xx.com --debug

( 2)接下来提示输入域名 多个用空格隔开 出现以下提示说明证书生成成功

使用证书

进入 /etc/letsencrypt/live/on-img.com/ 下,on-img.com 是第二部中填写的域名,到时候换成自己的域名即可。

  • cert.pem 服务器证书

  • privkey.pem 是证书私钥

如果是云服务器 + 负载均衡的话,直接添加以上证书,绑定负载均衡,直接访问 https:// xxx.com。如果是自己配置的 Nginx 的,需要以下配置 :

server
{
listen 443 ssl; /
server_name xxx.com; //这里是你的域名
index index.html index.htm index.php default.html default.htm default.php;
root /opt/wwwroot/ //网站目录
ssl_certificate /etc/letsencrypt/live/test.com/fullchain.pem; //前面生成的证书,改一下里面的域名就行,不建议更换路径
ssl_certificate_key /etc/letsencrypt/live/test.com/privkey.pem; //前面生成的密钥,改一下里面的域名就行,不建议更换路径
........
}

如果是使用的 Apache 服务器,在生成证书后也需要修改一下 apache 的配置文件 /usr/local/apache/conf/httpd.conf ,查找 httpd-ssl 将前面的 # 去掉。然后再执行:

cat >/usr/local/apache/conf/extra/httpd-ssl.conf<<EOF Listen 443
AddType application/x-x509-ca-cert .crt AddType application/x-pkcs7-crl .crl
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLProxyCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH SSLHonorCipherOrder on
SSLProtocol all -SSLv2 -SSLv3 SSLProxyProtocol all -SSLv2 -SSLv3 SSLPassPhraseDialog builtin
SSLSessionCache "shmcb:/usr/local/apache/logs/ssl_scache(512000)" SSLSessionCacheTimeout 300
SSLMutex "file:/usr/local/apache/logs/ssl_mutex" EOF

并在对应 apache 虚拟主机配置文件的最后下面添加上 SSL 部分的配置文件:

<VirtualHost *:443>
DocumentRoot /home/wwwroot/www.vpser.net //网站目录
ServerName www.vpser.net:443 //域名
ServerAdmin licess@vpser.net //邮箱
ErrorLog "/home/wwwlogs/www.vpser.net-error_log" //错误日志
CustomLog "/home/wwwlogs/www.vpser.net-access_log" common //访问日志
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/www.test.net/fullchain.pem //改一下里面的域名就行,不建议更换路径
SSLCertificateKeyFile /etc/letsencrypt/live/www.test.net/privkey.pem //改一下里面的域名就行,不建议更换路径
<Directory "/home/wwwroot/www.vpser.net"> //网站目录
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
DirectoryIndex index.html index.php
</Directory>
</VirtualHost>

auto-sni: 自动构建基于 HTTPS 的 NodeJS 服务端

  • 安装

npm install auto-sni
  • 创建服务器

var createServer = require("auto-sni");
var server = createServer({
email: ..., // Emailed when certificates expire.
agreeTos: true, // Required for letsencrypt.
debug: true, // Add console messages and uses staging LetsEncrypt server. (Disable in production)
domains: ["mysite.com", ["test.com", "www.test.com"]], // List of accepted domain names. (You can use nested arrays to register bundles with LE).
forceSSL: true, // Make this false to disable auto http->https redirects (default true).
ports: {
http: 80, // Optionally override the default http port.
https: 443 // // Optionally override the default https port.
}
});
// Server is a "https.createServer" instance.server.once("listening", ()=> {
console.log("We are ready to go.");
});
//使用Express
var createServer = require("auto-sni");
var express = require("express");
var app = express();
app.get("/test", ...);
createServer({ email: ..., agreeTos: true }, app);

SSL Configuration Generator

现在很多用户使用的还是低版本的浏览器,它们对于 SSL/TLS 协议支持的也不是很好,因此怎么为服务器选定一个正确的 HTTPS 也比较麻烦,幸好 Mozilla 提供了一个在线生成配置的工具,很是不错:

SSL Server Test

在你正确配置了你的站点之后,非常推荐使用SSL Labs这个在线测试工具来检查下你站点到底配置的是否安全。

HTTPS Configuration

Apache

<VirtualHost *:443>
...
SSLEngine on
SSLCertificateFile /path/to/signed_certificate_followed_by_intermediate_certs
SSLCertificateKeyFile /path/to/private/key
SSLCACertificateFile /path/to/all_ca_certs
# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always set Strict-Transport-Security "max-age=15768000"
...
</VirtualHost>
# intermediate configuration, tweak to your needs
SSLProtocol all -SSLv3
SSLCipherSuite ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
SSLHonorCipherOrder on
SSLCompression off
SSLSessionTickets off
# OCSP Stapling, only in httpd 2.3.3 and later
SSLUseStapling on
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors off
SSLStaplingCache shmcb:/var/run/ocsp(128000)

Nginx

server {
listen 80 default_server;
listen [::]:80 default_server;
# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /path/to/dhparam.pem;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
## verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
resolver <IP DNS resolver>;
....
}

Lighttpd

$SERVER["socket"] == ":443" {
protocol = "https://"
ssl.engine = "enable"
ssl.disable-client-renegotiation = "enable"
# pemfile is cert+privkey, ca-file is the intermediate chain in one file
ssl.pemfile = "/path/to/signed_cert_plus_private_key.pem"
ssl.ca-file = "/path/to/intermediate_certificate.pem"
# for DH/DHE ciphers, dhparam should be >= 2048-bit
ssl.dh-file = "/path/to/dhparam.pem"
# ECDH/ECDHE ciphers curve strength (see `openssl ecparam -list_curves`)
ssl.ec-curve = "secp384r1"
# Compression is by default off at compile-time, but use if needed
# ssl.use-compression = "disable"
# Environment flag for HTTPS enabled
setenv.add-environment = (
"HTTPS" => "on"
)
# intermediate configuration, tweak to your needs
ssl.use-sslv2 = "disable"
ssl.use-sslv3 = "disable"
ssl.honor-cipher-order = "enable"
ssl.cipher-list = "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"
# HSTS(15768000 seconds = 6 months)
setenv.add-response-header = (
"Strict-Transport-Security" => "max-age=15768000;"
)
...
}

HAProxy

global
# set default parameters to the intermediate configuration
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
ssl-default-bind-options no-sslv3 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS
ssl-default-server-options no-sslv3 no-tls-tickets
frontend ft_test
mode http
bind :443 ssl crt /path/to/<cert+privkey+intermediate+dhparam>
bind :80
redirect scheme https code 301 if !{ ssl_fc }
# HSTS (15768000 seconds = 6 months)
rspadd Strict-Transport-Security:\ max-age=15768000

AWS ELB

{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Example ELB with Mozilla recommended ciphersuite",
"Parameters": {
"SSLCertificateId": {
"Description": "The ARN of the SSL certificate to use",
"Type": "String",
"AllowedPattern": "^arn:[^:]*:[^:]*:[^:]*:[^:]*:.*$",
"ConstraintDescription":
"SSL Certificate ID must be a valid ARN. http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#genref-arns"
}
},
"Resources": {
"ExampleELB": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"Listeners": [
{
"LoadBalancerPort": "443",
"InstancePort": "80",
"PolicyNames": ["Mozilla-intermediate-2015-03"],
"SSLCertificateId": {
"Ref": "SSLCertificateId"
},
"Protocol": "HTTPS"
}
],
"AvailabilityZones": {
"Fn::GetAZs": ""
},
"Policies": [
{
"PolicyName": "Mozilla-intermediate-2015-03",
"PolicyType": "SSLNegotiationPolicyType",
"Attributes": [
{
"Name": "Protocol-TLSv1",
"Value": true
},
{
"Name": "Protocol-TLSv1.1",
"Value": true
},
{
"Name": "Protocol-TLSv1.2",
"Value": true
},
{
"Name": "Server-Defined-Cipher-Order",
"Value": true
},
{
"Name": "ECDHE-ECDSA-CHACHA20-POLY1305",
"Value": true
},
{
"Name": "ECDHE-RSA-CHACHA20-POLY1305",
"Value": true
},
{
"Name": "ECDHE-ECDSA-AES128-GCM-SHA256",
"Value": true
},
{
"Name": "ECDHE-RSA-AES128-GCM-SHA256",
"Value": true
},
{
"Name": "ECDHE-ECDSA-AES256-GCM-SHA384",
"Value": true
},
{
"Name": "ECDHE-RSA-AES256-GCM-SHA384",
"Value": true
},
{
"Name": "DHE-RSA-AES128-GCM-SHA256",
"Value": true
},
{
"Name": "DHE-RSA-AES256-GCM-SHA384",
"Value": true
},
{
"Name": "ECDHE-ECDSA-AES128-SHA256",
"Value": true
},
{
"Name": "ECDHE-RSA-AES128-SHA256",
"Value": true
},
{
"Name": "ECDHE-ECDSA-AES128-SHA",
"Value": true
},
{
"Name": "ECDHE-RSA-AES256-SHA384",
"Value": true
},
{
"Name": "ECDHE-RSA-AES128-SHA",
"Value": true
},
{
"Name": "ECDHE-ECDSA-AES256-SHA384",
"Value": true
},
{
"Name": "ECDHE-ECDSA-AES256-SHA",
"Value": true
},
{
"Name": "ECDHE-RSA-AES256-SHA",
"Value": true
},
{
"Name": "DHE-RSA-AES128-SHA256",
"Value": true
},
{
"Name": "DHE-RSA-AES128-SHA",
"Value": true
},
{
"Name": "DHE-RSA-AES256-SHA256",
"Value": true
},
{
"Name": "DHE-RSA-AES256-SHA",
"Value": true
},
{
"Name": "ECDHE-ECDSA-DES-CBC3-SHA",
"Value": true
},
{
"Name": "ECDHE-RSA-DES-CBC3-SHA",
"Value": true
},
{
"Name": "EDH-RSA-DES-CBC3-SHA",
"Value": true
},
{
"Name": "AES128-GCM-SHA256",
"Value": true
},
{
"Name": "AES256-GCM-SHA384",
"Value": true
},
{
"Name": "AES128-SHA256",
"Value": true
},
{
"Name": "AES256-SHA256",
"Value": true
},
{
"Name": "AES128-SHA",
"Value": true
},
{
"Name": "AES256-SHA",
"Value": true
},
{
"Name": "DES-CBC3-SHA",
"Value": true
}
]
}
]
}
}
},
"Outputs": {
"ELBDNSName": {
"Description": "DNS entry point to the stack (all ELBs)",
"Value": {
"Fn::GetAtt": ["ExampleELB", "DNSName"]
}
}
}
}