WordPress / 架站教學五:設定 SSL 加密連線,讓網站連線更安全 (以Apache為例)

WordPress / 架站教學五:設定 SSL 加密連線,讓網站連線更安全 (以Apache為例)

前面有提到關於 WordPress 網站連線有兩件重要的事,一是 domain name 網址的導引,這在前一篇已經談過。本篇要來談另外一個重要的事情,那就是網站的安全性。

SSL 憑證與 https 加密連線

wp-tutorial-5-0-tls-ssl-handshake
What is a SSL/TLS handshake? | Cloudflare

早期的 http 連線是瀏覽器存取網站的通訊協定,最早期是在 1898 年由歐洲核子研究組織 (CERN) 所提出,在當時這樣的通訊協定已經可以滿足通訊的需要。http 它是一個沒有經過加密的明文通訊傳輸,隨著網路的發展以及駭客們的鼓勵 ?! 安全性的問題日漸被重視。

明文通訊的資安風險非常的高,因為你無法得知和你通訊的人 (伺服器) 是不是你想像中應該的那個人?而伺服器也無法得知目前的 client 端是否是可以信任的那個人?就算雙方身份都正確,亦無法確認你們兩個之間的通訊是否有被擷取、竄改、攔截。所以 https 就這麼誕生啦~

https 的全文是 HyperText Transfer Protocol Secure,安全超文本通訊協定。和原始的 http 不同的地方是,它透過雙方的認證 (handshake,交握) 過程,確認彼此的身份,並藉由 SSL 或 TSL 憑證加解密通訊內容。

所以簡單來說,申請了一份 SSL 憑證就能讓伺服器以 https 的方式通訊,讓網站的連線更加安全。

SSL 連線的基本流程概述

在建立 SSL 連線前,首先要先知道一個重要的角色以及幾個檔案:

  1. CA (Certificate Authority,憑證簽發機構)
  2. CRT 憑證 (也就是 Public key,公鑰)
  3. Private key (密鑰)
  4. CSR (Certificate Signing Request,憑證簽署請求)
  5. 中繼憑證 (PEM or CRT)

首先,伺服器在建立 SSL 連線前會先自己生成一個 private key 密鑰,並且對應這個密鑰生成一個 CSR 憑證簽署請求。密鑰是自己保管著,不會洩漏給任何人看,包括 CA 也不會拿到我們的密鑰。

此時我們將 CSR 送給 CA 請求發一個憑證給你,CA 就會回傳一個 CRT 憑證,也就是俗稱的 public key 公鑰。 (當你拿到 CRT 之後,CSR 就沒有用處了) 而這個公鑰與密鑰就是加密與解密的金鑰。資料透過公鑰加密、透過密鑰解密,這是一個不可逆的過程。也就是說我無法把密文再透過公鑰反向解譯出來,除非我是密鑰的擁有者才有辦法解譯密文。這也是為什麼密鑰這麼重要的原因,所以請好好的保管。

CA 作為一個公正的憑證簽發機構,他有責任辨識憑證的真偽。當未來有人向我們的伺服器請求連線時,伺服器便會將 CRT 憑證傳給對方,對方便會向 CA 確認我們是否就是憑證的真正擁有者。確認之後,雙方就會議定用否種方式作加密通訊,之後就可以安全的通訊了~這就是 https 大略的連線機制。

▍ 整個 https 連線的流程大略如下圖:

  1. 憑證簽發 (伺服器 ⇄ CA):
    伺服器產生 Private key、CSR → 將 CSR 送給 CA 請求簽發憑證 → CA 簽發 CRT (public key)
  2. 連線請求 (伺服器 ⇄ 用戶端):
    用戶端請求連線 →  伺服器回傳 CRT → 用戶端請求 CA 確認憑證真偽 → 雙方議定連線加密方式與身份認證 → 加密連線

關於 SSL、TSL 或是 https 的發展歷史和詳細的通訊協定我們就暫時在這邊打住,以免篇幅不小心被拉得太大,有興趣的朋友可以參考本文最後面的參考資料。

