SSL證書與Https應用部署小結

爲了提高網站的安全性,一般會在比較敏感的部分頁面採用https傳輸,比如註冊、登錄、控制檯等。像Gmail、網銀等全部採用https傳輸。
https/ssl 主要起到兩個作用:網站認證、內容加密傳輸和數據一致性。經CA簽發的證書才起到認證可信的作用,所有有效證書均可以起到加密傳輸的作用。

瀏覽器與SSL證書
SSL應用部署小結 - hanguokai - 韓國愷的博客
上圖是IE和Chrome上對https的不同表現。
Chrome 網站安全性指示器說明:http://support.google.com/chrome/bin/answer.py?hl=zh&answer=95617

SSL最主要應用是在瀏覽器和Web服務器之間,儘管不限於此。當然,安全本身是重要的內在屬性。但在表面上看,部署SSL 就是爲了讓用戶瀏覽器裏看起來更安全一些,以增加用戶的信任感。所以很多企業更把它當作門面,而簽發機構也爲此賣高價,尤其是國內的價格明顯高於國外的。

實際上SSL證書也可以做客戶端認證,用戶擁有自己特有的證書,用它可以證明自己的身份,當然也就用不着用戶名和密碼了。但這種用的很少,一般web服務器也不支持。

內容加密傳輸更安全,如果只是爲了加密,使用自簽發的證書也可以,但瀏覽器無法驗證證書,所以會給出一個非常嚇人的警告,所以自簽發證書不適合給外人使用,只適合內部使用,把這個證書 加入到自己的信任列表或忽略證書驗證即可,以後就不會繼續攔截了。

證書需要被少數一級或二級 CA 認證纔有效。計算機安全中的信任就是一個信任鏈的關係,信任鏈最頂端的被稱爲根證書。
自簽發的證書在技術上是完全一樣的,僅用於加密傳輸是沒問題的。但是不能被外人信任,所以一般僅用於內部使用。除了自簽發不被信任,如果證書過期、已被吊銷或者非證書所代表的域名也都是不被信任的,導致證書驗證出錯。

用於網站的證書需要被大衆信任,所以不能自簽發的證書,那就申請(購買)一個吧。

申請證書

1.證書類別
按證書包含域名數量分爲:

單域名:只針對這個域名有效,不能用在其它域名下。
多域名:只針對列出的多個域名有效。
通配符域名(wildcard):對任意子域名有小,顯示的是 *.example.com。

注意:SSL所說的單個域名是一個完整的域名,一個子域名就算一個,而非一個頂級域名。
如果網站有很多子域名,只需要申請真正需要的域名證書。

按驗證的類別分:

域名認證(Domain Validation):認證你的域名所有權和網站,申請驗證簡單,幾分鐘即可。
組織機構認證(Organization Validation):認證的域名和公司信息,需要提交公司資料認證。
擴展認證(Extended Validation,簡稱EV):這種證書會在瀏覽器中出現“很明顯”的綠色地址欄,給用戶的可信度最高。有安全評估保證。

個人或小站點可用一類或二類,企業一般用二類認證,少數企業會用到EV認證。

  1. 證書價格
    看了看網上SSL證書的價格,便宜的一般都是10美元左右一個子域名/每年,按不同類別、不同品牌等價格在幾十美元到幾百美元一年。比如能顯示綠色地址欄的EV證書和通配符證書貴一些。國內自己的或代理的,比國外貴不少,動輒幾千元。其實就是由可信源認證了一下,類似於辦證,用起來沒什麼差別,並非越貴越好。

  2. 簽發機構(“賣家”)

SSL應用部署小結 - hanguokai - 韓國愷的博客
國外常見的SSL提供商有:Thawte,Go Daddy,VeriSign,RapidSSL,GeoTrust(QuickSSL),StartSSL,Comodo。
StartSSL、Go Daddy的比較便宜,GeoTrust、Comodo的價格適中,Thawte和VeriSign的價格較貴。

VeriSign現在歸屬賽門鐵克,在國內是由天威誠信代理的。世界真小,天威誠信就在我很多年以前的東家(啓明星辰)大樓裏,地下一層是他們的機房,我還進去過一次。

  1. 免費的StartSSL
    唯一免費的是StartSSL,其它的一般只提供30免費試用。

