XSS, 跨站腳本攻擊, 簡單來說, 就是非本站點的腳本被執行了。 關于XSS 的詳細介紹和防御參考:

[XSS(跨站腳本)攻擊與預防](https://blog.csdn.net/oscar999/article/details/127397397) 和 [跨站腳本攻擊(XSS)及防范措施](https://blog.csdn.net/oscar999/article/details/105369085)。
本篇介紹在Java 項目中如何快速修復XSS 漏洞。本篇使用的是黑名單的方式, 對于非法字符進行轉義。 黑名單的方式雖然不能完全的解決XSS的漏洞, 但是能有效的減輕攻擊, 對于使用類似Coverity等代碼靜態掃描攻擊掃描的漏洞, 修復之后就不會再報相關的警報了。
## 個別代碼處理
如果整個項目中僅有幾處被掃描出來存在XSS 攻擊漏洞, 以基于Spring 項目的請求方法為例,下面的代碼是存在XSS 漏洞的。
```
@RequestMapping("/unsafe")
public String unsafe(@RequestParam(required = false) String input) {
String s = "Hello";
s += input;
return s;
}
```
上面的代碼中, 如果 input 包含了 `<script>`的腳本語句的話, 則這個響應返回給請求方的瀏覽器端, 相關的腳本就有可能被執行。
解決方法就是對返回的字符串進行黑名單過濾, 該怎么處理字符串呢? 內置tomcat 的jar 檔- tomcat-embed-core-9.0.14.jar 提供了一個類: org.apache.tomcat.util.security.Escape , 該類中的htmlElementContent() 方法實現了對特殊字符轉義,仿造這個方法寫一個類似的htmlElementContent() , 完整代碼如下:
```
public class SecurityUtil {
/**
* XSS 防御, 黑名單, 將非法字符進行替換
* @param content
* @return
*/
public static String htmlElementContent(String content) {
if (content == null) {
return null;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < content.length(); i++) {
char c = content.charAt(i);
if (c == '<') {
sb.append("<");
} else if (c == '>') {
sb.append(">");
} else if (c == '\'') {
sb.append("'");
} else if (c == '&') {
sb.append("&");
} else if (c == '"') {
sb.append(""");
} else if (c == '/') {
sb.append("/");
} else {
sb.append(c);
}
}
return (sb.length() > content.length()) ? sb.toString() : content;
}
}
```
對接口方法進行改寫,響應的字符串通過htmlElementContent() 進行處理后則基本是安全的, 如下代碼:
```
@RequestMapping("/safe")
public String safe(@RequestParam(required = false) String input) {
String s = "Hello";
s += input;
return SecurityUtil.htmlElementContent(s);
}
```
## 過濾器統一處理
如果是Java Web項目中, 如果每一個請求處理方法都使用上面方式進行處理的話, 則需要處理的地方太多了, 比較好的方式是使用過濾器進行統一處理。
過濾器可以對請求和響應進行XSS 防御的處理, 比如定義一個XssFilter 如下:
```
/**??
*?@Title:?XssFilter.java
*?@Package?com.osxm.websecurity.xss
*?@Description:?TODO
*?@author?XM
*?@date?2023年1月8日?下午8:09:35
*?@Copyright:?2023
*?@version?V1.0??
*/
package com.osxm.websecurity.xss;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
?*?@ClassName?XssFilter
?*?@Description?TODO
?*?@author?XM
?*?@date?2023年1月8日
?*?
?*/
public class XssFilter {
FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new XssWrapper((HttpServletRequest) request), response);
}
}
```
在過濾器處理方法中, 使用XssWrapper對請求進行Xss 過濾, XssWrapper的內容如下:
```
/**??
*?@Title:?XssWrapper.java
*?@Package?com.osxm.websecurity.xss
*?@Description:?TODO
*?@author?XM
*?@date?2023年1月8日?下午8:10:49
*?@Copyright:?2023
*?@version?V1.0??
*/
package com.osxm.websecurity.xss;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
?*?@ClassName?XssWrapper
?*?@Description?TODO
?*?@author?XM
?*?@date?2023年1月8日
?*?
?*/
public class XssWrapper extends HttpServletRequestWrapper {
public static final String JSON_TYPE = "application/json";
public static final String CONTENT_TYPE = "Content-Type";
public static final String CHARSET = "UTF-8";
private String mBody;
HttpServletRequest originalRequest = null;
public XssWrapper(HttpServletRequest request) throws IOException {
super(request);
originalRequest = request;
setRequestBody(request.getInputStream());
}
/**
* 獲取最原始的request。已經被getInputStream()了。
*
* @return
*/
public HttpServletRequest getOrgRequest() {
return originalRequest;
}
/**
* 獲取最原始的request的靜態方法。已經被getInputStream()了。
*
* @return
*/
public static HttpServletRequest getOriginalRequest(HttpServletRequest req) {
if (req instanceof XssWrapper) {
return ((XssWrapper) req).getOrgRequest();
}
return req;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (StringUtils.isBlank(value)) {
return value;
}
return StringEscapeUtils.escapeHtml4(value);
}
@Override
public String getQueryString() {
return StringUtils.isBlank(super.getQueryString()) ? "" : StringEscapeUtils.escapeHtml4(super.getQueryString());
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (StringUtils.isBlank(value)) {
return value;
}
return StringEscapeUtils.escapeHtml4(value);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) {
return values;
}
for (int i = 0; i < values.length; i++) {
values[i] = StringEscapeUtils.escapeHtml4(values[i]);
}
return values;
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = new LinkedHashMap<String, String[]>();
Map<String, String[]> parameterMap = super.getParameterMap();
if (parameterMap == null) {
return super.getParameterMap();
}
for (String key : parameterMap.keySet()) {
String[] values = parameterMap.get(key);
if (values != null && values.length > 0) {
for (int i = 0; i < values.length; i++) {
values[i] = StringEscapeUtils.escapeHtml4(values[i]);
}
}
map.put(key, values);
}
return map;
}
private void setRequestBody(InputStream stream) throws JsonMappingException, JsonProcessingException {
String line = "";
StringBuilder body = new StringBuilder();
// 讀取POST提交的數據內容
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, Charset.forName(CHARSET)));
try {
while ((line = reader.readLine()) != null) {
body.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
mBody = body.toString();
if (StringUtils.isBlank(mBody)) {// 為空時,直接返回
return;
}
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> map = objectMapper.readValue(mBody, Map.class);
Map<String, Object> resultMap = new HashMap<>(map.size());
for (String key : map.keySet()) {
Object val = map.get(key);
if (map.get(key) instanceof String) {
resultMap.put(key, StringEscapeUtils.escapeHtml4(val.toString()));
} else {
resultMap.put(key, val);
}
}
mBody = objectMapper.writeValueAsString(resultMap);
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (!JSON_TYPE.equalsIgnoreCase(super.getHeader(CONTENT_TYPE))) {// 非json類型,直接返回
return super.getInputStream();
}
if (StringUtils.isBlank(mBody)) {// 為空時,直接返回
return super.getInputStream();
}
final ByteArrayInputStream bais = new ByteArrayInputStream(mBody.getBytes(CHARSET));
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
};
}
}
```
## 示例代碼項目
* [https://github.com/osxm/websecurity_ency/tree/main/src/main/java/com/osxm/websecurity/xss](https://github.com/osxm/websecurity_ency/tree/main/src/main/java/com/osxm/websecurity/xss)
*****
*****
https://www.cnblogs.com/wangsongbai/p/15065001.html
- 空白目錄
- 基本知識
- 數據與指令
- 漏洞掃描
- Coverity Scan
- 常見漏洞與處理措施
- Presentation Topic 1
- Preface
- 常見的軟件缺陷與風險
- 安全漏洞相關概念(CVE,CNA, CWE,CVSS,OWASP)
- Web 安全漏洞發生的原因
- XSS(跨站腳本)攻擊與預防
- CSRF-跨站點請求偽造
- SQL Inject
- 軟件弱點預防之 —— Filesystem path, filename, or URI manipulation - 操控文件系統路徑、文件名或 URI
- Improper Limitation of a Pathname to a Restricted Directory
- 點擊劫持漏洞 Clickjacking
- Java Web安全風險應對
- Concludes
- SQL注入風險與防范措施
- SQL注入類型
- Sample
- XSS - 跨站腳本攻擊
- 寬字節編碼引發的XSS血案
- HTML與JavaScript 的自解碼
- XSS Sample
- XSS 攻擊載荷
- Cross-Site Scripting: 跨站腳本攻擊 XSS
- XSS 與 CSRF
- 參考
- 解決示例
- CSRF-跨站請求偽造
- 基于Servlet 的Java Web項目的CSRF防御概念
- 基于JSP的Java Web項目的CSRF防御示例
- CSRF-跨站點請求偽造
- CSRF(跨站請求偽造)漏洞及解決方法
- Java Web應用CSRF防御攻略
- Spring Boot 項目使用Spring Security防護CSRF攻擊實戰
- 一次基于Coverity 掃描Spring Boot項目的CSRF弱點解決的探索之旅
- Coverity + CSRF
- Spring Boot CSRF攻擊防御
- 文件上傳漏洞
- 敏感信息泄露
- Filesystem path, filename, or URI manipulation -
- PATH_MANIPULATION+Coverity
- 防御方法
- Java漏洞及修復
- Java高風險弱點
- Java 防御XSS攻擊實戰與示例代碼
- Java防御路徑操作(Path Manipulation) 的正確姿勢
- 示例
- 示例2
- Java之路徑操縱解決的誤區
- 結合Coverity掃描Spring Boot項目進行Path Manipulation漏洞修復
- Spring Boot實戰項目之CSRF防御處理
- Java高風險弱點與修復之——SQL injection(SQL注入)
- Very weak password hashing (WEAK_PASSWORD_HASH)
- Insecure SSL/TLS: bad HostnameVerifier (BAD_CERT_VERIFICATION)
- 主機驗證示例1
- Resource Leak
- Java中風險弱點
- Java代碼弱點與修復之——Arguments in wrong order(參數順序錯誤)
- Java代碼弱點與修復之——ORM persistence error(對象關系映射持久錯誤)
- Java代碼弱點與修復之——Logically dead code-邏輯死代碼
- 示例1
- Java代碼弱點與修復之——URL manipulation(URL操縱)
- Java代碼弱點與修復之——Open redirect(開放式重定向)
- Spring項目Open Redirect漏洞解決
- Java代碼弱點與修復之——Dereference null return value(間接引用空返回值)
- Java代碼弱點與修復之——Dereference before null check 非空檢查前間接引用
- Java代碼弱點與修復之——Dereference after null check-空檢查后間接引用
- Java代碼弱點與修復之——Explicit null dereferenced(顯式空間接引用)
- Java非空判斷相關的弱點類型匯總與比較
- Java代碼弱點與修復之——Copy-paste error(復制粘貼錯誤)
- Java代碼弱點與修復之——Suspicious calls to generic collection methods
- Java代碼弱點與修復之——Repeated conditional test(重復的條件測試)
- Java代碼弱點與修復之——Masked Field(掩碼字段)
- Spring Boot項目之偽Masked Field弱點解決
- Java代碼弱點與修復之——STCAL: Static use of type Calendar or DateFormat
- Java代碼弱點與修復之——RC: Questionable use of reference equality rather than calling equals
- Java代碼弱點與修復之——Unintended regular expression(非期望的正則表達式)
- Java代碼弱點與修復之——LI: Unsynchronized Lazy Initialization
- Java代碼弱點與修復之——Risky cryptographic hashing function (RISKY_CRYPTO)
- 加密散列示例
- Java代碼弱點與修復之——INT: Suspicious integer expression
- NP: Null pointer dereference
- SA: Useless self-operation
- Unguarded read
- SWL: Sleep with lock held
- Use of freed resources
- Stray semicolon
- UG: Unsynchronized get method, synchronized set method (FB.UG_SYNC_SET_UNSYNC_GET)
- Identical code for different branches
- RANGE: Range checks
- Infinite Loop
- Missing authorization check
- Java低風險弱點
- Java代碼弱點與修復之——WMI: Inefficient Map Iterator(低效的Map迭代器)
- Java代碼弱點與修復之——Dead local store(本地變量存儲了閑置不用的對象)
- Java代碼弱點與修復之——BC: Bad casts of object references(錯誤的強制類型轉換)
- Java代碼弱點與修復之——'Constant' variable guards dead code
- Java代碼弱點與修復之——DE: Dropped or ignored exception(無視或忽略異常)
- Useless code - 無用的代碼
- Dm: Dubious method used
- 字節轉換
- Java代碼弱點與修復之——Se: Incorrect definition of Serializable class(可序列化類的定義不正確)
- FS: Format string problem
- IM: Questionable integer math
- Information exposure to log file
- Insecure HTTP firewall
- NS: Suspicious use of non-short-circuit boolean operator
- REC: RuntimeException capture
- Resource leak on an exceptional path
- RV: Bad use of return value
- SBSC: String concatenation in loop using + operator
- SIC: Inner class could be made static
- SS: Unread field should be static
- UC: Useless code
- Unnecessary call to org.hibernate.Session.get method
- Unused value
- UPM: Private method is never called
- UrF: Unread field
- UuF: Unused field
- UwF: Unwritten field
- Audit
- Non-constant SQL
- Log injection (LOG_INJECTION)
- 日志漏洞示例1
- 實際場景
- URL
- 模板
- Web漏洞及修復
- Web開發
- 客戶端請求地址
- Medium
- [Web缺陷與修復之]Property access or function call before check for null or undefined
- Bad use of null-like value
- Missing break in switch
- Logically dead code-JavaScript
- Identical code for different branches
- Expression with no effect
- Missing parentheses
- High
- Web之DOM-based cross-site scripting漏洞處理
- Summary
- Web基本知識
- 字符轉義
- 工具
- Java靜態分析工具之——SpogBugs
- FindBugs
- Synopsys Code Sight
- 使用Eclipse +SpotBugs 檢測代碼弱點