幫 WordPress 申請 SSL 憑證並設定加密連線

從上面的說明大概可以知道,設定 SSL 連線大致上分成兩個步驟:申請憑證伺服器參數設定

憑證的簽發單位 (CA) 有非常多個,憑證也分為很多種等級,費用、服務內容也不盡相同。這裡我們使用的是免費的 Let’s Encrypt。它比較特別的是不是透過網站上傳下載的方式取得憑證,而是必須透過伺服器的指令申請。如果你點過去官方網站看,裡面有很詳盡的說明文件。當然你也可以透過其他第三方的機構代為申請,例如 SSL for free。不過以我的習慣,安全性的東西盡量不假手他人會是比較好的。再來因為 Let’s Encrypt 的憑證只有 90 天的效期,總不會每 3 個月就要手動更新一次憑證也太累了吧!所以本篇教學文是以指令的方式完成憑證申請、安裝與自動更新。

SSL 憑證申請

Let’s Encrypt 的官方網站推薦使用 Certbot 申請、安裝憑證。Certbot 的確好用,不過就如我們之前所提,Bitnami 已經對系統進行了修改及優化設定, Certbot 沒辦法正常的幫我們自動建立好 SSL 連線。參考了 Bitnami 的說明文件,我們執行以下指令:

▍安裝 Lego Client

cd /tmp
curl -Ls https://api.github.com/repos/xenolf/lego/releases/latest | grep browser_download_url | grep linux_amd64 | cut -d '"' -f 4 | wget -i -

下載 Lego 安裝檔,放在 /tmp 當中。
並以 ls 指令確認下載回來的版本,像我下載的是 lego_v3.0.2_linux_amd64.tar.gz。

tar xf lego_vX.Y.Z_linux_amd64.tar.gz
sudo mkdir -p /opt/bitnami/letsencrypt
sudo mv lego /opt/bitnami/letsencrypt/lego

記得把 X.Y.Z 換成你所下載的版本、解壓縮,建立資料夾並將 Lego 移過去。

▍停用 Apache server

sudo /opt/bitnami/ctlscript.sh stop apache

▍產生密鑰並向 Let’s Encrypt 申請憑證

sudo /opt/bitnami/letsencrypt/lego --tls --email="EMAIL-ADDRESS" --domains="DOMAIN" --domains="www.DOMAIN" --path="/opt/bitnami/letsencrypt" run

EMAIL-ADDRESS 改成你的 email。將 DOMAIN 改成你要申請的網址,你可以同時申請一個或多個 domain name,只需加入 --domains="DOMAIN" 即可,如上例就是註冊了 DOMAIN 及 www.DOMAIN 兩的網址。如果同時註冊多個 domain,Lego 會產生 SAN (Subject Alternate Names) 多網域憑證,也就是一張憑證內含多個網域名稱,多網域憑證也方便我們同時管理多個網址。

在憑證當中,第一個 domain 會被放在 CommonName 的欄位,而剩下的會被寫入 DNSNames 欄位。 不過不重要,關於憑證的種類及細節,也可以參考文末的參考資料,總之把想要加密的網址通通加進去就對了!

執行指令 → 同意使用規章 (Y),憑證就會自動生成啦!
生成的憑證,預設會儲存在這個位置:

/opt/bitnami/letsencrypt/certificates

裡面包含了四個檔案,檔名都是你的第一個 domain name:

  1. CRT 憑證,也就是 public key: DOMAIN.com.crt
  2. Private key: DOMAIN.com.key
  3. 以及另外兩個憑證簽發相關的金鑰與資訊:DOMAIN.com.issuer.crt、DOMAIN.com.json

前兩個是設定 SSL 連線參數最重要的檔案,Private key 要好好保管千萬不要傳給任何人或存在任何雲端儲存空間上。

wp-tutorial-5-1-generate-ssl-certificate

Apache server 的 SSL 連線參數設定

接著我們要開啟 SSL 加密連線,並告訴 Apache server 新的憑證位置。方法有兩種,基本上是差不多的意思,擇一即可:

