
在去年(2016年)的12月底, Google公布了"From the end of January with Chrome 56, Chrome will mark HTTP sites that collect passwords or credit cards as non-secure."訊息, Google Chrome瀏覽器第56版後將在2017年1月把僅使用HTTP連線的個人隱私相關網頁標示為"不安全", 而網站的全面HTTPS化更是趨勢且具有許多優勢, 加上HTTP/2加速需要使用HTTPS, 所以HTTPS強化網站是刻不容緩之事, 但是對於個人網站呢? 購買SSL/TLS憑證費用還相當昂貴, 為了推廣網站的HTTPS化, 由公益組織"網際網路安全研究小組"發起"Let's Encrypt"服務網站, 可向該網站申請網站免費SSL/TLS憑證。

Let's Encrypt有許多贊助商, 包含了Mozilla基金會, Akamai與Facebook...等多家廠商, Let's Encrypt提供了自動化申請憑證服務, 並且讓網站能在三個月有效期到期之前更新。
前期準備
為了實際運作一個HTTPS網站, 我準備了:
1. 一個有效的網域名稱, 我在Gandi域名註冊商申請了"ryanlai.com.tw"網域名稱, 正好年費僅94元台幣促銷".com.tw"網域, 非常划算!
2. 一個運作網站, 使用Google Cloud Platform服務申請了一台最小規格的虛擬機, 租金約每月5美元。
3. 簡單的一頁式網頁, 測試HTTPS網站架設即可。
4. Let's Encrypt免費憑證申請, 採用Gea-Suan Lin所提供的Let's Encrypt使用教學
當DNS設定好我的網站域名ryanlai.com.tw與www.ryanlai.com.tw到Google Cloud Platform虛擬主機對外IP位址後, 等全面生效約需兩天, 不過我測試時大約兩三個小時就已經差不多生效, 有些作業可以在進行Let's Encrypt免費憑證申請前先做好。
架設網站
這裡我仍採用Nginx網站伺服器, 使用Debian Linux 8為作業系統, 由於僅為測試申請安裝SSL/TLS憑證, 故只使用簡單的HTML網頁, 沒有安裝其他Server side script與資料庫。
我使用NGINX官方網站提供的教學, 用Debian Linux 8的Pre-Built Packages
取得Nginx的singing key:
wget https://nginx.org/keys/nginx_signing.key
加入Nginx
apt-key add nginx_signing.key
修改sources list
nano /etc/apt/sources.list
將Nginx加入sources list並且儲存
deb http://nginx.org/packages/mainline/debian/ jessie nginx
deb-src http://nginx.org/packages/mainline/debian/ jessie nginx
更新sources list與系統
apt-get update
apt-get upgrade
安裝Nginx網站伺服器
apt-get install nginx
預設的網站根目錄是在/usr/share/nginx/html路徑, 將網頁複製到該目錄
cp index.html /usr/share/nginx/html
因為我只有一個HTML檔, 所以很簡單, 或是修改/etc/nginx/conf.d/default.conf把
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
中的root設定值改為你的網站目錄。
這時候網站已經運作了, 可直接在瀏覽器使用http://www.ryanlai.com.tw網址確認網站是否正常顯示。
申請SSL/TLS憑證
我們參考https://letsencrypt.tw的教學, 申請ryanlai.com.tw的SSL/TLS憑證。
Gea-Suan Lin採用了非Let's Encrypt官方的certbot, 而是dehydrated, 個人也是認為dehydrated使用起來簡單許多。
下載最新的dehydrated
curl -LO https://raw.githubusercontent.com/lukas2511/dehydrated/master/dehydrated
依照教學將dehydrated複製到/etc/dehydrated/目錄, 並將dehydrated這個shell script改為可執行
mkdir /etc/dehydrated/
cp dehydrated /etc/dehydrated/
chmod a+x /etc/dehydrated/dehydrated
建立SSL/TLS驗證過程時所需要的目錄
mkdir -p /var/www/dehydrated/
修改Nginx, 將驗證目錄加入
nano /etc/nginx/conf.d/default.conf
加入設定到目前的server區段
location /.well-known/acme-challenge/ {
alias /var/www/dehydrated/;
}
重新啟動Nginx使新設定生效
service nginx reload
向Let's Encrypt建立帳號
/etc/dehydrated/dehydrated --register --accept-terms
申請SSL/TLS驗證
/etc/dehydrated/dehydrated -c -d ryanlai.com.tw -d www.ryanlai.com.tw
因為我想把用戶存取ryanlai.com.tw都轉到www.ryanlai.com.tw, 所以兩個域名都申請
憑證申請完成, 可在/etc/dehydrated/certs/ryanlai.com.tw/目錄看到網站的憑證檔案
ls -l /etc/dehydrated/certs/ryanlai.com.tw/
有數個檔案, 給Apache與Nginx網站伺服器用的, 我們只需要Nginx的fullchain.pem與privkey.pem。
HTTPS修改與上線
修改Nginx, 增加監聽Port 443
nano /etc/nginx/conf.d/default.conf
以下是我的設定值
ssl_certificate /etc/dehydrated/certs/ryanlai.com.tw/fullchain.pem;
ssl_certificate_key /etc/dehydrated/certs/ryanlai.com.tw/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
server {
listen 80;
listen 443 ssl http2;
server_name "~^(?!www\.).*";
return 301 https://www.ryanlai.com.tw$request_uri;
}
server {
listen 443 ssl http2;
server_name www.ryanlai.com.tw;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /.well-known/acme-challenge/ {
alias /var/www/dehydrated/;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
第一個server設定區段(server_name "~^(?!www\.).*";), 監聽了Port 80(HTTP)與443(HTTPS), 將存取轉移到第二個server區段(server_name www.ryanlai.com.tw), 這樣存取HTTP或HTTPS的ryanlai.com.tw都會以HTTP 301 Moved Permanently轉到www.ryanlai.com.tw。

使用瀏覽器檢視網站https://www.ryanlai.com.tw, 確認運作是否正常。
憑證自動更新
由於Let's Encrypt發的憑證只有三個月有效期, 所以需要自動化更新, Gea-Suan Lin的教學網站也提供了自動化更新方法, 各位可以參考他的作法更新憑證: https://letsencrypt.tw。
Let's Encrypt免費憑證的缺點
其實上面提到, Let's Encrypt只給三個月有效期, 用在低流量可容許短暫的網站重啟時間是很合適又省錢, 若是你的網站loading很高, 憑證放在loading balance伺服器(例如用Nginx作reverse proxy)上同樣有重啟需要, 那麼買個長期SSL/TLS憑證比較恰當。
補充: Nginx HTTPS強化
參考https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html的資訊, 對Nginx網站伺服器HTTPS的強化。
產生Diffie-Hellman金鑰檔, 這裡若用4096-bit會花很多時間, 先用2048-bit
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
再次修改Nginx網站設定檔
nano /etc/nginx/conf.d/default.conf
新的設定值
ssl_certificate /etc/dehydrated/certs/ryanlai.com.tw/fullchain.pem;
ssl_certificate_key /etc/dehydrated/certs/ryanlai.com.tw/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
server {
listen 80;
listen 443 ssl http2;
server_name "~^(?!www\.).*";
return 301 https://www.ryanlai.com.tw$request_uri;
}
server {
listen 443 ssl http2;
server_name www.ryanlai.com.tw;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /.well-known/acme-challenge/ {
alias /var/www/dehydrated/;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
最後請記得重啟Nginx
service nginx reload
Qualys SSL Server Test
Qualys SSL Lab提供HTTPS網站的測試, 可以知道哪裡不足, 在經過HTTPS的強化設定重啟之後測試, 可以達到Overall A+的評分

Qualys SSL Lab的測試服務網址是: https://www.ssllabs.com/ssltest/index.html
4/24 更新
Qualys SSL Lab A+與各項100分
參考Michael Lustfield的文章Getting a Perfect SSL Labs Score: https://michael.lustfield.net/nginx/getting-a-perfect-ssl-labs-score, 取得Qualys SSL Lab A+與各項100分
將dhparam.pem改為4096 bit
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
這會花上許多時間, 可讓它背景執行方法, 你也能結束SSH連線先做其他事情, 再回來處理
nohup openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096 > /dev/null &
請擇一執行即可
修改Nginx網站設定檔
nano /etc/nginx/conf.d/default.conf
修改項目:
1. 只支援TLS 1.2
2. 高強度SSL Cipher
3. Gzip壓縮關閉
ssl_certificate /etc/dehydrated/certs/ryanlai.com.tw/fullchain.pem;
ssl_certificate_key /etc/dehydrated/certs/ryanlai.com.tw/privkey.pem;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "AES256+EECDH:AES256+EDH:!aNULL";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
server {
listen 80;
listen 443 ssl http2;
gzip off;
server_name "~^(?!www\.).*";
return 301 https://www.ryanlai.com.tw$request_uri;
}
server {
listen 443 ssl http2;
server_name www.ryanlai.com.tw;
gzip off;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /.well-known/acme-challenge/ {
alias /var/www/dehydrated/;
}
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
重啟Nginx
service nginx reload
再次測試的結果:

有幾個要注意的設定:
add_header X-Frame-Options DENY;
這會使HTML iframe標籤無效, 如果真的需要iframe, 可改白名單方式處理: https://developer.mozilla.org/zh-TW/docs/Web/HTTP/X-Frame-Options