
Java Frontend Audit
- 12 installs
- 1.6k repo stars
- Updated July 19, 2026
- wgpsec/aboutsecurity
Helps with frontend development tasks during AI-assisted development.
About
java-frontend-audit is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted coding.
- java-frontend-audit
- Frontend Development
- AI-coding skill
Java Frontend Audit by the numbers
- 12 all-time installs (skills.sh)
- Ranked #1,643 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wgpsec/aboutsecurity --skill java-frontend-auditAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 12 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | July 19, 2026 |
| Repository | wgpsec/aboutsecurity ↗ |
What it does
Helps with frontend development tasks during AI-assisted development.
Files
Java 前端安全类漏洞源码审计
本 skill 聚焦源码层面判断"前端安全漏洞是否成立",核心是验证用户输入在输出到 HTTP 响应(HTML/Header/Cookie/重定向)过程中的过滤与转义是否充分。构造 payload、绕 WAF 等运行时利用技术属于对应黑盒 exploit skill 范畴。
深入参考
- 5 类前端漏洞的危险模式 / 安全模式代码对比 / EVID 证据示例 -> references/frontend-vuln-patterns.md
---
5 类前端安全漏洞速查表
| 类型 | 典型 Sink | 危险模式 | 严重度 |
|---|---|---|---|
| XSS | JSP <%= %>, th:utext, ${var?no_esc}, out.println() | 用户输入未经转义直接输出到 HTML/JS 上下文 | High-Medium |
| CSRF | 状态变更 POST/PUT/DELETE 接口 | csrf().disable() 或缺少 Token 校验的写操作端点 | High-Medium |
| 开放重定向 | sendRedirect(), return "redirect:" + url | 用户可控 URL 直接传入重定向函数 | Medium |
| CRLF 注入 | setHeader(), addHeader(), addCookie() | 用户输入未过滤 \r\n 直接写入 HTTP 头 | Medium-High |
| Session 管理 | getSession(), Cookie 构造, session-timeout | 登录后未重建 Session / Cookie 缺少安全属性 | Medium-High |
通用审计流程(4 步)
Step 1 -- 确认 EVID 证据点: 从 java-audit-pipeline Phase 3 产出的 EVID_ 证据中,筛选前端安全类条目(EVID_XSS_、EVID_CSRF_、EVID_REDIR_、EVID_CRLF_、EVID_SESSION_)。没有 EVID 证据的 Sink 只能标"待验证"。
Step 2 -- 判断过滤有效性: 追踪 Source->Sink 路径上每一步过滤/转义操作,评估其对当前漏洞类型是否有效。常见陷阱: <c:out> 安全但 <%= %> 不转义、th:text 安全但 th:utext 不转义、URL 白名单可被 //evil.com 绕过。
Step 3 -- 评估绕过可能性: 过滤存在但不充分时,分析具体绕过路径(上下文切换、编码差异、URL 解析差异等)。能给出绕过思路则标"已确认",否则标"待验证"并记录已知过滤方式。
Step 4 -- 确定严重度: 使用 java-audit-pipeline 的三维度评分公式 Score = R*0.40 + I*0.35 + C*0.25。XSS 存储型 I=2-3,反射型 I=1-2;CSRF 取决于被保护操作的影响。
XSS 审计要点
- JSP 输出:
<%= request.getParameter("x") %>和<% out.println(input); %>均不转义,直接输出到 HTML 即反射型 XSS;安全写法:<c:out value="${param.x}"/>或${fn:escapeXml(param.x)},JSTL 默认转义 HTML 实体 - Thymeleaf:
th:utext="${userInput}"不转义(原始 HTML 输出)——危险;th:text="${userInput}"自动 HTML 转义——安全 - FreeMarker: 全局配置
output_format=HTMLOutputFormat+auto_escaping_policy=ENABLE_IF_SUPPORTED后默认转义;${var?no_esc}显式跳过转义——危险;未配置全局转义时所有${}均危险 - 上下文差异: HTML body 转义
< > & " '即可;HTML attribute 需额外处理引号闭合;JavaScript 上下文需 JS 编码而非 HTML 转义;URL 参数需 URL 编码——不同上下文使用错误的转义函数等于无防御 - DOM-based XSS: 后端返回 JSON 被前端
innerHTML/document.write()/eval()渲染,后端审计需确认 JSON 响应的Content-Type是否为application/json而非text/html - 存储型 XSS: 数据从数据库取出后未转义直接输出——追踪入库点是否做了过滤,出库点是否做了转义
- 富文本场景: 需使用白名单过滤库如 OWASP Java HTML Sanitizer (
PolicyFactory) 或Jsoup.clean(html, Safelist.basic())
CSRF 审计要点
- Spring Security 默认: CSRF Token 默认开启,但 REST API 项目常见
.csrf().disable()(旧版)或http.csrf(csrf -> csrf.disable())(Lambda DSL)——全局关闭后所有状态变更端点均暴露 - 自定义 Token: 评估生成强度(
SecureRandom安全 vsMath.random()可预测)、是否绑定 Session、校验时机(Controller 层 vs Filter 层) - Token 传递: 表单用
<input type="hidden" name="_csrf" value="..."/>;AJAX 用X-CSRF-TOKEN请求头配合<meta name="_csrf">读取——缺失任一传递方式则该场景无防护 - CORS 与 CSRF 的关系: 宽松 CORS(
Access-Control-Allow-Origin: *+Allow-Credentials: true)可被跨域 JS 读取响应中的 CSRF Token,间接绕过 CSRF 防护 - GET 请求状态变更: GET 执行写操作(如
/deleteUser?id=1)天然无 CSRF 防护——<img src="">即可触发
开放重定向审计要点
- Servlet 直接重定向:
response.sendRedirect(request.getParameter("url"))/response.sendRedirect(request.getParameter("next"))——用户完全可控 - Spring MVC:
return "redirect:" + target或RedirectView(url)中 target 来自请求参数 - 绕过手法:
//evil.com(协议相对 URL)、/\evil.com(部分解析器视\为路径分隔符)、URL 编码%2F%2Fevil.com、@符号http://trusted.com@evil.com、unicode 同形异义字 - 安全模式: 白名单域名校验 + 限制仅相对路径(以
/开头且不以//开头);使用UriComponentsBuilder解析后校验 host
CRLF 注入审计要点
- HTTP Header 注入:
response.setHeader("X-Custom", userInput)或response.addHeader("Location", "/path?lang=" + input)——注入\r\nSet-Cookie: admin=1可设置任意头 - Servlet 容器差异: Tomcat 7.0.67+ / Jetty 9.2.15+ 默认拒绝 Header 值中的
\r\n;旧版本或自定义 HTTP 框架(如 Netty 原始 API)可能无此保护——确认容器版本是关键 - Cookie 值注入:
new Cookie("lang", userInput)——注入\r\nSet-Cookie: session=evil可劫持 Session - 安全模式: 过滤或拒绝值中的
\r、\n、\0控制字符;升级到较新版本 Servlet 容器
Session 管理审计要点
- Session 固定: 登录前后 Session ID 不变——攻击者诱导受害者使用已知 Session ID;安全模式: 登录成功后
request.getSession().invalidate()+request.getSession(true)重建;Spring SecuritySessionFixationProtection.migrateSession默认启用 - Cookie 属性:
HttpOnly防 XSS 窃取 Cookie(cookie.setHttpOnly(true));Secure限制仅 HTTPS 传输(cookie.setSecure(true));SameSite=Lax/Strict防 CSRF(Servlet 4.0+ 或 Springserver.servlet.session.cookie.same-site) - 超时配置:
web.xml中<session-timeout>30</session-timeout>(分钟);Spring Bootserver.servlet.session.timeout=30m;过长超时增加 Session 劫持窗口 - 并发登录控制: Spring Security
sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true)——未配置时同一账户可多点登录,被盗凭据难以发现
检测清单
- [ ] 所有前端安全类 EVID_* 证据点已逐一审查
- [ ] JSP
<%= %>/out.println()输出点均已确认是否转义,Thymeleafth:utext已全部标记 - [ ] CSRF 保护状态已确认(是否 disable、Token 传递方式、GET 写操作)
- [ ] 重定向 Sink 的 URL 来源和白名单校验已确认
- [ ] HTTP Header/Cookie 写入点的用户输入过滤已检查,容器版本已记录
- [ ] Session 生命周期(固定/超时/并发/Cookie 属性)已审查
- [ ] 过滤不充分的点已给出具体绕过思路或标"待验证"
- [ ] 严重度评分使用了统一公式,与 pipeline 一致
Java 前端安全类漏洞审计模式参考
5 类前端安全漏洞的危险代码 / 安全代码对比 + EVID_* 证据格式示例。
---
1. XSS(跨站脚本)
1.1 JSP 反射型 XSS
<%-- 危险: 表达式/Scriptlet 直接输出 --%>
<p>欢迎, <%= request.getParameter("name") %></p>
<% out.println("搜索: " + request.getParameter("keyword")); %>
<%-- 安全: JSTL c:out / fn:escapeXml --%>
<p>欢迎, <c:out value="${param.name}"/></p>
<p>搜索: ${fn:escapeXml(param.keyword)}</p>审计关键: 全局搜索 <%= 和 out.print,逐一确认输出值来源。EL ${param.*} 在 JSP 2.0+ 默认转义,显式禁用时仍危险。
1.2 Thymeleaf XSS
<!-- 危险: th:utext 不转义 / [(${...})] 内联不转义 -->
<p th:utext="${userInput}">原始 HTML 输出</p>
<p>[(${userInput})]</p>
<!-- 安全: th:text 自动转义 / [[${...}]] 内联转义 -->
<p th:text="${userInput}">转义输出</p>
<p>[[${userInput}]]</p>1.3 FreeMarker XSS
<#-- 危险: 未配置全局转义时所有插值不转义 / 显式跳过 -->
<p>${username}</p>
<p>${content?no_esc}</p>
<#-- 安全: 全局配置 OutputFormat + 手动转义 -->
<#-- cfg.setOutputFormat(HTMLOutputFormat.INSTANCE) -->
<p>${username?html}</p>
<#ftl output_format="HTML">审计关键: 检查 Configuration 的 OutputFormat 和 AutoEscapingPolicy。搜索 ?no_esc 找显式跳过转义。
1.4 存储型 XSS 与 JSON 响应 XSS
// 存储型: 入库参数化安全但 XSS payload 原样入库,出库 <%= comment %> 不转义
// 安全: 出库用 <c:out> 或入库时 Jsoup.clean(input, Safelist.basic())
// JSON XSS: Content-Type 误配为 text/html → 浏览器按 HTML 解析
response.setContentType("text/html"); // 危险
response.setContentType("application/json;charset=UTF-8"); // 安全
// Spring @ResponseBody 默认 application/json(安全)
// 加 X-Content-Type-Options: nosniff 防 MIME 嗅探1.5 富文本安全过滤
// OWASP Java HTML Sanitizer
PolicyFactory policy = new HtmlPolicyBuilder()
.allowElements("p", "b", "i", "em", "strong", "a")
.allowAttributes("href").onElements("a").allowUrlProtocols("http", "https").toFactory();
String safe = policy.sanitize(userInput);
// Jsoup.clean() — Safelist.basic() 允许基本格式标签,禁止 script/img/事件属性
String safe = Jsoup.clean(userInput, Safelist.basic());1.6 XSS EVID 证据示例
[EVID_XSS_OUTPUT_POINT] views/user/profile.jsp:45 | <%= user.getBio() %>
[EVID_XSS_DATA_FLOW] UserController.java:89 → bio 来自数据库,入库无过滤
[EVID_XSS_CONTEXT] HTML body | 模板未使用 c:out → 存储型 XSS 已确认
[EVID_XSS_OUTPUT_POINT] templates/search.html:12 | th:utext="${keyword}"
[EVID_XSS_DATA_FLOW] SearchController.java:34 → keyword=request.getParameter("q")
[EVID_XSS_CONTEXT] HTML body | th:utext 不转义 → 反射型 XSS 已确认---
2. CSRF(跨站请求伪造)
2.1 Spring Security CSRF Token
// 默认启用: POST/PUT/DELETE/PATCH 需携带 Token
// 表单: <input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
// AJAX: X-CSRF-TOKEN 请求头 + <meta name="_csrf"> 读取2.2 CSRF 保护被关闭
// 危险: 全局关闭(旧版 DSL / Lambda DSL)
http.csrf().disable();
http.csrf(csrf -> csrf.disable());
// 危险: 忽略范围过大
http.csrf(csrf -> csrf.ignoringRequestMatchers("/api/**"));
// 安全: 仅忽略无状态回调 + CookieCsrfTokenRepository
http.csrf(csrf -> csrf
.ignoringRequestMatchers("/api/webhook/**")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()));审计关键: 搜索 csrf().disable() 和 csrf(csrf -> csrf.disable()),评估影响范围。
2.3 自定义 Token 与绕过场景
// 危险: Math.random() 可预测 / Token 未绑定 Session
// 安全: SecureRandom 32 字节 + 存入 Session + MessageDigest.isEqual() 校验
// 绕过 1: GET 执行状态变更(天然无 CSRF 保护)
@GetMapping("/deleteUser") // <img src="..."> 即可触发
public String delete(@RequestParam Long id) { userService.delete(id); }
// 绕过 2: <form enctype="text/plain"> 发送类 JSON body(Content-Type 绕过)
// 绕过 3: 宽松 CORS 可跨域读取 CSRF Token2.4 CSRF EVID 证据示例
[EVID_CSRF_GLOBAL_CONFIG] SecurityConfig.java:35 | http.csrf(csrf -> csrf.disable())
[EVID_CSRF_STATE_CHANGE_ENDPOINT] UserController.java:67 | @PostMapping("/updateProfile") 无 Token 校验
[EVID_CSRF_GET_STATE_CHANGE] AdminController.java:112 | @GetMapping("/admin/deleteLog") GET 执行删除---
3. 开放重定向
3.1 Servlet 与 Spring MVC 重定向
// 危险: 参数直接传入重定向
response.sendRedirect(request.getParameter("url"));
return "redirect:" + request.getParameter("target");
return new RedirectView(request.getParameter("url"));
return new ModelAndView("redirect:" + dest);
ResponseEntity.status(302).header("Location", userUrl).build();
// 危险: forward 到用户可控路径(信息泄露)
request.getRequestDispatcher(request.getParameter("page")).forward(request, response);
// page = "/WEB-INF/web.xml" → 读取配置3.2 绕过白名单
//evil.com 协议相对 URL
/\evil.com 反斜线被视为路径分隔符
%2F%2Fevil.com URL 编码绕过
http://trusted.com@evil.com @ 符号(trusted 变为 userinfo)
http://trusted.com.evil.com 子域名混淆
http://evil.com?trusted.com 查询参数混淆3.3 安全模式
// 白名单域名 + 相对路径限制
private static final Set<String> ALLOWED = Set.of("trusted.com", "sub.trusted.com");
public String safeRedirect(String url) {
if (url.startsWith("//") || url.startsWith("/\\")) return "/default";
if (url.startsWith("/") && !url.startsWith("//")) return url; // 相对路径
try {
String host = new URI(url).getHost();
if (host != null && ALLOWED.contains(host.toLowerCase())) return url;
} catch (URISyntaxException e) { /* 拒绝 */ }
return "/default";
}
// Spring: UriComponentsBuilder.fromUriString(url).build().getHost() 校验白名单3.4 开放重定向 EVID 证据示例
[EVID_REDIR_SINK] AuthController.java:89 | response.sendRedirect(redirectUrl)
[EVID_REDIR_PARAM] :85 | redirectUrl = request.getParameter("return_url") | 无白名单 → 已确认
[EVID_REDIR_SINK] LoginController.java:56 | return "redirect:" + next
[EVID_REDIR_PARAM] :52 | startsWith("/") 校验 → //evil.com 绕过 → 已确认---
4. CRLF 注入
4.1 HTTP Header 与 Cookie 注入
// 危险: Header 注入
response.setHeader("Content-Language", request.getParameter("lang"));
// lang = "zh\r\nSet-Cookie: admin=true" → 注入任意 Header
// 危险: Location Header → HTTP Response Splitting
response.addHeader("Location", "/app?lang=" + request.getParameter("path"));
// 注入 \r\n\r\n<script>... → 完整控制响应体
// 危险: Cookie 值注入
response.addCookie(new Cookie("theme", request.getParameter("theme")));
// 注入 \r\nSet-Cookie: session=hijacked → 劫持 Session4.2 Servlet 容器防护差异
| 容器 | 版本 | CRLF 防护 |
|---|---|---|
| Tomcat | 7.0.67+ / 8.0.30+ | Header 值含 \r\n 抛 IllegalArgumentException |
| Jetty | 9.2.15+ | 拒绝 CR/LF |
| Undertow | 较新版本 | 默认过滤 |
| Tomcat 6.x / Jetty 8.x | 旧版 | 无防护,可直接利用 |
| Netty / Vert.x | 所有版本 | 原始 API 无自动过滤 |
| Spring Boot 内嵌 | 取决于内嵌容器版本 | 检查 pom.xml 版本号 |
4.3 安全模式
// 过滤控制字符
String safe = value.replaceAll("[\\r\\n\\0]", "");
// 白名单校验
if (lang.matches("^[a-zA-Z\\-]{2,10}$")) response.setHeader("Content-Language", lang);
// URL 编码(适用于 Location)— URLEncoder 编码 \r\n 为 %0D%0A
response.sendRedirect("/app?lang=" + URLEncoder.encode(path, "UTF-8"));4.4 CRLF EVID 证据示例
[EVID_CRLF_HEADER_WRITE] ApiController.java:45 | response.setHeader("X-User", username)
[EVID_CRLF_CONTAINER] Tomcat 9.0.x → 内置防护 → 低风险
[EVID_CRLF_HEADER_WRITE] GatewayHandler.java:78 | ctx.response().putHeader("X-Trace", traceId)
[EVID_CRLF_CONTAINER] Vert.x 4.x (Netty) → 无防护 → CRLF 注入已确认---
5. Session 管理
5.1 Session 固定(Session Fixation)
// 危险: 登录后未重建 Session
if (authService.authenticate(user, pass)) {
request.getSession().setAttribute("user", user); // Session ID 不变
}
// 攻击: 诱导受害者携带已知 JSESSIONID 登录 → 共享 Session
// 安全: 销毁旧 Session + 创建新 Session
request.getSession().invalidate();
HttpSession s = request.getSession(true);
s.setAttribute("user", user);
// Spring Security 默认:
// sessionFixation().migrateSession() 迁移属性到新 Session(默认)
// sessionFixation().changeSessionId() Servlet 3.1+ 仅改 ID(推荐)
// sessionFixation().none() 不处理(危险!)5.2 Cookie 属性
// 危险: 未设置安全属性 → XSS 窃取 / HTTP 明文截获 / CSRF 携带
Cookie c = new Cookie("JSESSIONID", sid);
response.addCookie(c);
// 安全: 完整设置
c.setHttpOnly(true); c.setSecure(true); c.setPath("/"); c.setMaxAge(-1);
// SameSite(Servlet 4.0+ 不原生支持):
response.setHeader("Set-Cookie", "JSESSIONID=" + sid + "; Path=/; HttpOnly; Secure; SameSite=Lax");
// Spring Boot: server.servlet.session.cookie.same-site=lax
// Spring Session: DefaultCookieSerializer.setSameSite("Lax")5.3 超时与并发控制
<!-- web.xml 超时配置 -->
<session-config>
<session-timeout>30</session-timeout> <!-- 分钟。危险: 480/-1 -->
<tracking-mode>COOKIE</tracking-mode> <!-- 缺失则可能支持 URL 重写 ;jsessionid=xxx -->
</session-config>// Spring Boot: server.servlet.session.timeout=30m
// 编程方式: request.getSession().setMaxInactiveInterval(1800);
// Spring Security 并发控制
http.sessionManagement(s -> s.maximumSessions(1).maxSessionsPreventsLogin(false));
// 需注册 HttpSessionEventPublisher Bean 否则不生效
// 未配置 → 同一账户无限并发登录 → 凭据泄露难以发现5.4 Session EVID 证据示例
[EVID_SESSION_FIXATION] LoginServlet.java:78 | 登录后未 invalidate() → Session Fixation 已确认
[EVID_SESSION_COOKIE] web.xml 缺少 <cookie-config> | HttpOnly/Secure/SameSite 均未设置
[EVID_SESSION_TIMEOUT] web.xml:15 | <session-timeout>480</session-timeout> → 8 小时窗口过大
[EVID_SESSION_CONCURRENT] SecurityConfig.java 未配置 maximumSessions → 无并发控制
[EVID_SESSION_TRACKING] web.xml 未配置 <tracking-mode>COOKIE → 可能支持 URL 重写