Nginx部署部分https與部分http

    一般而言,大規模的網站都有很多臺Web服務器和應用服務器組成,用戶的請求可能是經由Varnish,HAProxy,Nginx之後纔到應用服務器,中間有好幾層。而中小規模的典型部署常見的是 Nginx+Tomcat/jboss 這種兩層配置,而Tomcat或jboss 會多於一臺,Nginx 作爲靜態文件處理和負載均衡。下面重點講解nginx+jboss+ssl實現部分頁面https部分頁面http

    如果Nginx作爲前端代理的話,則Tomcat/jboss不處理自己 https,全交由Nginx處理是可以的。一般情況下nginxTomcat/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


同時支持就是用戶用什麼協議訪問都可以,那麼用戶的請求主要就是由頁面本身的鏈接引導來的,因爲一般用戶不會自己特意去修改地址欄的。

一般我們的網站可以做成同時支持httphttps,都可以訪問。但是這就容易有後面說的混合內容或混合腳本的問題。

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

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


解決混合內容問題(httphttps)


混合內容是指:在https的頁面中混合了非https的資源請求,比如圖片、cssjs 等等。如果是混合了非 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傳送的就可以了,這個在後面的代碼中展示。

在其他主流瀏覽器(如IE9FF4)需要點擊確認對話框來確定是否顯示混合內容。


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;

            #當AAABBB相同時,可用$host常量

            rewrite ^(.*) https://www.BBB.com$1 permanent;

        }

Url重寫之後,到443端口,然後通過443端口的代理配置: 

        proxy_pass http://cluster;

Nginxhttps請求轉發給後臺的jboss應用服務器。由於https狀態會保持,在登陸之後跳轉到其它頁面時,如果不進行強制使用http,會一直保持https狀態,443端口的如下配置:

    location / {

            rewrite ^(.*) http://www.AAA.com$1 permanent;

        }

會使得https狀態下的非login.htm鏈接使用http協議。

在登錄頁面引入的cssjsgif等資源,由於是後端的jbosshttp狀態返回給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頁面的cssjsgif也都會走https,影響效率,又使得http頁面存在https方式引入的內容,解決辦法是把登錄頁面需要的資源放在一個可以區分的路徑下面,location匹配的時候,讓該路徑下的cssjsgif等資源走https,其他路徑下面的資源走http


在代理模式下,jboss 如何識別用戶的直接請求(URLIPhttps還是http )


在透明代理下,如果不做任何配置jboss認爲所有的請求都是 Nginx 發出來的,這樣會導致如下的錯誤結果:

request.getScheme()  //總是 http,而不是實際的httphttps

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通道實現


如果想要在nginxjboss之間也實現加密傳輸,僅需要做如下關鍵點的修改:

(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)修改其它混合內容







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