▍方法一:將預設的憑證位置重新指向至新的憑證 (參考 Bitnami 官方文件)

由於 Bitnami 原本就有設定好 SSL 憑證的路徑:

/opt/bitnami/apache2/conf/server.crt 
/opt/bitnami/apache2/conf/server.key 

不過目前這兩個都是假的憑證,裡面的網址是 example.com 顯然不是我們要的。為了方便管理以及後續更新憑證,我們就讓 Lego 生成的憑證待在原本的位置不要動。讓上面這兩個檔案 (server.crt, server.key) 重新指向 (link) 到真正的憑證即可。也就是說,當 Apache server 在找這兩個憑證和密鑰時,會被重新連結到我們所設定的位置,有點像是 windows 捷徑的概念。

執行下列指令,記得把紅字換成你的 domain name (或檔名)

sudo mv /opt/bitnami/apache2/conf/server.crt /opt/bitnami/apache2/conf/server.crt.old
sudo mv /opt/bitnami/apache2/conf/server.key /opt/bitnami/apache2/conf/server.key.old
sudo mv /opt/bitnami/apache2/conf/server.csr /opt/bitnami/apache2/conf/server.csr.old
sudo ln -sf /opt/bitnami/letsencrypt/certificates/DOMAIN.key /opt/bitnami/apache2/conf/server.key
sudo ln -sf /opt/bitnami/letsencrypt/certificates/DOMAIN.crt /opt/bitnami/apache2/conf/server.crt
sudo chown root:root /opt/bitnami/apache2/conf/server*
sudo chmod 600 /opt/bitnami/apache2/conf/server*

這幾個指令的意義分別是:

  • 將舊的 SSL 憑證檔案改名成 .old 結尾作保留
  • 建立兩個鏈結 (link) 從預設的位置指向 Lego 生成的位置
  • 設定憑證相關檔案的權限,只有 root user 可以存取

如果你用的是 Nginx 的伺服器,可以參考 Bitnami 的說明文件:Generate And Install A Let’s Encrypt SSL Certificate For A Bitnami Application

▍方法二:直接設定 Virtual Host 參數

方法一依然保留了 Bitnami 一貫的風格,把整個 Apache server 的參數疊床架屋,這個疊那個、再 include 另一個,還要連結到這邊那邊。不過在系統管理的層面上的確有他的好處啦,有點類似模組化的概念。

而方法二則是直接告訴 Apache server SSL 憑證的真正位置在哪裡,不必再用鏈結重新導向。首先開啟 WordPress 的 Virtual Host 參數檔:

sudo nano /opt/bitnami/apps/wordpress/conf/httpd-vhosts.conf

直接修改以下參數啟動 SSL 連線,並將 SSL 憑證位置修改到任何你想要的地方:

SSLEngine on
SSLCertificateFile "/opt/bitnami/apps/letsencrypt/certificates/test.charlestw.com.crt"
SSLCertificateKeyFile "/opt/bitnami/apps/letsencrypt/certificates/test.charlestw.com.key"
wp-tutorial-5-2-virtual-host-conf

啟用並強制使用 SSL 連線

▍開啟 SSL 加密連線

還記得我們先前有把 SSL 連線關閉嗎?可以檢查一下 Apache 和 WordPress 的參數檔,將 SSLEngine 設定為 on,也把 SSLCertificateFileSSLCertificateKeyFile 的註解拿掉 (啟用它)。

  • Apache 參數:
sudo nano /opt/bitnami/apache2/conf/bitnami/bitnami.conf
  • WordPress 參數:
sudo nano /opt/bitnami/wordpress/conf/httpd-vhosts.conf

▍強制使用 https 連線

為了強制使用 SSL 連線,也必須把 http 連線強制轉址到 https。同樣檢查 Apache 和 WordPress 的參數檔,在 port 80 裡面加入下列 redirect 參數:

  RewriteEngine On
  RewriteCond %{HTTPS} !=on
  RewriteCond %{HTTP_HOST} !^(localhost|127.0.0.1)
  RewriteCond %{REQUEST_URI} !^/\.well-known
  RewriteRule ^/(.*) https://%{SERVER_NAME}/$1 [R,L]

