請求和響應在Web開發當中沒有語言之分不管是ASP、PHP、ASPX還是JAVAEE也好,Web服務的核心應該是一樣的。
在我看來Web開發最為核心也是最為基礎的東西就是Request和Response!我們的Web應用最終都是面向用戶的,而請求和響應完成了客戶端和服務器端的交互。 服務器的工作主要是圍繞著客戶端的請求與響應的。
如下圖我們通過Tamper data攔截請求后可以從請求頭中清晰的看到發出請求的客戶端請求的地址為:localhost。
瀏覽器為FireFox,操作系統為Win7等信息,這些是客戶端的請求行為,也就是Request。???
當客戶端發送一個Http請求到達服務器端之后,服務器端會接受到客戶端提交的請求信息(HttpServletRequest),然后進行處理并返回處理結(HttpServletResopnse)。
下圖演示了服務器接收到客戶端發送的請求頭里面包含的信息:??
??頁面輸出的內容為:
?host=localhost
?user-agent=Mozilla/5.0 (Windows NT 6.1; rv:18.0) Gecko/20100101 Firefox/18.0?
accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8?
accept-language=zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3?
accept-encoding=gzip, deflate?
connection=keep-alive
關于偽造問題我是這樣理解的:發送Http請求是客戶端的主動行為,服務器端通過ServerSocket監聽并按照Http協議去解析客戶端的請求行為。
所以請求頭當中的信息可能并不一定遵循標準Http協議。
用FireFox的Tamper Data和Moify?Headers(FireFox擴展中心搜Headers和Tamper Data都能找到) 插件修改下就實現了,請先安裝FireFox和Tamper Data:?????? 點擊Start Tamper 然后請求Web頁面,會發現請求已經被Tamper Data攔截下來了。選擇Tamper:
點擊Start Tamper 然后請求Web頁面,會發現請求已經被Tamper Data攔截下來了。選擇Tamper:
修改請求頭信息:??
?Servlet Request接受到的請求:
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();//獲取key
String value = request.getHeader(name);//得到對應的值
out.println(name + "=" + value + "<br>");//輸出如cookie=123
}?
源碼下載:http://pan.baidu.com/share/link?shareid=166499&uk=2332775740
使用Moify Headers自定義的修改Headers:
修改請求頭的作用是在某些業務邏輯下程序猿需要去記錄用戶的請求頭信息到數據庫,而通過偽造的請求頭一旦到了數據庫可能造成xss,或者在未到數據庫的時候就造成了SQL注入,因為對于程序員來說,大多數人認為一般從Headers里面取出來的數據是安全可靠的,可以放心的拼SQL(記得好像Discuz有這樣一個漏洞)。今年一月份的時候我發現xss.tw也有一個這樣的經典案例,Wdot那哥們在記錄用戶的請求頭信息的時候沒有去轉意特殊的腳本,導致我們通過偽造的請求頭直接存儲到數據庫。
XSS.tw平臺由于沒有對請求頭處理導致可以通過XSS屌絲逆襲高富黑。?剛回來的時候被隨風玩爆菊了。通過修改請求頭信息為XSS腳本,xss那平臺直接接收并信任參數,因為很少有人會蛋疼的去懷疑請求頭的信息,所以這里造成了存儲型的XSS。只要別人一登錄xss就會自動的執行我們的XSS代碼了。
Xss.tw由于ID很容易預測,所以很輕易的就能夠影響到所有用戶:
?
于是某一天就有了所有的xss.tw用戶被隨風那2貨全部彈了www.gov.cn: ?
代碼就不貼了,在發送請求的時候設置setRequestProperty 就行了,如:
URL realUrl = new URL(url);
URLConnection connection = realUrl.openConnection();
connection.setConnectTimeout(5000);//連接超時
connection.setReadTimeout(5000);// 讀取超時
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
(………………………..)
Test Servlet:
?
Session是存儲于服務器內存當中的會話,我們知道Http是無狀態協議,為了支持客戶端與服務器之間的交互,我們就需要通過不同的技術為交互存儲狀態,而這些不同的技術就是Cookie和Session了。
設置一個session:
session.setAttribute("name",name);//從請求中獲取用戶的name放到session當中
?session.setAttribute("ip",request.getRemoteAddr());//獲取用戶請求Ip地址
out.println("Session 設置成功.");
直接獲取session如下圖可以看到我們用FireFox和Chrome請求同一個URL得到的SessionId并不一樣,說明SessionId是唯一的。一旦Session在服務器端設置成功那么我們在此次回話當中就可以一直共享這個SessionId對應的session信息,而session是有有效期的,一般默認在20-30分鐘,你會看到xss平臺往往有一個功能叫keepSession,每過一段時間就帶著sessionId去請求一次,其實就是在保持session的有效不過期。
??1、session的默認過期時間是30分鐘,可修改的最大時間是1440分鐘(1440除以60=24小時=1天)。
?2、服務器重啟或關閉Session失效。
當我們關閉服務器時Tomcat會在安裝目錄workCatalinalocalhost項目名目錄下建立SESSIONS.ser文件。此文件就是Session在Tomcat停止的時候 持久化到硬盤中的文件. 所有當前訪問的用戶Session都存儲到此文件中. Tomcat啟動成功后.SESSIONS.ser??又會反序列化到內存中,所以啟動成功后此文件就消失了. 所以正常情況下 從啟Tomcat用戶是不需要登錄的. 注意有個前提,就是存儲到Session里面的user對象所對應的User類必須要序列化才可以。(摘自:http://alone-knight.iteye.com/blog/1611112)
??我們不妨來做一個偷取sessionId的實驗:?首先訪問:http://localhost/Test/SessionTest?action=setSession&name=selina 完成session的創建,如何建立就不解釋了如上所述。?
同時開啟FireFox和Chrome瀏覽器設置兩個Session:??????
我們來看下當前用戶的請求頭分別是怎樣的:????
??我們依舊用TamperData來修改請求的Cookie當中的jsessionId,下面是見證奇跡的時刻:???
我要說的話都已經在圖片當中的文字注釋里面了,偉大的Xss黑客們看明白了嗎?你盜取的也許是jsessionId(Java里面叫jsessionId),而不只是cookie。那么假設我們的Session被設置得特別長那么這個SessionId就會長時間的保留,而為Xss攻擊提供了得天獨厚的條件。而這種Session長期存在會浪費服務器的內存也會導致:SessionFixation攻擊!??
??1、用戶輸入正確的憑據,系統驗證用戶并完成登錄,并建立新的會話ID。?
2、Session會話加Ip控制?
3、加強程序員的防范意識:寫出明顯xss的程序員記過一次,寫出隱晦的xss的程序員警告教育一次,連續查出存在3個及其以上xss的程序員理解解除勞動合同(哈哈,開玩笑了)。
Cookie是以文件形式[緩存在客戶端]的憑證(精簡下為了通俗易懂),cookie的生命周期主要在于服務器給設置的有效時間。如果不設置過期時間,則表示這個cookie生命周期為瀏覽器會話期間,只要關閉瀏覽器窗口,cookie就消失了。?這次我們以IE為例:
?我們來創建一個Cookie:??
if(!"".equals(name)){
? ? ? ? ?Cookie cookies = new Cookie("name",name);//把用戶名放到cookie
? ? ? ? ?cookies.setMaxAge(60*60*60*12*30) ;//設置cookie的有效期
? //? ? ?c1.setDomain(".ahack.net");//設置有效的域?
? ? ? ?response.addCookie(cookies);//把Cookie保存到客戶端
? ? ? ? ?out.println("當前登錄:"+name);
}else {
? ? ? ? ?out.println("用戶名不能為空!");?
}
有些大牛級別的程序員直接把帳號密碼明文存儲到客戶端的cookie里面去,不得不佩服其功力深厚啊。客戶端直接記事本打開就能看到自己的帳號密碼了。
?繼續讀取Cookie:
我想cookie以明文的形式存儲在客戶端我就不用解釋了吧?文件和數據擺在面前!?盜取cookie的最直接的方式就是xss,利用IE瀏覽器輸出當前站點的cookie:
javascript:document.write(document.cookie)? ? ?
??首先我們用FireFox創建cookie:??
然后TamperData修改Cookie:
一般來說直接把cookie發送給服務器服務器,程序員過度相信客戶端cookie值那么我們就可以在不用知道用戶名和密碼的情況下登錄后臺,甚至是cookie注入。jsessionid也會放到cookie里面,所以拿到了cookie對應的也拿到了jsessionid,拿到了jsessionid就拿到了對應的會話當中的所有信息,而如果那個jsessionid恰好是管理員的呢?
上面我們用
javascript:document.write(document.cookie)
通過document對象能夠拿到存儲于客戶端的cookie信息。
HttpOnly設置后再使用document.cookie去取cookie值就不行了。
通過添加HttpOnly以后會在原cookie后多出一個HttpOnly;
普通的cookie設置:
Cookie: jsessionid=AS348AF929FK219CKA9FK3B79870H;
加上HttpOnly后的Cookie:
Cookie: jsessionid=AS348AF929FK219CKA9FK3B79870H; HttpOnly;
(參考YearOfSecurityforJava)
在JAVAEE6的API里面已經有了直接設置HttpOnly的方法了: ?
API的對應說明:
大致的意思是:如果isHttpOnly被設置成true,那么cookie會被標識成HttpOnly.能夠在一定程度上解決跨站腳本攻擊。 ?
在servlet3.0開始才支持直接通過setHttpOnly設置,其實就算不是JavaEE6也可以在set Cookie的時候加上HttpOnly; 讓瀏覽器知道你的cookie需要以HttpOnly方式管理。而ng a 在新的Servlet當中不只是能夠通過手動的去setHttpOnly還可以通過在web.xml當中添加cookie-config(HttpOnly默認開啟,注意配置的是web-app_3_0.xsd):
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<session-config>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
還可以設置下session有效期(30分):
<session-timeout>30</session-timeout>
CSRF(Cross Site Request Forgery, 跨站域請求偽造)用戶請求偽造,以受害人的身份構造惡意請求。(經典解析參考:http://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/ )
在討論如何抵御 CSRF 之前,先要明確 CSRF 攻擊的對象,也就是要保護的對象。從以上的例子可知,CSRF 攻擊是黑客借助受害者的 cookie 騙取服務器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的內容。另外,對于服務器返回的結果,由于瀏覽器同源策略的限制,黑客也無法進行解析。因此,黑客無法從返回的結果中得到任何東西,他所能做的就是給服務器發送請求,以執行請求中所描述的命令,在服務器端直接改變數據的值,而非竊取服務器中的數據。所以,我們要保護的對象是那些可以直接產生數據改變的服務,而對于讀取數據的服務,則不需要進行 CSRF 的保護。比如銀行系統中轉賬的請求會直接改變賬戶的金額,會遭到 CSRF 攻擊,需要保護。而查詢余額是對金額的讀取操作,不會改變數據,CSRF 攻擊無法解析服務器返回的結果,無需保護。
對象:A:普通用戶,B:攻擊者
1、假設A已經登錄過xxx.com并且取得了合法的session,假設用戶中心地址為:http://xxx.com/ucenter/index.do
2、B想把A余額轉到自己的賬戶上,但是B不知道A的密碼,通過分析轉賬功能發現xxx.com網站存在CSRF攻擊漏洞和XSS漏洞。
3、B通過構建轉賬鏈接的URL如:http://xxx.com/ucenter/index.do?action=transfer&money=100000 &toUser=(B的帳號),因為A已經登錄了所以后端在驗證身份信息的時候肯定能取得A的信息。B可以通過xss或在其他站點構建這樣一個URL誘惑A去點擊或觸發Xss。一旦A用自己的合法身份去發送一個GET請求后A的100000元人民幣就轉到B賬戶去了。當然了在轉賬支付等操作時這種低級的安全問題一般都很少出現。
驗證 HTTP Referer 字段
在請求地址中添加 token 并驗證
在 HTTP 頭中自定義屬性并驗證
加驗證碼
(copy防御CSRF毫無意義,參考上面給的IBM專題的URL)
最常見的做法是加token,Java里面典型的做法是用filter:https://code.google.com/p/csrf-filter/(鏈接由plt提供,源碼上面的在:http://ahack.iteye.com/blog/1900708)
CSRF的介紹drops已有文章,可以參考:http://drops.wooyun.org/papers/155