隨著網際網路快速發展,網際網路資訊保安越來越受到大家重視,HTTPS
應該是近兩年各大廠商都在盡力普及的技術之一。國內大廠基本上已經全面普及了 HTTPS。
本文首發於我的個人網站:聽說 – https://tasaid.com/,建議在我的個人網站閱讀,擁有更好的閱讀體驗。
前端開發 QQ 群:377786580
HTTPS 現狀
早在 2016 年底,我就寫過 《 從 HTTP 到 HTTPS 系列 》文章來講解 HTTPS
。當時結合本站的部署經驗,給大家詳細介紹了 《 IIS 部署免費 HTTPS 》。
這篇文章就跟大家介紹一下 Node.js
如何部署免費 HTTPS
以及簡單的部署 HTTP/2
。
截止 2018 年 03 月 13 日,由 Let`s Encrypt 實時統計報告 顯示,在統計的 6930 多萬活躍網站中,已經有 5350 萬(約 77%)的站點部署了 HTTPS
證書服務。
同時 Google 透明度報告 – 網路上的 HTTPS 加密 中,統計了使用 Chrome 瀏覽器,訪問的站點統計中,HTTPS 使用率的增長情況:
而在今年 2 月份,Chrome 團隊也宣佈,將在 2018 年 7 月份釋出的 Chrome 68 中,將沒有部署 HTTPS 的網站標記為 “不安全”。
簡而言之,HTTPS 大勢所趨。
Node.js 部署 HTTPS
早在 《 從 HTTP 到 HTTPS – IIS 部署免費 HTTPS 》一文中,我就指出了 Let`s Encrypt 免費證書的優勢:
由 ISRG(Internet Security Research Group,網際網路安全研究小組)提供服務,免費、訪問速度快,穩定等。
所以這次部署的證書也是圍繞 Let`s Encrypt
展開。
greenlock-express
由於 js 生態圈的繁華,所以想找一個現有的包是件很輕鬆的事情,greenlock-express 這個包就幫助我們封裝了 Let`s Enctrypt
證書的部署,只需要引入這個包並使用,就可以:
- 自動註冊
Let`s Encrypt
證書 - 自動續訂( 80 天左右),且伺服器無需重啟
- 支援虛擬主機
並且 greenlock
相關的證書生態圈十分完善,同樣有支援 koa
的 greenlock-koa。
安裝和使用
通過 npm
安裝 greenlock-express
:
$ npm install --save greenlock-express@2.x
使用起來非常簡單,這是 greenlock-express
預設提供的 demo:
const greenlock = require(`greenlock-express`)
require(`greenlock-express`).create({
// 測試
server: `staging`,
// 聯絡郵箱
email: `john.doe@example.com`,
// 是否同意 Let`s Encrypt 條款... 這必須為 true 啊,不然走不下去
agreeTos: true,
// 申請的域名列表,不支援萬用字元
approveDomains: [ `tasaid.com`, `www.tasaid.com` ],
// 繫結 express app
app: require(`express`)().use(`/`, function (req, res) {
res.end(`Hello, World!`);
})
}).listen(80, 443)
證書存在 ~/letsencrypt
。
當然上面程式碼只能用於測試/開發環境,因為它並沒有申請一個有效的證書,而是生成了一個自簽名的證書(跟以前的 12306 自簽證書一樣),用於在開發環境中除錯。
API
greenlock-express
的 create(options)
函式引數簽名如下:
interface Options {
/**
* Express app
*/
app: Express
/*
* 遠端伺服器
* 測試環境中可用為 staging
* 生產環境中為 https://acme-v01.api.letsencrypt.org/directory
*/
server: string
/**
* 用於接收 let`s encrypt 協議的郵箱
*/
email: string
/**
* 是否同意協議
*/
agreeTos: boolean
/**
* 在註冊域名獲取證書前,會執行這個回撥函式
* string[]: 一組需要註冊證書的域名
* 函式: 第一個引數跟 Options 格式差不多,第二個引數是當前自動獲取的域名資訊,第三個引數是在處理完之後傳遞的回撥函式
*/
approveDomains: string[] | (opts, certs: cb) => any
/**
* 更新證書最大天數 (以毫秒為單位)
*/
renewWithin: number
/**
* 更新證書的最小天數(以毫秒為單位)
*/
renewBy: number
}
經過測試,在真實的生產環境中, approveDomains
必須為函式,傳陣列的話不會生效。
生產環境
生產環境中部署還需要做一些配置改動和引入一些包。
更新包:
$ npm i --save greenlock-express@2.x
$ npm i --save le-challenge-fs
$ npm i --save le-store-certbot
$ npm i --save redirect-https
生產程式碼:
const greenlock = require(`greenlock-express`)
const express = require(`express`)
const app = express()
const lex = greenlock.create({
// 注意這裡要成這個固定地址
server: `https://acme-v01.api.letsencrypt.org/directory`,
challenges: {
`http-01`: require(`le-challenge-fs`).create({ webrootPath: `~/letsencrypt/var/acme-challenges` })
},
store: require(`le-store-certbot`).create({
webrootPath: `~/letsencrypt/srv/www/:hostname/.well-known/acme-challenge`
}),
approveDomains: (opts: any, certs: any, cb: any) => {
appLog.info(`approveDomains`, { opts, certs })
if (certs) {
/*
* 注意這裡如果是這樣寫的話,一定要對域名做校驗
* 否則其他人可以通過將域名指向你的伺服器地址,導致你註冊了其他域名的證書
* 從而造成安全性問題
*/
// opts.domains = certs.altnames
opts.domains = [ `tasaid.com`, `www.tasaid.com` ]
} else {
opts.email = `你的郵箱@live.com`
opts.agreeTos = true
}
cb(null, { options: opts, certs: certs })
},
})
// 這裡的 redirect-https 用於自動將 HTTP 請求跳到 HTTPS 上
require(`http`).createServer(
lex.middleware(
require(`redirect-https`)()
)
).listen(80, function () {
console.log(`Listening`, `for ACME http-01 challenges on: ${JSON.stringify(this.address())}`)
})
// 繫結 HTTPS 埠
require(`https`).createServer(
lex.httpsOptions,
lex.middleware(app)
).listen(443, function () {
console.log((`App is running at http://localhost:%d in %s mode`), app.get(`port`), app.get(`env`))
console.log(`Press CTRL-C to stop
`)
})
如果沒有生效,可以檢查下 ~/letsencrypt
的證書資訊,和 443 埠是否開啟。
部署 HTTP/2
HTTP/2 是 HTTP/1.1 的升級版,主要來說改進了這些地方:
- 二進位制協議:採用二進位制流
- 多路複用:一次請求多次複用管道
- 伺服器推送:解決 HTTP/1.x 時代最大的痛點
值的注意的是,HTTP/2 是支援 HTTP 協議的,只不過瀏覽器廠商都不願意支援 HTTP,所以基本上可以認為,用上 HTTP/2 的前置條件是必須部署 HTTPS。
SPDY
早在 2009 年,Google 開發了一個實驗性協議,叫做 SPDY,目的解決 HTTP/1.x 中的一些設計缺陷。在 SPDY 釋出幾年後,這個新的實驗性協議得到了 Chrome、Firefox 和 Opera 的支援,應用越來越廣泛。然後 HTTP 工作組 (HTTP-WG) 在這個 SPDY 的基礎上,設計了 HTTP/2,所以可以說 SPDY 是 HTTP/2 的前身。
關於 HTTP/2 的詳情可以參考 這篇文章。
部署 HTTP/2
引入 HTTP/2
在 Node.js
中也十分簡單,只需要引入 spdy
包即可:
$ npm i --save spdy
然後我們把上一節的程式碼做一點修改即可支援 HTTP/2:
const greenlock = require(`greenlock-express`)
const express = require(`express`)
// HTTP/2
const spdy = require(`spdy`)
const app = express()
const lex = greenlock.create({
// 注意這裡要成這個固定地址
server: `https://acme-v01.api.letsencrypt.org/directory`,
challenges: {
`http-01`: require(`le-challenge-fs`).create({ webrootPath: `~/letsencrypt/var/acme-challenges` })
},
store: require(`le-store-certbot`).create({
webrootPath: `~/letsencrypt/srv/www/:hostname/.well-known/acme-challenge`
}),
approveDomains: (opts: any, certs: any, cb: any) => {
appLog.info(`approveDomains`, { opts, certs })
if (certs) {
/*
* 注意這裡如果是這樣寫的話,一定要對域名做校驗
* 否則其他人可以通過將域名指向你的伺服器地址,導致你註冊了其他域名的證書
* 從而造成安全性問題
*/
// opts.domains = certs.altnames
opts.domains = [ `tasaid.com`, `www.tasaid.com` ]
} else {
opts.email = `你的郵箱@live.com`
opts.agreeTos = true
}
cb(null, { options: opts, certs: certs })
},
})
// 這裡的 redirect-https 用於自動將 HTTP 請求跳到 HTTPS 上
require(`http`).createServer(
lex.middleware(
require(`redirect-https`)()
)
).listen(80, function () {
console.log(`Listening`, `for ACME http-01 challenges on: ${JSON.stringify(this.address())}`)
})
// HTTP/2
spdy.createServer(lex.httpsOptions, lex.middleware(app)).listen(443, function () {
console.log(`Listening https`, `for ACME tls-sni-01 challenges and serve app on: ${JSON.stringify(this.address())}`)
})
至於 HTTP/2 相關的技術應用,會在後續篇幅中再為大家講解。
本文首發於我的個人網站:聽說 – https://tasaid.com/,建議在我的個人網站閱讀,擁有更好的閱讀體驗。
前端開發 QQ 群:377786580