以後只要有人使用 http 連線過來,就會被強制轉址 (redirect) 到 https 的網址去囉!

wp-tutorial-5-3-redirect

▍最後記得要重新啟動 Aapche server

sudo /opt/bitnami/ctlscript.sh start apache

自動更新憑證設定,使用 crontab 排程

Let’s Encrypt 的憑證只有 90 天的效期,但每三個月都要重新申請一次憑證也真的是挺累人的。好在我們可以利用 Linux 本身的自動排程功能幫我們定期的更新憑證,把更新憑證的指令都寫在一個 script 當中,定期的執行一次這個腳本。

▍建立更新憑證的腳本

假設我們要把腳本存放在 /opt/bitnami/letsencrypt/scripts/renew-certificate.sh

sudo nano /opt/bitnami/letsencrypt/scripts/renew-certificate.sh

使用 nano 編輯器,因為檔案本身不存在,所以建立了一個新的檔案名為 renew-certificate.sh。

並且將更新憑證的指令依照步驟放進腳本中:

#!/bin/bash

sudo /opt/bitnami/ctlscript.sh stop apache
sudo /opt/bitnami/letsencrypt/lego --tls --email="EMAIL-ADDRESS" --domains="DOMAIN" --path="/opt/bitnami/letsencrypt" renew --days 90
sudo /opt/bitnami/ctlscript.sh start apache

同樣的,記得要把 email 和 domain name 換成你自己的,如果有多個 domain 一樣自行加入 --domains="alternative.domain.com" 參數即可。

wp-tutorial-5-3-crontab

▍將腳本權限設定為可執行

因為腳本必須被執行,所以必須開啟可被執行的權限:

chmod +x /opt/bitnami/letsencrypt/scripts/renew-certificate.sh

▍設定 Crontab 自動排程

Crontab 可以設定 Linux 的自動排程,定期的執行指令。首先開啟 Crontab:

sudo crontab -e

在 Crontab 中加入這一行指令:

0 0 1 * * /opt/bitnami/letsencrypt/scripts/renew-certificate.sh 1>> /opt/bitnami/letsencrypt/logs/renew-log 2>> /opt/bitnami/letsencrypt/logs/renew-error

儲存檔案,憑證更新就會自動加入例行性排成當中啦!

稍微解說一下 Crontab 的參數,主要分成兩個部分:前半段的週期設定,與後面執行的指令。

前面五個數字分別代表分、時、日、月、星期,只要符合上述的時間就會執行後面的指令,* 字號代表不拘。以我們上面的參數為例, 0 0 1 * * 表示只要在每月 1 日的 00 時 00 分 (月份及星期不拘) 就會執行後面那一串指令。所以我們的憑證是每個月的 1 日檢查一次。

後半段的指令當然可以是任一指令。上面這個指令執行了我們建立的腳本 renew-certificate.sh,並且把執行結果輸出到 renew-log 這個檔案、錯誤訊息輸出到 renew-error 這個檔案,之後可以在這裡查詢憑證更新的紀錄。

這裡就不說太多關於 Crontab 的細節,其他可以參考文末的參考資料。

FTP 同樣也應該申請一個 SSL 憑證 (補充)

還記得久遠以前我們架的 FTP server 嗎?忘記可以回頭參考 WordPress / 架站教學二:透過 SSH 連線遠端控制 GCP 並設定 FTP 伺服器 (使用 WordPress Bitnami)。因為基本的 FTP 是以明文傳輸的,所以當時我們有設定 SSL 連線,但那張憑證是我們自己簽發給自己的憑證 (self-signed SSL certificate),目的只是為了加密 FTP 的傳輸,不然所有檔案和指令都透過明文傳輸真的是非常可怕。可以想像任何人都可以隨意的截取你的 Facebook 、Line 的訊息內容嗎?

