44 * @email : whgojp@foxmail.com
55 * @Date : 2024/5/19 19:03
66 */
7- const vul1ReflectRaw = "// 原生漏洞场景, 未加任何过滤,Controller接口返回Json类型结果 \n" +
7+ const vul1ReflectRaw = "// 原生漏洞场景, 未加任何过滤,Controller接口返回JSON类型结果 \n" +
88 "public R vul1(String payload) {\n" +
99 " return R.ok(payload);\n" +
1010 "}\n" +
@@ -14,7 +14,7 @@ const vul1ReflectRaw = "// 原生漏洞场景,未加任何过滤,Controller接
1414 "// \"msg\": \"<script>alert(document.cookie)</script>\",\n" +
1515 "// \"code\": 0\n" +
1616 "// }\n" +
17- "// payload在json中是不会触发xss的 需要解析到页面中 \n" +
17+ "// JSON响应本身通常不会直接执行脚本;前端若把字段用innerHTML等方式写入页面,才会触发XSS \n" +
1818 "\n" +
1919 "// 原生漏洞场景,未加任何过滤,Controller接口返回String类型结果\n" +
2020 "public String vul2(String payload) {\n" +
@@ -35,7 +35,7 @@ const vul2ReflectContentType = "// Tomcat内置HttpServletResponse,Content-Typ
3535 " ...\n" +
3636 " }\n" +
3737 "}"
38- const safe1CheckUserInput = "// 对用户输入的数据进行验证和过滤,确保不包含恶意代码。使用白名单过滤,只允许特定类型的输入,如纯文本或指定格式的数据 \n" +
38+ const safe1CheckUserInput = "// 使用白名单限制输入格式,适合约束字段类型;最终仍需根据输出位置进行上下文编码 \n" +
3939 "// 前端校验代码\n" +
4040 "var whitelistRegex = /^[a-zA-Z0-9_\\s]+$/;\n" +
4141 "\n" +
@@ -55,19 +55,20 @@ const safe1CheckUserInput = "// 对用户输入的数据进行验证和过滤,
5555 "if (matcher.matches()){\n" +
5656 " return R.ok(payload);\n" +
5757 "}else return R.error(\"输入内容包含非法字符,请检查输入\");"
58- const safe2CSP = "// 内容安全策略(Content Security Policy)是一种由浏览器实施的安全机制,旨在减少和防范跨站脚本攻击(XSS)等安全威胁。它通过允许网站管理员定义哪些内容来源是可信任的,从而防止恶意内容的加载和执行 \n" +
58+ const safe2CSP = "// 内容安全策略(Content Security Policy)是浏览器实施的额外防护层,可降低恶意脚本加载和执行风险,但不能替代输出编码与安全DOM/模板用法 \n" +
5959 "// 前端Meta配置\n" +
6060 "<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'; script-src 'self' https://apis.example.com; style-src 'self' https://fonts.googleapis.com; img-src 'self' data: https://*.example.com;\">\n" +
6161 "\n" +
6262 "\n" +
6363 "// 后端Header配置\n" +
6464 "public String safe2(String payload,HttpServletResponse response) {\n" +
65- " response.setHeader(\"Content-Security-Policy\",\"default-src self\");\n" +
65+ " response.setHeader(\"Content-Security-Policy\", \"default-src 'self'; script-src 'self'\");\n" +
66+ " response.setHeader(\"Content-Security-Policy-Report-Only\", \"default-src 'self'; report-uri /xss/reflect/csp-report-endpoint\");\n" +
6667 " return payload;\n" +
6768 "}"
6869
69- const safe3EntityEscape = '// 特殊字符实体转义是一种将HTML中的特殊字符转换为预定义实体表示的过程 \n' +
70- '// 这种转义是为了确保在HTML页面中正确显示特定字符,同时避免它们被浏览器误解为HTML标签或JavaScript代码的一部分,从而导致页面结构混乱或安全漏洞 \n' +
70+ const safe3EntityEscape = '// HTML正文输出编码会将特殊字符转换为HTML实体,避免浏览器把不可信数据解析为标签或脚本 \n' +
71+ '// 注意:HTML属性、URL、JavaScript字符串、CSS等不同上下文需要使用不同的编码或白名单校验策略 \n' +
7172 'public R safe3(@ApiParam(String type, String payload) {\n' +
7273 ' String filterContented = "";\n' +
7374 ' switch (type){\n' +
@@ -87,7 +88,7 @@ const safe3EntityEscape = '// 特殊字符实体转义是一种将HTML中的特
8788 ' }\n' +
8889 '}'
8990
90- const safe4HttpOnly = "// HttpOnly是HTTP响应头属性,用于增强Web应用程序安全性。它防止客户端脚本访问(只能通过http/https协议访问)带有HttpOnly标记的 cookie,从而减少跨站点脚本攻击(XSS)的风险 \n" +
91+ const safe4HttpOnly = "// HttpOnly可以阻止客户端脚本直接读取带有该属性的Cookie,降低XSS窃取Cookie的影响,但不能修复XSS本身 \n" +
9192 "// 单个接口配置\n" +
9293 "public R safe4(String payload, HttpServletRequest request,HttpServletResponse response) {\n" +
9394 " Cookie cookie = request.getCookies()[ueditor];\n" +
@@ -118,7 +119,7 @@ const safe4HttpOnly = "// HttpOnly是HTTP响应头属性,用于增强Web应用
118119 " ...\n" +
119120 "}"
120121
121- const vul1StoreRaw = "// 原生漏洞场景, 未加任何过滤,将用户输入存储到数据库中 \n" +
122+ const vul1StoreRaw = "// 原生漏洞场景, 未加任何过滤,将用户输入和User-Agent持久化;后续页面不安全渲染时触发存储型XSS \n" +
122123 "// Controller层\n" +
123124 "public R vul(String payload,HttpServletRequest request) {\n" +
124125 " String ua = request.getHeader(\"User-Agent\");\n" +
@@ -139,7 +140,7 @@ const vul1StoreRaw = "// 原生漏洞场景,未加任何过滤,将用户输入
139140 " values (#{content,jdbcType=VARCHAR},#{ua,jdbcType=VARCHAR}, #{date,jdbcType=VARCHAR})\n" +
140141 "</insert>"
141142
142- const safe1StoreEntityEscape = "// 表格数据渲染\n" +
143+ const safe1StoreEntityEscape = "// 表格数据渲染:数据库仍保存原始值,输出到HTML页面前按HTML正文文本编码 \n" +
143144 "table.render({\n" +
144145 "\t...\n" +
145146 " cols: [\n" +
@@ -151,10 +152,13 @@ const safe1StoreEntityEscape = "// 表格数据渲染\n" +
151152 " return escapeHtml(d.ua); \n" +
152153 " }},\n" +
153154 " \t...\n" +
154- "// 方法一、HTML 实体转义函数 \n" +
155+ "// 方法一、HTML正文输出编码函数 \n" +
155156 "function escapeHtml(html) {\n" +
157+ " if (html === null || html === undefined) {\n" +
158+ " return '';\n" +
159+ " }\n" +
156160 " var text = document.createElement(\"textarea\");\n" +
157- " text.textContent = html;\n" +
161+ " text.textContent = String( html) ;\n" +
158162 " return text.innerHTML;\n" +
159163 "}\n" +
160164 "// 方法二、JavaScript的文本节点\n" +
@@ -210,11 +214,38 @@ const vul1DomRaw = "// 1. innerHTML XSS\n" +
210214 "});\n" +
211215 "form.on('submit(document-domain)', function(data) {\n" +
212216 " var payload = data.field.documentPayload;\n" +
213- " document.domain = payload; // 漏洞点 :直接修改document.domain \n" +
217+ " document.domain = payload; // 风险点 :直接修改document.domain会放宽同源边界或造成异常行为 \n" +
214218 " return false;\n" +
215219 "});"
216220
217- const vul1OtherUpload = "public String uploadFile(MultipartFile file, String suffix,String path) throws IOException {\n" +
221+ const safeDomCode = "// 1. 普通文本输出:使用textContent,不解析HTML\n" +
222+ "document.getElementById('safe-dom-result').textContent = userInput;\n" +
223+ "\n" +
224+ "// 2. URL跳转:校验协议白名单,拒绝javascript:、data:等危险协议\n" +
225+ "var url = new URL(userInput, window.location.origin);\n" +
226+ "var allowedProtocols = ['http:', 'https:'];\n" +
227+ "if (allowedProtocols.indexOf(url.protocol) === -1) {\n" +
228+ " throw new Error('dangerous protocol');\n" +
229+ "}\n" +
230+ "\n" +
231+ "// 3. 替代eval:使用命令白名单映射,而不是执行用户输入\n" +
232+ "var actions = {\n" +
233+ " showTime: function () { return new Date().toLocaleString(); },\n" +
234+ " showLocation: function () { return window.location.pathname; }\n" +
235+ "};\n" +
236+ "var action = actions[userInput];\n" +
237+ "if (action) {\n" +
238+ " action();\n" +
239+ "}\n" +
240+ "\n" +
241+ "// 4. DOM API:创建文本节点,不拼接HTML字符串\n" +
242+ "var node = document.createTextNode(userInput);\n" +
243+ "element.appendChild(node);\n" +
244+ "\n" +
245+ "// 如果业务必须展示富文本,应先使用白名单HTML净化库处理后再渲染\n"
246+
247+ const vul1OtherUpload = "// 上传可被浏览器或预览服务解析的HTML/SVG/XML/PDF等文件,后续访问文件时可能触发XSS或内容安全问题\n" +
248+ "public String uploadFile(MultipartFile file, String suffix,String path) throws IOException {\n" +
218249 " String uploadFolderPath = sysConstant.getUploadFolder();\n" +
219250 " try {\n" +
220251 " String fileName = +DateUtil.current() + \".\"+suffix;\n" +
@@ -230,7 +261,8 @@ const vul1OtherUpload = "public String uploadFile(MultipartFile file, String suf
230261 " }\n" +
231262 "}"
232263
233- const vul2OtherTemplate = "public String handleTemplateInjection(String payload,String type, Model model) {\n" +
264+ const vul2OtherTemplate = "// th:utext会把内容作为HTML渲染;th:text会进行HTML转义\n" +
265+ "public String handleTemplateInjection(String payload,String type, Model model) {\n" +
234266 " if (\"html\".equals(type)) {\n" +
235267 " model.addAttribute(\"html\", payload);\n" +
236268 " } else if (\"text\".equals(type)) {\n" +
0 commit comments