但 StartSSL 提供的免費證書是一類的、僅對域名和email進行驗證,不對組織做驗證(也就是面向自然人的,非面向組織機構的),不過
僅作爲域名驗證和數據加密也夠了,並且瀏覽器也認它,一般人也不會去看你的證書級別。適合個人和初創網站使用,以後有錢了再申請個收費的替換即可。

我很順利地申請到了免費的StartSSL證書,分別用在兩個子域上。

最後,證書籤發給你後,最主要是保護好私鑰證書,這個丟失或泄漏就完了。因爲如果被別人利用也就毫無安全性了,需要向證書籤發機構申請撤銷證書並申請新的證書,這當然也是要收費的。

應用規劃、配置和調整
並不是說有了SSL證書就沒事了,還要考慮應用中的使用問題,需要規劃、服務器配置、應用調整等多個環節。

SSL比 http 要消耗更多cpu資源(主要是在建立連接的階段,之後還要對內容加密),所以對一般網站,只需要對部分地方採用https,大部分開放內容是沒必要的,具體取決於你的業務要求。比如對於很多安全要求較低的網站,完全不用https也是可接受的。

某些頁面是同時支持 http 和 https ,還是隻支持 https、強制 https?
同時支持就是用戶用什麼協議訪問都可以,那麼用戶的請求主要就是由頁面本身的鏈接引導來的,因爲一般用戶不會自己特意去修改地址欄的。
一般我們的網站可以做成同時支持http和https,都可以訪問。但是這就容易有後面說的混合內容或混合腳本的問題。

還可以規劃爲部分頁面支持 https,一般公開頁面不用https,只是將部分地方的鏈接改爲 https 就可以了。專門期望以 https 訪問的頁面中,引用的絕對URL可以明確的使用 https鏈接。

是否強制 https ?對於安全性高的網站或網站中的部分頁面,可以強制使用https訪問, 即使用戶在地址欄裏手工把 https 改爲 http, 也會被自動重定向回 https 上。比如可以通過配置web服務器 rewrite 規則將這些 http url 自動重定向到對應的 https url 上(這樣維護比較簡單),而不用改應用。

解決混合內容問題(http和https)

混合內容是指:在https的頁面中混合了非https的資源請求,比如圖片、css、js 等等。如果是混合了非 https 的 js 代碼,則被稱爲混合腳本。
混合內容的危害:如果只是混合了不安全的圖片和css,那麼受中間人攻擊篡改,一般只會影響頁面的顯示,危害相對小一點。如果是混合了不安全的 js 代碼,則這個不安全的 js 可以完全訪問和修改頁面中的任何內容,這是非常危險的。

另請參看,Chrome對混合腳本危害的說明與提示:Trying to end mixed scripting vulnerabilities

所以,只有頁面本身和所有引用的資源都是 https 的瀏覽器才認爲是安全的,只要其中引用了非安全資源(即使圖片),瀏覽器都會給出不安全的提示,特別是有 js 的情況。如果瀏覽器提示不安全,那樣我們就達不到原來目的了。我們費了半天功夫去申請 SSL 證書,配置Web服務器,最後如果因爲混合內容而前功盡棄就太糟了。咱繼續努力吧,想辦法讓所有引用資源都是安全的。

理論上,混合了第三方的內容,即使是SSL的第三方內容也不是很好。因爲用戶信任的是你,而不是第三方,即使第三方也支持https,但你能保證第三方就絕對安全嗎。不引用任何第三方纔是絕對安全的,但這樣太嚴格了,安全其實也是一個 tradeoff 的問題,需要考慮很多方面的平衡。還好,起碼現在瀏覽器認爲已經是安全的了。

引用第三方文件的問題(如 CDN 分發的文件)

簡單地說,這個問題要麼有第三方提供 https 支持,要麼不用它(用自己本地的)。

一般我們會引用由 CDN 分發的文件,比如某個 js 庫文件,而不用訪問自己網站上的,這樣藉助 CDN 網絡可以加快速度,這當然很好。
但是,如果我們在頁面中使用絕對 URL 直接引用這個文件就無法自動使用 https 了!出現了混合協議內容,瀏覽器又該“變臉”了。

