這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
為什麼要使用HTTPS?使用HTTPS的途徑有哪些?如何用Go來部署HTTPS?拿出你的小本本,你要的乾貨都在這兒!
HTTPS的好處我們已在之前的文章中提高好多。它加密瀏覽器和伺服器之間的流量,保障你密碼傳輸的安全,讓你的頁面載入速度飛快,有助於網站的SEO最佳化還有對HTTP網站百般嫌棄的瀏覽器廠商......這些都是使用HTTPS的理由。那麼問題來了,怎樣可以又快又好地部署HTTPS呢?
使用第三方提供的HTTPS
比如說CloudFlare,他們的免費方案為你那僅支援HTTP的網站提供仿HTTPS代理服務。
要使用CloudFlare:
配置你的域,使用CloudFlare的DNS伺服器
在CloudFlare的DNS設定中,將域指向你的伺服器,並通將“狀態”設為“DNS和HTTPProxy 伺服器(CDN)”
在CloudFlare的加密設定中,將SSL設為“靈活”(該選項使瀏覽器通過HTTPS與CloudFlare對話,CloudFlare通過HTTP與瀏覽器對話)
在web管理介面配置CloudFlare的HTTPS代理,提供你的伺服器的IP地址。
除此之外,還要啟用“總是使用HTTPS”選項
瀏覽器與CloudFlare對話,CloudFlare負責提供SSL認證,並代理通向你的伺服器的通訊。由於額外的通訊量,這可能會減慢網路速度,也可能由於CloudFlare伺服器比你的伺服器要快(變得更快是他們的工作),所以導致網路速度變快。
AWS、Google雲以及其它一些主機服務提供者也為託管在他們那裡的伺服器提供免費HTTPS服務。
另一個選項是在支援HTTPS的反向 Proxy伺服器(如Caddy)後面運行你的伺服器。
直接支援HTTPS
很久很久以前,想要一張ssl認證就必須每年為一個網域名稱花很多錢。而現在,Let's Encrypt改變了這一切。
這是一個非營利性機構,它提供免費認證,並且提供HTTP API來獲得認證。API允許自動化處理這一過程。
在Let’s Encrypt出現之前,你可能會購買一個認證,而這僅僅是一串位元組而已。你會把認證存放在一個檔案中,並配置你的網路伺服器來使用它。
有了Let’s Encrypt以後,你就能夠使用他們的API來免費獲得認證了,而且這一過程是在你的伺服器啟動後自動完成的。
值得慶幸的是,所有與API進行對話的艱苦工作都已由其他人做完了。我們只需要安裝外掛程式就可以了。
有兩個Go庫可以實現Let’s Encrypt支援。
我一直在使用golang.org/x/crypto/acme/autocert,它是由Go的核心開發人員開發的。
現在已經有幾個月了,它運行一切正常。
下面是怎樣啟動一個使用Let’s Encrypt提供的免費SSL認證的HTTPS網路伺服器的方法。
完整的例子請看:free-ssl-certificates/main.go。
const (
htmlIndex = `Welcome!`
inProduction = true
)
func handleIndex(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, htmlIndex)
}
func makeHTTPServer() *http.Server {
mux := &http.ServeMux{}
mux.HandleFunc("/", handleIndex)
// set timeouts so that a slow or malicious client doesn't
// hold resources forever
return &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 120 * time.Second,
Handler: mux,
}
}
func main() {
var httpsSrv *http.Server
// when testing locally it doesn't make sense to start
// HTTPS server, so only do it in production.
// In real code, I control this with -production cmd-line flag
if inProduction {
// Note: use a sensible value for data directory
// this is where cached certificates are stored
dataDir := "."
hostPolicy := func(ctx context.Context, host string) error {
// Note: change to your real domain
allowedHost := "www.mydomain.com"
if host == allowedHost {
return nil
}
return fmt.Errorf("acme/autocert: only %s host is allowed", allowedHost)
}
httpsSrv = makeHTTPServer()
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: hostPolicy,
Cache: autocert.DirCache(dataDir),
}
httpsSrv.Addr = ":443"
httpsSrv.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
go func() {
err := httpsSrv.ListenAndServeTLS("", "")
if err != nil {
log.Fatalf("httpsSrv.ListendAndServeTLS() failed with %s", err)
}
}()
}
httpSrv := makeHTTPServer()
httpSrv.Addr = ":80"
err := httpSrv.ListenAndServe()
if err != nil {
log.Fatalf("httpSrv.ListenAndServe() failed with %s", err)
}}
需要注意的是:
HTTPS的標準連接埠是443。
你可以只運行HTTP、只運行HTTPS或兩者都運行。
如果伺服器沒有認證,那麼它將會使用HTTP API向Let’s Encrypt伺服器請求認證。
這些請求被限制到每周處理20個,以避免Let’s Encrypt伺服器過載。
因此,在某個地方緩衝認證是非常重要的。在我們的例子中,我們將認證緩衝到磁碟上,使用的是autocert.DirCache命令。
緩衝只是一個介面,所以你可以在一個SQL資料庫或Redis中執行你所儲存的。
你必須正確安裝DNS。
為核實你確實是你所申請認證的域的所有者,Let’s Encrypt伺服器會回叫你的伺服器。
為了正常工作,DNS名必須解析到你的伺服器的IP地址。
這意味著,HTTPS代碼-路徑的本地測試是很難的。我通常不這麼做。
如果你真的想這麼做,你可以使用ngrok來將你的本地連接埠暴露給互連網,設定DNS來將你的網域名稱解析到ngrok建立的公用DNS上,然後等待確認DNS資訊傳給Let’s Encrypt的電腦。
你可能會好奇:這個HostPolicy業務是什嗎?
正如我提到的, Let’s Encrypt限制了認證供應,所以你需要確保伺服器不會向其請求你不關心的域的認證。Autocert的文檔很好地解釋了這一點。
我們的例子假設的是最常見的情況:一個伺服器僅響應一個域。你可以很容易地改變這一點。
在本地測試時,我們不運行HTTPS。
當在你的膝上型電腦上進行本地測試時,運行HTTPS版本是毫無意義的。
你的電腦很可能沒有可見公用IP地址,所以Let’s Encrypt伺服器無法達到你那裡,所以你將不會得到認證。
我們也不能綁定到HTTPS連接埠443(只有根進程可以綁定到1024以下的連接埠)。
在這個例子中,我使用inProduction標記來決定是否應該啟動HTTPS伺服器。
在實際代碼中,我會加入檢查-production命令列標誌的代碼,並使用它。
從HTTP重新導向到HTTPS
如果你能夠使用HTTPS了,那麼提供純HTTP就毫無意義了。
我們可以將所有HTTP請求重新導向到同樣的HTTPS上,以獲得更好的安全性和搜尋引擎最佳化效果。
func makeServerFromMux(mux *http.ServeMux) *http.Server {
// set timeouts so that a slow or malicious client doesn't
// hold resources forever
return &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 120 * time.Second,
Handler: mux,
}
}
func makeHTTPToHTTPSRedirectServer() *http.Server {
handleRedirect := func(w http.ResponseWriter, r *http.Request) {
newURI := "https://" + r.Host + r.URL.String()
http.Redirect(w, r, newURI, http.StatusFound)
}
mux := &http.ServeMux{}
mux.HandleFunc("/", handleRedirect)
return makeServerFromMux(mux)
}
func main() {
httpSrv := makeHTTPToHTTPSRedirectServer()
httpSrv.Addr = ":80"
fmt.Printf("Starting HTTP server on %s\n", httpSrv.Addr)
err := httpSrv.ListenAndServe()
if err != nil {
log.Fatalf("httpSrv.ListenAndServe() failed with %s", err)
}
}
技術部分到此為止了。
免費認證從何而來?
有種意見認為,由於一個設計錯誤,SSL協議不僅進行加密,還會向瀏覽器證明網站的身份。它擔負著責任,使得我們可以追蹤google.com網站的所有者,並且看到該網站的確是由美國Google公司,而不是由莫斯科的駭客伊萬所有。
我們只信任極少數公司(憑證授權單位)會頒發能夠證明網站所有者身份的認證。
當你申請一個認證時,憑證授權單位必須核實你的身份。他們通過查看你的檔案來做這項工作。
核實檔案需要人力。保證認證安全也需要人力。憑證授權單位為頒發認證而收費是合理的。
信任並不是成比例的。瀏覽器和作業系統銷售商可以信任10家公司不會頒發無效認證,但他們不可能信任1000家。
我們不想讓隨便一家公司變成騙子憑證授權單位,然後為駭客伊萬頒發google.com域的認證。
為了得到僅有的幾個理想結果而連續審核心數千家憑證授權單位,將會花費太多的精力。
由少數幾家公司控制市場很可能導致壟斷,這樣,由於缺乏競爭,價格將居高不下。
這正是SSL認證市場當前的行情。你每年只花60美元就可以擁有一台低端伺服器,但一個認證卻比這要昂貴。
這是一個問題,因為SSL認證的成本是所有網站採用加密技術的明顯障礙。
少數幾家公司決定共用他們的資源來解決這一問題,從而更有利於整個互連網。於是他們資助了Let’s Encrypt這樣一家憑證授權單位,然後編製一些必要的軟體並運營著頒發認證的伺服器。
這就是免費認證的來曆。
更多資訊請詳見亞洲誠信官方號:TrustAsia_SSL