不過由於是自己簽發的憑證,也就是自己就是自己的 CA (Certificate Authority),前面提過 CA 是一個具有公信力的單位負責憑證的簽發與真偽辨認,不是隨便的阿貓阿狗都能成為一個 “可被信任的 CA”。通常瀏覽器或 FTP 連線軟體都會有一個 CA 的名單,只有列在上面的 CA 所發行的憑證才會被認定是一個安全的憑證。所以,使用我們自己發行給自己的憑證當然會被判定是一個不安全的 https 連線。

如果你先前嘗試過以 FTP 連線到伺服器上,應該會看到一個警告和你說無法確認這個 SSL 憑證的發行 CA 並不在列表上,有可能會是不安全的連線,確定還要繼續嗎?所以這裡我們同樣可以幫 FTP 設定好真正的 SSL 憑證。

幫 FTP server 申請憑證

其實這一部分算是補充說明,因為要幫 FTP server 申請憑證應該在先前的指令當中多加一個給 FTP 的 domain 就行了~ 不過剛剛沒申請沒關係,現在再申請一張新的憑證就好了。

sudo /opt/bitnami/letsencrypt/lego --tls --email="EMAIL-ADDRESS" --domains="DOMAIN" --domains="www.DOMAIN" --domains="ftp.DOMAIN" --path="/opt/bitnami/letsencrypt" run

記得連 Crontab 裡面的指令也要加入喔!

sudo /opt/bitnami/letsencrypt/lego --tls --email="EMAIL-ADDRESS" --domains="DOMAIN" --domains="www.DOMAIN" --domains="ftp.DOMAIN" --path="/opt/bitnami/letsencrypt" renew --days 90

設定 FTP server 參數

▍修改 vsftpd.conf 重新指定新的憑證

開啟 vsftpd.conf:

sudo nano /etc/vsftpd.conf

修改 SSL 連線參數:

rsa_cert_file=/opt/bitnami/apache2/conf/server.crt
rsa_private_key_file=/opt/bitnami/apache2/conf/server.key
ssl_enable=YES

allow_anon_ssl=NO

force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO

require_ssl_reuse=NO
ssl_ciphers=HIGH

紅字的路徑就是憑證和金鑰的路徑,主要參數是啟用 SSL 連線、強制使用者以 SSL 登入。

修改後存檔並重啟 FTP server:

sudo systemctl restart vsftpd

到這邊整個網站的連線都經過 SSL 的加密連線啦!

 

下一篇:WordPress / 架站教學六:檔案及目錄的結構簡介與預設檔案權限

 

WordPress 網站架設與搬家教學系列

WordPress 快速架站教學 (濃縮版):
在 Google Cloud (GCP) 台灣主機上免費架設 WordPress Bitnami

WP 架站教學一:使用 Google 台灣主機免費架設自己的網站 (Mac/Win版)
WP 架站教學二:透過 SSH 連線遠端控制 GCP 並設定 FTP 伺服器 (使用 WordPress Bitnami)
WP 架站教學三:更改 MySQL Administrator 密碼、設定外部靜態 IP 位址
WP 架站教學四:設定 Domain Name 及 Apache 參數
WP 架站教學五:設定 SSL 加密連線,讓網站連線更安全 (以Apache為例)
WP 架站教學六:WordPress 檔案及目錄的結構簡介與預設檔案權限
WP 架站教學七:修改網站外觀樣式,如何建立子主題 (child theme)
WP 多重架站一:如何在同一個伺服器架設第二個 WordPress (Multiple WordPress)
WP 多重架站二:設定 Domain Name,指定多個 WordPress 的網址 (使用subdomain)
WP 多重架站三:設定 SSL 加密連線 (以 Apache 為例)
WP 搬家教學:一個按鍵幫網站搬家,使用 All-in-One Migration

WordPress / 網站架設與搬家教學系列簡介及目錄
https://note.charlestw.com/wordpress-tutorial-category/

Copyright announcement:
the featured image: Photo by Amanda Vick on Unsplash