當SSL 遇上CDN 或 其它第三方文件就有點麻煩,因爲很多CDN還不支持SSL。如果支持 https 的話就可以直接用 https 的絕對URL了,即使是同時支持http 和 https 的頁面,這樣做也不算太浪費,起碼解決了問題。

因爲CDN的雲文件提供者,一般爲每個cdn用戶創建一個單獨的子域名來使用,這樣的話,CDN提供者要想支持 https 就必須支持所有可能的子域名,因此要求CDN提供方使用那種通配符子域名的證書。

相對 URL、絕對 URL 與 只缺協議的URL(Protocol Relative URL)
相對路徑比較簡單,自動匹配用戶請求的 http 或 https 協議。

但是絕對 url 則不成,因爲絕對 url 已經明確地寫上了協議: http://www.example.com/jquery.js
這個問題還有一個辦法解決,你一定沒見過這種形式: //www.example.com/jquery.js
哈哈,一個缺少協議的URL(實際上還算是相對URL),這種形式可以在瀏覽器中被正確補充上合適的協議!很多人都用這種方法。
但是,這裏有點小問題,IE7 和 IE8 處理這種缺少協議的URL的css 文件時,同一個css文件會下載兩次,詳見Steve的文章 。

JS 自動判斷當前協議
現在我們經常用 js 來加載其它 js 文件或 其它別的文件,如果是請求是相對URL則沒問題,如果是絕對URL怎麼辦?
其實 js 腳本可以這樣:document.location.protocol 等於 ‘http:’ 還是 ‘https:’ 來判斷。例如在 Google Analytics 的嵌入代碼中:
ga.src = (‘https:’ == document.location.protocol ? ‘https://ssl’ : ‘http://www‘) + ‘.google-analytics.com/ga.js’;

應用程序中如何判斷訪問協議
對於動態頁面,如 jsp、php等,也是可以動態判斷當前是否使用了 https 協議的。所以應用可以根據動態判斷,來生成不同的引用 URL。這樣雖然有點麻煩,但也算是解決了自動識別協議的問題,當然相對路徑總是不需要處理的。
比如在 jsp 中:

request.isSecure() 爲true 表示當前爲 https ,false表示 http 訪問
request.getScheme() 返回字符串 https 或 http

注意,如果 tomcat 部署在其它web服務器代理的後面,需要正確配置好才能返回正確結果,見本文最後一部分。

同源策略的問題
最後提醒一點:http 和 https是不同源的!即使後面的內容都一樣。所以 ajax 發請求的時候要使用正確協議的絕對URL才行。
相對URL的 ajax 請求沒關係。

Nginx 配置
小結一下 Nginx 配置SSL注意的問題,詳細安裝配置內容請參考其它資料,如官方 SSL模塊 和 https配置文檔。
1. 首先檢查一下是否已安裝了 SSL模塊,因爲默認是不包含的。

用 nginx -V 命令檢查一下。如果沒有ssl模塊則需要重新安裝(建議升級到最新版本),注意安裝時加上ssl 選項:
./configure –with-http_ssl_module

另外,nginx需要依賴 openssl 提供ssl支持,這個也要有。

  1. nginx.conf 中的典型配置示例
    listen 80;
    listen 443 ssl;
    ssl_certificate cert.pem; #修改具體文件
    ssl_certificate_key ssl.key; #修改具體文件

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;

上面第2-4項是關鍵。這些配置放在 server 塊就可以對其中的所有 location 生效了,並且同時支持 http 和 https 。或者把 http 和 https 分開配置也很常見。

  1. 合併證書配置文件
    和Apache配置不同,Nginx需要將服務器證書和ca證書鏈合併到一個文件中,作爲 ssl_certificate 配置的內容。
    例如,按照證書鏈從下向上的順序,我有三個證書:

    ssl.crt(自己域名的服務器證書)
    sub.class1.server.ca.pem(startssl 的一類證書)
    ca.pem(startssl 的根證書)

把它們的內容按順序連接到的一個文件中,每個內容另起一行,中間沒有空行或空格。

  1. 避免啓動時輸入密碼
    配好之後,啓動nginx 要你輸入密鑰的密碼。這是因爲 ssl_certificate_key 配置對應的文件(也就是 startssl 給你的私鑰文件)內容是加密的,需要輸入你創建這個時設置的密碼才能解密。這樣私鑰雖然很安全,但是每次重啓服務都要輸入一次密碼也太麻煩了。其實,只要證書改爲解密了的內容,就可以避免每次輸入密碼。用如下命令即可:
    openssl rsa -in ssl.key -out newssl.key 輸入密碼,就生成了解密後的私鑰內容,使用這個就OK了。

但是就像前面說的,一定要在服務器上保護好它,例如:
chmod 400 ssl.key (僅root可讀)

  1. 優化SSL配置
    SSL 很消耗 CPU 資源,尤其是在建立連接的握手階段。一是通過開啓 keepalive 可以重用連接。二是可以重用和共享ssl session,見上面ssl_session相關配置。

獨立Tomcat+SSL
Tomcat 是很常見的 Java應用服務器,當然也可以作爲獨立的 Web服務器,所有用戶請求直接訪問 tomcat。

如果 Tomcat 作爲獨立的Web服務器,那麼就需要配置Tomcat就可以了,文檔參考這裏 和 這個。主要是配置存放證書的 Keystore 和 連接器Connector。

Java的keystore
keystore 是 Java 中專用並內置的一個類似於 openssl 的工具,一個 keystore 文件就是一個“保險箱”(database),專門存放證書和密鑰,和相關的管理功能:生成自簽發的證書、密鑰、導入導出等。可以通過 keytool 命令或 Java api 交互。
利用keytool 命令將你的證書導入進去。

Tomcat中Connector
tomcat中有三種 Connector 實現:block、nio 和 APR。前兩者使用Java SSL(這需要 keystore 的配置 ),APR使用OpenSSL(不需要用keystore,直接指定證書),配置略有不同。

Nginx+Tomcat+SSL
實際上,大規模的網站都有很多臺Web服務器和應用服務器組成,用戶的請求可能是經由 Varnish、HAProxy、Nginx之後纔到應用服務器,中間有好幾層。而中小規模的典型部署常見的是 Nginx+Tomcat 這種兩層配置,而Tomcat 會多於一臺,Nginx 作爲靜態文件處理和負載均衡。

如果Nginx作爲前端代理的話,則Tomcat根本不需要自己處理 https,全是Nginx處理的。用戶首先和Nginx建立連接,完成SSL握手,而後Nginx 作爲代理以 http 協議將請求轉給 tomcat 處理,Nginx再把 tomcat 的輸出通過SSL 加密發回給用戶,這中間是透明的,Tomcat只是在處理 http 請求而已。因此,這種情況下不需要配置 Tomcat 的SSL,只需要配置 Nginx 的SSL 和 Proxy。

在代理模式下,Tomcat 如何識別用戶的直接請求(URL、IP、https還是http )?
在透明代理下,如果不做任何配置Tomcat 認爲所有的請求都是 Nginx 發出來的,這樣會導致如下的錯誤結果:

request.getScheme()  //總是 http,而不是實際的http或https
request.isSecure()  //總是false(因爲總是http)
request.getRemoteAddr()  //總是 nginx 請求的 IP,而不是用戶的IP
request.getRequestURL()  //總是 nginx 請求的URL 而不是用戶實際請求的 URL
response.sendRedirect( 相對url )  //總是重定向到 http 上 (因爲認爲當前是 http 請求)

如果程序中把這些當實際用戶請求做處理就有問題了。解決方法很簡單,只需要分別配置一下 Nginx 和 Tomcat 就好了,而不用改程序。
配置 Nginx 的轉發選項:

proxy_set_header Host host;proxysetheaderXRealIP remote_addr;
proxy_set_header X-Forwarded-For proxy_add_x_forwarded_for;  
proxy_set_header X-Forwarded-Proto
scheme;

配置Tomcat server.xml 的 Engine 模塊下配置一個 Value:

配置雙方的 X-Forwarded-Proto 就是爲了正確地識別實際用戶發出的協議是 http 還是 https。X-Forwarded-For 是爲了獲得實際用戶的 IP。
這樣以上5項測試就都變爲正確的結果了,就像用戶在直接訪問 Tomcat 一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章