基礎 Web 漏洞攻擊與防禦的思考
未知攻焉知防,攻與防的較量每天都在上演,但這些攻擊都逃不過這幾大基本的漏洞型別,下面我簡單的列舉的了幾個漏洞型別,並對其中比較重要的漏洞的產生原因、攻擊方法、防禦措施做了一個簡單的分析,並加入了一些自己的思考。
0X01 頁面展示類漏洞
一、XSS
1.XSS 的型別
(1)反射型
攻擊者會通過社會工程學手段,傳送一個URL連線給使用者開啟,在使用者開啟頁面的同時,瀏覽器會執行頁面中嵌入的惡意指令碼。
(2)儲存型
攻擊者利用web應用程式提供的錄入或修改資料功能,將資料儲存到伺服器或使用者cookie中,當其他使用者瀏覽展示該資料的頁面時,瀏覽器會執行頁面中嵌入的惡意指令碼。所有瀏覽者都會受到攻擊。
(3)DOM型
由於html頁面中,定義了一段JS,JS 程式碼會根據使用者的輸入,動態顯示一段html程式碼,攻擊者可以在輸入時,插入一段惡意指令碼,最終展示時,會執行惡意指令碼。
DOM跨站和以上兩個跨站攻擊的差別是,DOM跨站是純頁面指令碼的輸出,只有規範使用JAVASCRIPT,才可以防禦
2.XSS 的本質
本質就是 執行 JS 指令碼 ,所以 js 能做什麼XSS 就能做什麼,最主要的還是一種互動,向外界傳送資料或者從外界獲取資料,再或者就是直接完成一些動作,如跳轉、模擬點選等。
(1)傳送資料體現在向外傳送cookie 或者 CSRF token 等,來幫助攻擊者獲取令牌偽造身份
(2)從外界獲取資料體現在利用 XSS 進行釣魚,比如在該頁面上面引入外部頁面,覆蓋原有頁面掩人耳目
(3)完成動作體現在,URL 重定向攻擊,帶受害者去另一個惡意頁面,進行釣魚以及模擬使用者傳送請求(CSRF),如自我複製的 XSS 蠕蟲
3.XSS 的產生原因
對使用者資料過濾不嚴而將惡意的 JS 程式碼直接輸入了 html 標籤,當頁面載入時便會執行。
4.解決方案
1.在HTML/XML中顯示“使用者可控資料”前,應該進行html escape轉義。
2.在javascript內容中輸出的“使用者可控資料”,需要做javascript escape轉義。
3.對輸出到富文字中的“使用者可控資料”,做富文字安全過濾(允許使用者輸出HTML的情況)。
4.一些html標籤的屬性,需要,如果接收“使用者可控資料”,需要做安全檢查。
5.DOM XSS 請規範使用 javascript,遵守 javascript 安全編碼規範
6.在給使用者設定認證COOKIE時,加入HTTPONLY
7.在style內容中輸出的“使用者可控資料”,需要做CSS escape轉義
8.ajax 中的 xml json 中的使用者可控資料 做 escape 轉義
二、Third-party script references
1.Third-party script references的原理及危害
Third-party script references,引用第三方網站指令碼或iframe指向第三方網站。
當html頁面引用了第三方網站的指令碼,或者有iframe指向第三方網站時,一旦這個第三方網站出現安全問題,被黑客控制寫入惡意指令碼,那麼該頁面或指令碼也會展示在自己網站上,會導致自己網站的安全性大大降低,訪問者間接受到影響。
2.攻擊示例
當自己網站上有這樣一段程式碼的時候
<script src="http://www.another.com/some.js"></script>
這段程式碼會執行www.another.com網站下的some.js指令碼。
或者自己網站上有著這樣一段程式碼
下面是一段引用第三方網站iframe的程式碼:
<iframe src="http://www.another.com/some.html"></iframe>
這段程式碼會同時開啟 http://www.another.com/some.html 這個頁面。
惡意使用者可以做以下攻擊:惡意使用者會先入侵 www.some.com 這個網站,之後將惡意程式碼加入 http://www.another.com/some.js ,當這個檔案有惡意程式碼時,也會被展示在自己的web頁面上。
3.解決方案
正確使用 CSP 來禁用外部指令碼,或者用 CSP 設定指令碼來源白名單
0X02 偽裝欺騙類漏洞
一、CSRF
1.CSRF 的原理
Cross-Site Request Forgery(CSRF),跨站請求偽造攻擊。
攻擊者在使用者瀏覽網頁時,利用頁面元素(例如img的src),強迫受害者的瀏覽器向Web應用程式傳送一個改變使用者資訊的請求。由於發生CSRF攻擊後,攻擊者是強迫使用者向伺服器傳送請求,所以會造成使用者資訊被迫修改,更嚴重者引發蠕蟲攻擊。
2.CSRF 的攻擊方式及危害
CSRF攻擊可以從站外和站內發起。
(1)從站內發起CSRF攻擊:需要利用網站 本身的業務 ,比如“自定義頭像”功能,惡意使用者指定自己的頭像URL是一個修改使用者資訊的連結,當其他已登入使用者瀏覽惡意使用者頭像時,會自動向這個連結傳送修改資訊請求。
(2)從站外發起CSRF攻擊:則需要惡意使用者在自己的伺服器上,放一個自動提交修改個人資訊的html頁面,並把頁面地址發給受害者使用者,受害者使用者開啟時,會發起一個請求。如果惡意使用者能夠知道網站管理後臺某項功能的URL,就可以直接攻擊管理員,強迫管理員執行惡意使用者定義的操作。
3.CSRF 的本質
本質就是 利用 JS 指令碼 以及攻擊者精心構造的惡意請求,結合使用者的某網站 SESSION 已經存在的條件,在使用者不知不覺中向該網站發起篡改請求。
再結合我們之前說的 XSS 的攻擊方式的本質 ,我們也不難發現兩者之間的緊密聯絡,這也是兩者經常配合利用的原因,兩者的結合也是釣魚人經常利用的手法。
(1)網站內部指令碼:因為當網站出現 XSS 漏洞的時候,就意味著能插入 JS 指令碼,那就能在使用者不知情的情況下向本站發起請求,這種請求是模仿來源於本站使用者的請求的,如果使用者是管理員且登入後臺,攻擊者在知道後臺表單格式的情況下,就可以構造針對性的請求使之被莫名篡改,達成攻擊目的。
(2)網站外部指令碼:如果網站出現 XSS ,那麼就可以引入一個外部頁面,這個頁面可以是任意頁面,使用者看起來就是到了另一個頁面(注意此時位址列的 URL 是不變的,當然使用者不會注意到),我們可以在這個頁面裡面引入一個 iframe,iframe 引入我們的惡意請求(當然我們要設定 iframe 寬高為 0),這樣也同樣達到了隱形篡改的目的。
4.解決方案
(1)Token 防禦
遵循以下三步可預防 CSRF:
1.在使用者登陸時,設定一個CSRF的隨機TOKEN,存放在使用者的 設定了 http-only 的cookie中 ,或者是 session 中,當用戶瀏覽器關閉、或使用者再次登入、或退出時,清除token。
2.在表單中,生成一個隱藏域,它的值就是COOKIE中隨機TOKEN。
3.表單被提交後,就可以在接收使用者請求的web應用中,判斷表單中的TOKEN值是否和使用者 http-only 的COOKIE中的或者是伺服器端的 sesson 中的TOKEN值一致,如果不一致或沒有這個值,就判斷為CSRF攻擊,同時記錄攻擊日誌
由於攻擊者無法預測每一個使用者登入時生成的那個隨機TOKEN值,所以無法偽造這個引數。
(2)同源策略
對於 ajax 形式的 csrf ,同源策略是一概拒絕的
(3)驗證 referer :
(1)但是由於 referer 是瀏覽器設定的,屬於第三方,這樣就把安全寄託於第三方之上,某種程度來說也是不是很安全,雖然已經安全做的很好了
(2)由於使用者可以自己設定不生成 referer ,因此在這種情況下不是很好用
(3) referer 只能用來防禦外部指令碼的 CSRF ,但是對於網站本身由 XSS 造成的 CSRF 是無法防禦的。
注意:
1.放在 http-only 的 cookie 裡面是為了,防止出現 正規網站的 xss 獲取 cookie 中的 token 後在傳送 請求
2.XSS 漏洞高於 SCRF 如果出現了 xss 的話,很有可能迴繞過 CSRF 防護進行攻擊
3.當出現GET請求修改使用者資料時,一旦在url中出現了csrftoken,當前頁面就不允許出現使用者定義的站外連結,否則攻擊者可以引誘使用者點選攻擊者定義的連結,訪問在自己的網站,從referer中,獲取url中的csrftoken,造成csrftoken洩露。
二、URL Redirect
1.URL Redirect 的原理
Web應用程式接收到使用者提交的URL引數後,沒有對引數做“可信任URL”的驗證,就向用戶瀏覽器返回跳轉到該URL的指令。
2.URL Redirect攻擊方式及危害
如果 xxxx.com 這個可信網站下的某個web應用程式存在這個漏洞,惡意攻擊者可以傳送給使用者一個看上去是指向 xxxx.com網站的連結,但是使用者開啟後,實際上早就不在 xxxx.com 網站下了,而是跳轉到了攻擊者精心設計的釣魚網站頁面,而此時受害者還以為自己在 xxxx.com 網站下,這就導致使用者被釣魚攻擊,賬號被盜,或賬號相關財產被盜。
例如這樣一段程式碼
if(checklogin(request)){ response.sendRedirect(request.getParameter("url")); }
這段程式碼並沒有檢驗 url 的跳轉地址就直接跳轉了,導致了該漏洞的發生,同樣,這也是釣魚人最喜歡的攻擊方式
3.攻擊舉例
攻擊者某天盜了某管理員同事的QQ號,然後給這個管理員發了一個連結,連結如下:
http://admin.xxxx.com/member/sigin.htm?done=http://www.hacker.com:8080/admin/sigin.htm
因為這個連結很長,管理員可能就看了一下開頭,感覺是自己管理介面的 URL ,然後以為有什麼事,就點開了,但是沒想到這裡存在一個 URL Redirect 漏洞,實際訪問的是 http://www.hacker.com:8080/admin/sigin.htm 這是一個攻擊者精心構造的,和管理後臺介面一樣的頁面,然後要求管理員輸入密碼,這時候其實輸入任何密碼都會說錯誤,然後就會跳轉到真正的登入介面,此時管理員並沒有發覺,再次輸入密碼,成功進入後臺,但此時攻擊者早就掌握了管理員的賬號密碼了。
4.解決方案
1.設定並驗證 token
為了保證使用者所點選的URL,是從web應用程式中生成的URL,所以要做TOKEN驗證。
(1)當用戶訪問需要生成跳轉URL的頁面時,首先生成隨機token,並放入cookie。
(2)在顯示連線的頁面上生成URL,在URL引數中加入token。
示例:
http://admin.xxxx.com/member/sigin.htm?done=http://www.xxxx.com&token=5743892783432432
(3)應用程式在跳轉前,判斷token是否和cookie中的token一致,如果不一致,就判定為URL跳轉攻擊,並記錄日誌.
(4)如果在javascript中做頁面跳轉,需要判斷域名白名單後,才能跳轉。
2.設定地址白名單
(1)如果應用只有跳轉到本集團/公司網站的需求,可以設定白名單,判斷目的地址是否在白名單列表中,如果不在列表中,就判定為URL跳轉攻擊,並記錄日誌
(2)不允許配置集團以外網站到白名單列表中。
這兩個方案都可以保證所有在應用中發出的重定向地址,都是可信任的地址。
三、SSRF
1.SSRF的原理及危害
很多web應用都提供了從其他的伺服器上獲取資料的功能。使用使用者指定的URL,web應用可以獲取圖片,下載檔案,讀取檔案內容等。這個功能如果被惡意使用,可以利用存在缺陷的web應用作為代理攻擊遠端和本地的伺服器。這種形式的攻擊稱為服務端請求偽造攻擊(Server-side Request Forgery)。
一般情況下, SSRF攻擊的目標是從外網無法訪問的內部系統(正是因為它是由服務端發起的,所以它能夠請求到與它相連而與外網隔離的內部系統 )
SSRF 形成的原因大都是由於 服務端提供了從其他伺服器應用獲取資料的功能且沒有對目標地址做過濾與限制 比如從指定URL地址獲取網頁文字內容,載入指定地址的圖片,下載等等。
攻擊者利用ssrf可以實現的攻擊主要有5種:
1.可以對外網、伺服器所在內網、本地進行埠掃描,獲取一些服務的banner資訊;
2.攻擊執行在內網或本地的應用程式(比如溢位);
3.對內網web應用進行指紋識別,通過訪問預設檔案實現;
4.攻擊內外網的web應用,主要是使用get引數就可以實現的攻擊(比如struts2,sqli等);
5.利用file協議讀取本地檔案等。
6.還可以使用 gopher 協議拓寬我們的攻擊面,比如實現攻擊內網的 mysql
2.攻擊方式
3.解決方案
解決方案無非就兩種思路,一種是打斷從內網出外網的道路,另一種就是打斷邊界伺服器進內網的道路,當然實際中這兩者需要結合使用
打斷由內到外:
1.過濾內網伺服器對公網伺服器請求的響應。如果Web應用是獲取某一型別的檔案,在把返回結果展示給使用者之前應先驗證返回的資訊是否符合檔案型別標準,比如返回資訊應為圖片,如果返回資訊是HTML,則停止將返回資訊返回客戶端。
2.統一錯誤提示資訊,避免使用者可以根據錯誤資訊來判斷遠端伺服器的埠狀態。
打斷由外到內:
1.在內網伺服器的防火牆上限制公網伺服器的請求埠為HTTP等協議常用埠,如:80、443、8080、8090。
2.若公網伺服器的內網IP與內網無業務通訊,建議將公網伺服器對應的內網IP列入黑名單,避免應用被用來獲取內網資料。
3.內網伺服器禁用不必要的協議,僅允許HTTP和HTTPS請求,防止類似於file:///、gopher://、ftp:// 等協議引起的安全問題。
0X03 注入類漏洞
一、SQLi
1.SQLi 的原理
當應用程式將使用者輸入的內容, 拼接 到SQL語句中,一起提交給資料庫執行時,就會產生SQL注入威脅。
2.SQLi 的攻擊方式以及危害
由於使用者的輸入,也是SQL語句的一部分,所以攻擊者可以利用這部分可以控制的內容,注入自己定義的語句,改變SQL語句執行邏輯,讓資料庫執行任意自己需要的指令。通過控制部分SQL語句,攻擊者可以查詢資料庫中任何自己需要的資料,利用資料庫的一些特性,可以直接獲取資料庫伺服器的系統許可權。
3.解決方案
使用預處理的方式去執行SQL語句,對所有傳入SQL語句中的變數,做繫結。這樣,使用者拼接進來的變數,無論內容是什麼,都會被當做替代符號“?”所替代的值,資料庫也不會把惡意使用者拼接進來的資料,當做部分SQL語句去解析,從而保證了資料庫的安全性。
Java odbc示例:
com.mysql.jdbc.Connection conn = db.JdbcConnection.getConn(); final String sql = "select * from product where pname like ?"; java.sql.PreparedStatement ps = (java.sql.PreparedStatement) conn.prepareStatement(sql); ps.setObject(1, "%"+request.getParameter("pname")+"%"); ResultSet rs = ps.executeQuery();
PHP示例:
$query = "INSERT INTO myCity (Name, CountryCode, District) VALUES (?,?,?)"; $stmt = $mysqli->prepare($query); $stmt->bind_param("sss", $val1, $val2, $val3); $val1 = 'Stuttgart'; $val2 = 'DEU'; $val3 = 'Baden-Wuerttemberg'; /* Execute the statement */ $stmt->execute();
當然和對於 PHP 而言現在有一種方法叫做 PDO,和 mysqli 原理類似,但是支援更多的資料庫型別,不像 mysqli 只支援 MySQL 一種,當然 mysqli 這種針對性的擴充套件相對於 MySQL 資料庫而言效果更好。兩者更詳細的比較可以參考 這篇文章
4.常見問題
用了 PDO 這種預編譯就能完全預防 SQL 注入了嗎?
(1) PDO 並不能滿足全部的 SQL 語句,有些語句過於複雜,使用 PDO 就非常困難,
(2)我們說安全最薄弱環節的還是人,有一些老的系統是從原來的語句改成使用預編譯的,這種情況下就很可能有漏網之魚
(3)p 牛寫過 一篇文章 ,關於 TP5 的一處注入,在 PDO::ATTR_EMULATE_PREPARES => false(模擬預編譯為 flase)的情況下如果能控制PDO 執行三部曲
prepare($SQL) 編譯SQL語句 bindValue($param, $value) 將value繫結到param的位置上 execute() 執行
中的第一步,就能讓其在伺服器端編譯的時候報錯,而不像控制第二部報錯了不能執行到第三部
(4)PHP 5.3.6及以前版本的PDO的bindParam,bindValue潛在的安全隱患,PHP 5.3.6及老版本,並不支援在DSN中定義charset屬性(會忽略之),這時如果使用PDO的本地轉義,仍然可能導致SQL注入,這個點的來源是 這篇文章
二、Code injection
1.Code injection 原理及危害
在 web 應用執行的過程中,使用者輸入的引數直接或者間接地作為程式碼的一部分傳入到可以執行程式碼的函式 如:php 中的 eval()當中,在沒有過濾或者過濾的不完全的情況下,攻擊者可以控制這個輸入實現任意程式碼在伺服器端執行,從而實現各種攻擊目的。
惡意使用者可以寫一個遠端控制木馬,直接獲取伺服器控制權限,所有伺服器上的資源都會被惡意使用者獲取和修改,甚至可以直接控制資料庫。
2.攻擊示例
servlet 程式碼:
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); try { File file = File.createTempFile("JavaRuntime", ".java", new File(System.getProperty("user.dir"))); String filename = file.getName(); String classname = filename.substring(0, filename.length() - 5); String[] args = new String[] { "-d", System.getProperty("user.dir"), filename }; PrintWriter outfile = new PrintWriter(new FileOutputStream(file)); outfile.write("public class " + classname + "{public void myfun(String args)" + "{try {"+ request.getParameter("code")+ "} catch (Exception e) {}}}"); outfile.flush(); outfile.close(); (new Main()).compile(args, outfile); URL url = new URL("file://"+ file.getPath().substring(0,file.getPath().lastIndexOf("\\") + 1)); java.net.URLClassLoader myloader = new URLClassLoader(new URL[] { url }, Thread.currentThread().getContextClassLoader()); Class cls = myloader.loadClass(classname); cls.getMethod("myfun", new Class[] { String.class }).invoke(cls.newInstance(), new Object[] { "" }); } catch (Exception se) { se.printStackTrace(); } out.println(); out.flush(); out.close();
}
這個程式碼中 request.getParameter(“code”) 中的 code 使用者可控,且沒有有效的過濾,我們就可以構造下面的語句進行攻擊
Payload:
http://www.xxxx.com/servlet/Active?code=System.out.println("kxlzx");
3.解決方案
對於應用程式碼中的可以執行程式碼的函式的輸入全部來源於開發人員的編寫,來確保使用者不可控,對於一定要使用者確定程式碼的情況,使用者能做的只是根據開發人員提供的介面選擇對應的程式碼執行。
三、XML injection(普通)
1.XML injection(普通) 原理及危害
XML是儲存資料的地方,如果在查詢或修改時,如果沒有做轉義,直接輸入或輸出資料,都將導致XML注入漏洞。攻擊者可以修改XML資料格式,增加新的XML節點,對資料處理流程產生影響。
2.攻擊示例
這裡是一個儲存註冊使用者資訊為xml的例子:
final String GUESTROLE = "guest_role"; ... //userdata是準備儲存的xml資料,接收了name和email兩個使用者提交來的資料。 String userdata = "<USER role="+ GUESTROLE+"><name>"+ request.getParameter("name")+ "</name><email>"+ request.getParameter("email")+"</email></USER>"; //儲存xml userDao.save(userdata);
對使用者的輸入沒有做任何的過濾,原本註冊使用者後,應該產生一條這樣的使用者記錄:
<?xml version="1.0" encoding="UTF-8"?> <USER role="guest_role"> <name>user1</name> <email>[email protected]</email> </USER>
但是惡意使用者的輸入卻是下面這個樣子
[email protected]</email></USER><USER role="admin_role"><name>kxlzx</name><email>[email protected]
於是最後註冊以後就變成了下面這個樣子
<?xml version="1.0" encoding="UTF-8"?> <USER role="guest_role"> <name>user1</name> <email>[email protected]</email> </USER> <USER role="admin_role"> <name>kxlzx</name> <email>[email protected]</email> </USER>
這樣就無形中增加了一個攻擊者自定義的管理員
3.解決方案
在XML儲存和展示前,對資料部分單獨做xml escape。
String userdata = "<USER role="+GUESTROLE+"><name>"+ StringUtil.xmlencode(request.getParameter("name"))+"</name><email>"+ StringUtil.xmlencode(rrequest.getParameter("email"))+"</email></USER>";
字元會按照下面的對映關係進行轉義
& --> & < --> < > --> > " --> " ' -->
四、XML injection(外部實體XXE)
1.XXE 的原理以及危害
XXE 全稱 XML 外部實體注入,XML 可以使用 DTD 來規定整個 XML 的格式,DTD 中可以定義 XML 中的實體,且 DTD 可以外部引用,由於該實體會在後面的 XML 中呼叫,所以如果我們能控制該外部實體
的引用路徑的話,我們就能向該路徑發起請求,可以利用這個特性實現檔案讀取、內網探測、sqli、檔案上傳等操作,具體的分析我在我之前的一篇文章中介紹過了。
2.攻擊示例
xml.php
<?php libxml_disable_entity_loader (false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom($dom); echo $creds; ?>
payload:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE creds [ <!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]> <creds>&goodies;</creds>
執行效果:
![此處輸入圖片的描述][7]
3.解決方案
方案一:使用語言中推薦的禁用外部實體的方法
PHP:
libxml_disable_entity_loader(true);
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance(); dbf.setExpandEntityReferences(false); .setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); .setFeature("http://xml.org/sax/features/external-general-entities",false) .setFeature("http://xml.org/sax/features/external-parameter-entities",false);
Python:
from lxml import etree xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
方案二:手動黑名單過濾(不推薦)
過濾關鍵詞:
<!DOCTYPE、<!ENTITY SYSTEM、PUBLIC
五、System command injection
1.System command injection 的原理及危害
系統命令執行攻擊,是指程式碼中有一段執行系統命令的程式碼,而使用者的輸入可以直接或者間接地作為這個命令或者是命令的一部分,所以在沒有對使用者的輸入進行過濾或者過濾不完全的時候,惡意攻擊者可以通過這個功能直接執行系統命令從而直接控制目標伺服器。
2.攻擊示例
java:
Runtime.getRuntime().exec(request.getParameter("cmd"));
攻擊者輸入:
http://www.xxxx.com/servlet/command?cmd=shutdown
就能實現關機的功能
3.解決方案
所有需要執行的系統命令,必須是開發人員定義好的,不允許接收使用者傳來的引數,加入到系統命令中去。
0X04 檔案操作類漏洞
一、File upload
1.File upload 原理及危害
Web應用程式在處理使用者上傳的檔案時,沒有判斷檔案的副檔名是否在允許的範圍內,或者是判斷過濾不到位,再或者就是存在某種可以被利用的解析漏洞,使檔案被儲存在伺服器上,導致惡意使用者可以上傳可以被伺服器解析的檔案,甚至上傳指令碼木馬到web伺服器上,從而可以直接控制web伺服器。如果配合檔案包含漏洞的話能達到更好的效果。
2.攻擊示例
下面的程式碼沒有過濾副檔名:
java
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter( request.getRealPath("/")+getFIlename(request)))); ServletInputStream in = request.getInputStream(); int i = in.read(); while (i != -1) { pw.print((char) i); i = in.read(); } pw.close();
php:
file_put_contents($_REQUEST["filename"],$_REQUEST["context"]);
假如第一段程式碼是xxxx.com下的一個web應用程式。這段程式碼將直接把使用者上傳的檔案,儲存在web目錄中,上傳後,使用者可以通過
http://www.xxxx.com/jspmuma.jsp
訪問上傳的JSP木馬,控制伺服器
注:即使jsp檔案不會被web容器解析執行,攻擊者也可以上傳自定義的htm檔案,造成XSS攻擊。
3.解決方案
1.檢查上傳副檔名白名單,不屬於白名單內,不允許上傳。
2.上傳檔案的目錄必須是http請求無法直接訪問到的。如果需要訪問的,必須上傳到其他(和web伺服器不同的)域名下,並設定該目錄為不解析jsp等指令碼語言的目錄。
3.上傳檔案要儲存的檔名和目錄名由系統隨機生成,不可預測,更不允許使用者自定義。
4.圖片上傳,要通過處理(縮圖、水印等),無異常後才能儲存到伺服器。
5.上傳檔案需要做日誌記錄
二、File download and Directory traversal
1.File download and Directory traversal 原理及危害
File download and Directory traversal,任意檔案下載攻擊和目錄遍歷攻擊。
(1)處理使用者請求下載檔案時,允許使用者提交任意檔案路徑,並把伺服器上對應的檔案直接傳送給使用者,這將造成任意檔案下載威脅。
(2)如果讓使用者提交檔案目錄地址,就把目錄下的檔案列表發給使用者,會造成目錄遍歷安全威脅。
惡意使用者會變換目錄或檔案地址,下載伺服器上的敏感檔案、資料庫連結配置檔案、網站原始碼等。
2.攻擊示例
java:
String path = request.getParameter("path"); java.io.OutputStream os = response.getOutputStream(); java.io.FileInputStream fis = new java.io.FileInputStream(path); byte[] b = new byte[1024]; int i = 0; while ((i = fis.read(b)) > 0 ){ os.write(b, 0, i); } fis.close(); os.flush(); os.close();
php:
$o = file_get_contents($filename); echo $o;
以上程式碼根據使用者提交的path,從伺服器上獲取指定檔案,展示給使用者。而攻擊者會變化引數中的檔名,下載伺服器中的敏感檔案,資料庫配置檔案等。
http://www.xxxx.com/filedownload.do?filename=/etc/passwd
3.解決方案
對檔案操作功能,做到以下幾點:
1.檔案路徑儲存至資料庫,讓使用者提交檔案對應ID下載檔案。
2.下載檔案之前做許可權判斷。
3.檔案放在web無法直接訪問的目錄下。
4.記錄檔案下載日誌。
7.不允許提供目錄遍歷服務。
0X05 訪問控制類漏洞
一、Vertical Access Control
1.Vertical Access Control 的原理及危害
由於web應用程式沒有做許可權控制,或僅僅在選單上做了許可權控制,導致的惡意使用者只要猜測其他管理頁面的URL,就可以訪問或控制其他角色擁有的資料或頁面,達到許可權提升目的。
這個威脅可能導致普通使用者變成管理員許可權。
2.攻擊示例
一個僅僅做了選單控制的程式碼:
<tr><td><a href="/user.jsp">管理個人資訊</a></td></tr> <%if (power.indexOf("administrators")>-1){%>管理所有使用者 <%}%><
="" code="">
惡意使用者,可以直接猜測“管理所有使用者”的頁面,通過URL訪問,看到管理員頁面,以管理員身份進行非法操作
3.解決方案
在開啟管理頁面URL時,首先判斷當前使用者是否擁有該頁面的許可權,如果沒有許可權,就判定為“許可權提升”攻擊,同時記錄安全日誌,同時建議使用成熟的許可權框架處理許可權問題,比如spring security。
二、Horizontal Access Control
1.Horizontal Access Control 原理與危害
Horizontal Access Control,訪問控制攻擊,也就是水平許可權安全攻擊。
Web應用程式接收到使用者請求,修改某條資料時,沒有判斷資料的所屬人,或判斷資料所屬人時,從使用者提交的request引數(使用者可控資料)中,獲取了資料所屬人id,導致惡意攻擊者可以通過變換資料ID,或變換所屬人id,修改不屬於自己的資料。惡意使用者可以刪除或修改其他人資料。
2.攻擊示例
訪問資料層(dao),所有的更新語句操作,都可能產生這個漏洞。
以下程式碼存在這個漏洞,web應用在修改使用者個人資訊時,從從使用者提交的request引數(使用者可控資料)中,獲取了userid,執行修改操作。
修改使用者個人資訊頁面
<form action="/struts1/edituser.htm" method="post"> <input name="userid" type="hidden" value="<%=userid%>"> <table border="1"> <tr> <td>username:</td> <td><%=rs.getString("name")%></td> </tr> <tr> <td>passwd:</td> <td> <input name="pass" value="<%=rs.getString("pass")%>"></td> </tr> <tr> <td>type:</td> <td><%=rs.getString("type")%></td> </tr> <tr> <td>realname:</td> <td><input name="realname" value="<%=rs.getString("realname")%>"></td> </tr> <tr> <td>email:</td> <td> <input name="email" value="<%=rs.getString("email")%>"></td> </tr> <tr> <td>tel:</td> <td> <input name="tel" value="<%=rs.getString("tel")%>"></td> </tr> </table> <html:submit/> </form>
表單中,將使用者的useird作為隱藏欄位,提交給處理修改個人資訊的應用。
下面程式碼是修改個人資訊的應用
int userid=Integer.valueOf( request.getParameter("userid")); String email=request.getParameter("email"); String tel=request.getParameter("tel"); String realname=request.getParameter("realname"); String pass=request.getParameter("pass"); JdbcConnection conn = null; try { conn = new JdbcConnection(); Object[] params = new Object[5]; params[0] = email; params[1] = tel; params[2] = realname; params[3] = pass; params[4] = userid; final String sql = "update user set email=?,tel=?,realname=?,pass=? where userid=?"; conn.execUpdate(sql,params); conn.closeConn();
這段程式碼是從request的引數列表中,獲取userid,也就是表單提交上來的userid,之後修改userid對應的使用者資料。而表單中的userid是可以讓使用者隨意修改的,如果惡意使用者提交時篡改了這個值,就能欺騙伺服器成為另一個使用者,從而修改其他使用者的資訊
3.解決方案
從使用者的 加密認證 cookie中,獲取當前使用者的id,並且需要在執行的SQL語句中,加入當前使用者id作為條件語句。由於是web應用控制的加密演算法,所以惡意使用者無法修改加密資訊。
示例程式碼:
int userid=Integer.valueOf( GetUseridFromCookie(request)); String email=request.getParameter("email"); String tel=request.getParameter("tel"); String realname=request.getParameter("realname"); String pass=request.getParameter("pass"); JdbcConnection conn = null; try { conn = new JdbcConnection(); Object[] params = new Object[5]; params[0] = email; params[1] = tel; params[2] = realname; params[3] = pass; params[4] = userid; final String sql = "update user set email=?,tel=?,realname=?,pass=? where userid=?"; conn.execUpdate(sql,params); conn.closeConn();
程式碼中通過GetUseridFromCookie,從加密的COOKIE中獲取了當前使用者的id,並加入到SQL語句中的WHERE條件中
0X06 Session 管理
一、Cookie httponly flag
1.Cookie httponly flag的原理及危害
Cookie http only,是設定COOKIE時,可以設定的一個屬性,如果COOKIE沒有設定這個屬性,該COOKIE值可以被頁面 js 指令碼讀取
當攻擊者發現一個XSS漏洞時,通常會寫一段頁面指令碼,竊取使用者的COOKIE,如果不設定這個屬性就會出現因為XSS漏洞導致大面積使用者COOKIE被盜,從而嚴重地危害業務安全。
2.攻擊示例
設定cookie的程式碼
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie"));
這段程式碼沒有設定http only屬性,攻擊者可以通過 JS 指令碼獲取使用者的 cookie,詳情見 XSS 漏洞
3.解決方案
設定cookie時,加入屬性即可
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly");
4.常見問題
httponly已經可以防止使用者cookie被竊取,還需要做XSS防禦嗎?
這個flag只能增加攻擊者的難度,不能達到完全防禦XSS攻擊,並且 XSS 能做的事非常的多,不只是獲取使用者的 cookie
二、Cookie Secure flag
1.Cookie Secure flag 的概念及優點
Cookie Secure,是設定COOKIE時,可以設定的一個屬性,設定了這個屬性後,只有在https訪問時,瀏覽器才會傳送該COOKIE給伺服器。
瀏覽器預設只要使用http請求一個站點,就會發送明文cookie,如果網路中有監控,可能被截獲。
如果web應用網站全站是https的,可以設定cookie加上Secure屬性,這樣瀏覽器就只會在https訪問時,傳送cookie,這樣攻擊者即使竊聽網路,也無法獲取使用者明文cookie
2.攻擊示例
設定cookie的程式碼
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly");
這段程式碼沒有設定Secure屬性,如果此時進行網路監聽,就可以看到沒有設定Secure屬性的COOKIE傳送的資料包。
3.解決方案
在設定認證COOKIE時,加入Secure 屬性。
程式碼:
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly ; Secure ");
此時如果再次訪問http網站,抓資料包可以看到,已經不再發送這個COOKIE了。
三、Session Expires
1.Session Expires 的原理及危害
Session Expires,Session有效期安全攻擊(會話固定漏洞)。
由於Session沒有在web應用中設定強制超時時間,攻擊者一旦曾經獲取過使用者的Session,就可以一直使用,從而可以偽造使用者的身份做任何事。
2.攻擊示例
設定cookie的程式碼
response.setHeader("SET-COOKIE", "user=" + request.getParameter("cookie") + "; HttpOnly ; Secure ");
這段程式碼沒有在伺服器中設定強制超時時間。利用網路監聽或者xss等一些手段獲取使用者的cookie,之後就可以一直使用使用者身份登入。
3.解決方案
在設定認證cookie中,加入兩個時間,一個是“即使一直在活動,也要失效”的時間,一個是“長時間不活動的失效時間”。並在web應用中,首先判斷兩個時間是否已超時,再執行其他操作。
示例:
// 判斷會員的cookie是否過期 if (isLogin) { String timeStampStr = (String) map.get(UserAuthenticationContext.TIMESTAMP); long loginTime = 0; try { loginTime = Long.parseLong(timeStampStr); } catch (NumberFormatException e) { if (logger.isInfoEnabled()) { logger.info(" loginId: " + usr.getLoginId() + " timestamp has exception " + timeStampStr); } } long now = System.currentTimeMillis() / 1000; if (now - loginTime > UserAuthenticationContext.COOKIE_VALIDITY) { usr.setAuthenticated(false, true); if (logger.isInfoEnabled()) { logger.info("loginId: " + usr.getLoginId() + " loginTime: " + loginTime + " nowTime: " + now); } } }
0X07 總結
實際上漏洞型別應該有更多,但是這裡我僅僅是列出了比較常見的或者說是比較 “大” 的漏洞,對於一些更細小的漏洞沒有闡述,這或許等過一段時間再回來補充。