轉自
Nginx部署部分https與部分http - na_tion的專欄 - 博客頻道 - CSDN.NET
http://blog.csdn.net/na_tion/article/details/17334669
一般而言,大規模的網站都有很多台Web服務器和應用服務器組成,用戶的請求可能是經由Varnish,HAProxy,Nginx之后才到應用服務器,中間有好幾層。而中小規模的典型部署常見的是 Nginx+Tomcat/jboss 這種兩層配置,而Tomcat或jboss 會多於一台,Nginx 作為靜態文件處理和負載均衡。下面重點講解nginx+jboss+ssl實現部分頁面https部分頁面http
如果Nginx作為前端代理的話,則Tomcat/jboss不處理自己 https,全交由Nginx處理是可以的。(一般情況下nginx和Tomcat/jboss處於同一個局域網內,安全問題暫時忽略。nginx與jboss之間加密傳輸在本文最后說明)用戶首先和Nginx建立連接,完成SSL握手,而后Nginx 作為代理以 http 協議將請求轉給 tomcat 處理,Nginx再把 Tomcat/jboss 的輸出通過SSL 加密發回給用戶,這中間是透明的,Tomcat/jboss只是在處理 http 請求而已。因此,這種情況下不需要配置 Tomcat 的SSL,只需要配置 Nginx 的SSL 和 Proxy。
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 代碼,則被稱為混合腳本。
特別注意:
(1)在http頁面混有https內容時,頁面排版不會發生亂排現象
(2)在https頁面,只有包含以http方式引入的資源(如圖片,js等)時,才算作混合內容,如果https頁面有超鏈接(如http:www.baidu.com),但是該超鏈接並沒有引入資源,不算做混合內容,頁面顯示正常,鼠標放在該超鏈接時,頁腳顯示:http://www.baidu.com或者www.baidu.com
混合內容的危害:如果只是混合了不安全的圖片和css,那么受中間人攻擊篡改,一般只會影響頁面的顯示,危害相對小一點。如果是混合了不安全的 js 代碼,則這個不安全的 js 可以完全訪問和修改頁面中的任何內容,這是非常危險的。
所以,只有頁面本身和所有引用的資源都是 https 的瀏覽器才認為是安全的,只要其中引用了非安全資源(即使圖片),瀏覽器都會給出不安全的提示,特別是有 js 的情況。如果瀏覽器提示不安全,那樣我們就達不到原來目的了。我們費了半天功夫去申請 SSL 證書,配置Web服務器,最后如果因為混合內容而前功盡棄就太糟了。
理論上,混合了第三方的內容,即使是SSL的第三方內容也不是很好。因為用戶信任的是你,而不是第三方,即使第三方也支持https,但你能保證第三方就絕對安全嗎。不引用任何第三方才是絕對安全的,但這樣太嚴格了,安全其實也是一個 tradeoff 的問題,需要考慮很多方面的平衡。
Chrome出絕招阻斷混合腳本漏洞:http://news.mydrivers.com/1/197/197058.htm
谷歌瀏覽器混合顯示內容會這樣指示:
谷歌瀏覽器混合腳本得以執行時,整個原始頁面都會受到影響,原因是瀏覽器阻止混合內容的加載。如果Chrome的UI顯示網站上存在混合內容問題,可以嘗試Google的開發工具定位問題。有用的信息通常記錄在JavaScript控制台(菜單->工具->JavaScript控制台),只要把提示的非https傳送內容改為https傳送的就可以了,這個在后面的代碼中展示。
在其他主流瀏覽器(如IE9或FF4)需要點擊確認對話框來確定是否顯示混合內容。
Nginx+jboss+http/https詳細配置
1、nginx.conf 中的配置示例
http {
upstream cluster{
server 172.18.0.33:80 weight=1;
server 172.18.0.101:8088 weight=5;
}
upstream clusterssl{
server 172.18.0.33:8443 weight=1;
server 172.18.0.101:8443 weight=5;
}
server {
listen 80;
server_name www.AAA.com;
location / {
proxy_pass http://cluster;
index index.htm;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 10;
proxy_read_timeout 120;
}
location ~* /*login.htm {
#rewrite ^(.*) https://$host$1 permanent;
rewrite ^(.*) https://www.BBB.com$1 permanent;
}
location ~ \.(css|js|gif)$ {
#rewrite ^(.*) https://$host$1 permanent;
rewrite ^(.*) https://www.BBB.com$1 permanent;
}
# HTTPS server
server {
listen 443;
server_name www.BBB.com;
ssl on;
ssl_certificate server.pem;
ssl_certificate_key server.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_protocols SSLv3 TLSv1;
ssl_ciphers HIGH:!ADH:!EXPORT56:RC4+RSA:+MEDIUM;
ssl_prefer_server_ciphers on;
location / {
rewrite ^(.*) http://www.AAA.com$1 permanent;
}
location ~* /*login.htm {
proxy_pass http://cluster;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ \.(css|js|gif)$ {
proxy_pass http://cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
首先,用戶訪問http://www.AAA.com,進入網站首頁,當點擊登陸頁面http://www.AAA.com/login.htm頁面時,由於在80端口進行如下配置:
location ~* /*login.htm {
#rewrite ^(.*) https://$host$1 permanent;
#當AAA與BBB相同時,可用$host常量
rewrite ^(.*) https://www.BBB.com$1 permanent;
}
Url重寫之后,到443端口,然后通過443端口的代理配置:
proxy_pass http://cluster;
Nginx將https請求轉發給后台的jboss應用服務器。由於https狀態會保持,在登陸之后跳轉到其它頁面時,如果不進行強制使用http,會一直保持https狀態,443端口的如下配置:
location / {
rewrite ^(.*) http://www.AAA.com$1 permanent;
}
會使得https狀態下的非login.htm鏈接使用http協議。
在登錄頁面引入的css、js、gif等資源,由於是后端的jboss以http狀態返回給nginx的,所以login.htm頁面會有混合內容,瀏覽器認為是不安全的,不進行加載,會出現頁面排版亂排等現象,谷歌瀏覽器提示如下:
打開谷歌瀏覽器:菜單->工具->JavaScript控制台,信息如下
IE9顯示如下:
在80端口下進行如下配置:
location ~ \.(css|js|gif)$ {
#rewrite ^(.*) https://$host$1 permanent;
rewrite ^(.*) https://www.BBB.com$1 permanent;
}
在443端口進行如下配置:
location ~ \.(css|js|gif)$ {
proxy_pass http://cluster;
}
可以解決頁面有混合內容的問題。
現在,另一個問題出現了,如果按照上面進行配置,那么其它非login頁面的css、js、gif也都會走https,影響效率,又使得http頁面存在https方式引入的內容,解決辦法是把登錄頁面需要的資源放在一個可以區分的路徑下面,location匹配的時候,讓該路徑下的css、js、gif等資源走https,其他路徑下面的資源走http。
在代理模式下,jboss 如何識別用戶的直接請求(URL、IP、https還是http )?
在透明代理下,如果不做任何配置jboss認為所有的請求都是 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 就好了,而不用改程序。
配置 Nginx 的轉發選項:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
優化
1、優化rewrite語句:
rewrite復雜而且低效,用return語句代替,如
rewrite (.*) http://www.example.org$1;
改為
retrun 301 http://www.example.org$request_uri;
2、優化SSL配置
SSL操作需要消耗CPU資源,所以在多處理器的系統,需要啟動多個工作進程,而且數量需要不少於可用CPU的個數。最消耗CPU資源的SSL操作是SSL握手,有兩種方法可以將每個客戶端的握手操作數量降到最低:第一種是保持客戶端長連接,在一個SSL連接發送多個請求;第二種是在並發的連接或者后續的連接中重用SSL會話參數,這樣可以避免SSL握手的操作。會話緩存用於保存SSL會話,這些緩存在工作進程間共享,可以使用ssl_session_cache指令進行配置。1M緩存可以存放大約4000個會話。默認的緩存超時是5分鍾,可以使用ssl_session_timeout加大它。下面是一個針對4核系統的配置優化的例子,使用10M的共享會話緩存:
worker_processes 4;
http {
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server {
listen 443;
server_name www.example.com;
keepalive_timeout 70;
ssl on;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
證書密鑰的保護:
服務器證書是公開的,會被傳送到每一個連接到服務器的客戶端,而私鑰不是公開的,不會被發送,在服務器上要保護好它,比如存放在訪問受限的文件中,例如:chmod 400 ssl.key (僅root可讀),當然,nginx主進程必須有讀取密鑰的權限;或者為私鑰文件設置密碼時,這樣私鑰雖然很安全,但是每次重啟nginx服務都要輸入一次密碼,比較麻煩
Nginx前端機與后端jboss間部分http部分https通道實現
如果想要在nginx與jboss之間也實現加密傳輸,僅需要做如下關鍵點的修改:
(1)在jboss端配置服務器證書,並開啟https端口(下面以8443端口為例)
(2)配置jboss端的部分https部分http
(2)在nginx監聽443端口的服務修改如下:
location ~* /*login.htm {
proxy_pass https://clusterssl;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
(3)修改其它混合內容