HttpServletRequestWrapper的应用(对所有请求进行处理 例如编码 去空格)

本文深入探讨了为何大多数程序员难以成为架构师,通过分析一个名为ParameterRequestWrapper的类,解释了参数请求封装与过滤过程,并提供了实际应用示例,揭示了架构师在细节处理上的独特视角。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

public class ParameterRequestWrapper extends HttpServletRequestWrapper {

    /**
     * 请求参数
     */
    private Map<String, String[]> params = new HashMap<String, String[]>();

    /**
     * 
     * 构造函数:
     *
     * @param request HttpServletRequest
     */
    public ParameterRequestWrapper(HttpServletRequest request) {

        // 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似
        super(request);
        // 将参数表,赋予给当前的Map以便于持有request中的参数
        this.params.putAll(request.getParameterMap());
    }

    /**
     * 
     * 构造函数:重载一个构造方法
     * 
     * @param request HttpServletRequest
     * @param extendParams Map<String, Object>
     */
    public ParameterRequestWrapper(HttpServletRequest request, Map<String, Object> extendParams) {

        this(request);
        // 这里将扩展参数写入参数表
        addAllParameters(extendParams);
    }

    /**
     * 
     * 功能描述:重写getParameter,代表参数从当前类中的map获取
     *
     * @author :
     * 创建日期 :2014年1月7日 下午4:54:53
     *
     * @param name 参数名称
     * @return
     *
     * 修改历史 :(修改人,修改时间,修改原因/内容)
     */
    @Override
    public String getParameter(String name) {

        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    /**
     * 
     * 功能描述:重写getParameter,代表参数从当前类中的map获取
     *
     * @author :
     * 创建日期 :2014年1月7日 下午4:55:14
     *
     * @param name 参数名称
     * @return
     *
     * 修改历史 :(修改人,修改时间,修改原因/内容)
     */
    @Override
    public String[] getParameterValues(String name) {

        return params.get(name);
    }

    /**
     * 
     * 功能描述:增加多个参数
     *
     * @author :
     * 创建日期 :2014年1月7日 下午4:54:20
     *
     * @param otherParams Map<String, Object>
     *
     * 修改历史 :(修改人,修改时间,修改原因/内容)
     */
    public void addAllParameters(Map<String, Object> otherParams) {

        for(Map.Entry<String, Object> entry : otherParams.entrySet()) {
            addParameter(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 
     * 功能描述:增加参数
     * 
     * @author :创建日期 :2014年1月7日 下午4:53:46
     * 
     * @param name 参数名称
     * @param value 参数值
     * 
     *            修改历史 :(修改人,修改时间,修改原因/内容)
     */
    public void addParameter(String name, Object value) {

        if (value != null) {
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[] { (String) value });
            } else {
                params.put(name, new String[] { String.valueOf(value) });
            }
        }
    }
    
    /**
     * 
     * 功能描述:重写getParameterMap,代表参数从当前类中的map获取
     * 
     * @author :创建日期 :2014年1月7日 下午4:53:46
     * 
     * @return Map<String, String[]>
     * 
     *            修改历史 :(修改人,修改时间,修改原因/内容)
     */
    @Override
    public Map<String, String[]> getParameterMap(){
        return params;
    }

}
public class ParameterRequestFilter implements Filter {

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest)req);
        // 获取请求参数
        Map<String, String[]> paramsMap = requestWrapper.getParameterMap();
        if(paramsMap != null && paramsMap.size() > 0){
            // 遍历请求参数Map
            for(Entry<String, String[]> entry:paramsMap.entrySet()){
                String[] values = entry.getValue();
                if(values != null && values.length > 0){
                    String[] trimValues = new String[values.length];
                    for(int index = 0; index < values.length; index++){
                        trimValues[index] = StringUtil.trim(values[index]);
                    }
                    entry.setValue(trimValues);
                }
                
            }
            
        }
        chain.doFilter(requestWrapper, res);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }

}


转载于:https://my.oschina.net/pingdy/blog/191929

public class XssFilter implements Filter { /** * 排除链接 */ public List<String> excludes = new ArrayList<>(); @Override public void init(FilterConfig filterConfig) throws ServletException { String tempExcludes = filterConfig.getInitParameter("excludes"); if (StringUtils.isNotEmpty(tempExcludes)) { String[] url = tempExcludes.split(","); for (int i = 0; url != null && i < url.length; i++) { excludes.add(url[i]); } } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (handleExcludeURL(req, resp)) { chain.doFilter(request, response); return; } XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); chain.doFilter(xssRequest, response); } private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) { String url = request.getServletPath(); String method = request.getMethod(); // GET DELETE 不过滤 if (method == null) { return true; } return StringUtils.matches(url, excludes); } @Override public void destroy() { } }/** * XSS过滤处理 * * @author ruoyi */ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { /** * @param request */ public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if (values != null) { int length = values.length; String[] escapseValues = new String[length]; for (int i = 0; i < length; i++) { // 防xss攻击和过滤前后空格 escapseValues[i] = EscapeUtil.clean(values[i]).trim(); } return escapseValues; } return super.getParameterValues(name); } } 这是若依框架的防止Xss攻击的代码 为什么我用appscan还是扫描出了这个问题:反射型跨站点脚本攻击 严重性: 高 CVSS 分数: 7.5 URL:: http://localhost/jmreport/view/881703033710092288 实体: [NUMERIC] (Parameter) 风险: 可能会窃取或操纵客户会话和 cookie,它们可能用于假冒合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行 事务 原因: 当攻击者向受害者的浏览器发送恶意代码(大多数情况下使用 JavaScript)时,会出现跨站脚本攻击 (XSS) 漏洞。易受攻击的 Web 应用程序可能会在不进行过滤或编码的情况下将不受信任的数据嵌入到输出中。这样,攻击者便可以向该应用程序注入恶意 脚本,并在响应中返回该脚本。然后,在受害者的浏览器上运行该脚本。 尤其是,未对用户输入或不受信任的数据正确执行危险字符的清理。 在反射型攻击中,攻击者诱骗最终用户将包含恶意代码的请求发送到易受攻击的 Web 服务器,然后该服务器将攻击反射回最终 用户的浏览器。 服务器直接从 HTTP 请求接收恶意数据,并将其反射回 HTTP 响应。发送恶意内容的最常见方法是将其作为参数添加到公开发布 或通过电子邮件直接发送给受害者的 URL 中。包含恶意脚本的 URL 构成了许多网络钓鱼方案的核心,被诱骗的受害者访问指向 易受攻击站点的 URL。然后,该站点将恶意内容反射回受害者,接着受害者的浏览器会执行该内容。 固定值: 查看危险字符注入的可能解决方案 差异: 参数 [NUMERIC] 从以下位置进行控制: 881703033710092288 至: 881703033710092288jy%22onerror%20=%22208+%7BvalueOf:alert%7D%22jy 推理: 测试结果似乎指示存在脆弱性,因为 Appscan 在响应中成功嵌入了脚本,在用户浏览器中装入页面时将执行该脚本。 怎么处理
最新发布
08-06
<think>我们讨论的是在HTTP请求流中实施存储型XSS过滤。存储型XSS是指恶意脚本被永久存储在目标服务器上(如数据库),当其他用户访问时,脚本从服务器加载执行。过滤需要在数据存储前进行。 解决方案思路: 1. 在数据进入持久层(如数据库)前进行过滤/转义。 2. 根据不同的数据类型(普通文本、富文本等)采取不同的策略。 3. 使用成熟的库进行XSS过滤,避免自己编写复杂的正则表达式。 以下是一个基于Java Servlet Filter的实现,同时考虑普通请求和JSON请求的过滤:</think>### HTTP 请求流中存储型 XSS 过滤实现 存储型 XSS 过滤需要在数据进入持久层(如数据库)前进行严格消毒。以下是完整的 HTTP 请求流过滤解决方案: #### 1. 多层级过滤架构设计 ```mermaid graph TD A[HTTP请求] --> B[网络层过滤] B --> C[应用层过滤] C --> D[业务层验证] D --> E[持久层消毒] E --> F[数据库存储] ``` #### 2. 核心过滤组件实现 ##### (1) 网络层过滤 (Servlet Filter) ```java public class XssPreFilter extends OncePerRequestFilter { private final AntPathMatcher pathMatcher = new AntPathMatcher(); private final Set<String> excludePatterns = Set.of("/static/**", "/public/**"); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // 排除静态资源 if (excludePatterns.stream().anyMatch( pattern -> pathMatcher.match(pattern, request.getRequestURI()))) { chain.doFilter(request, response); return; } // 包装请求进行XSS处理 chain.doFilter(new XssShieldRequestWrapper(request), response); } } ``` ##### (2) 请求包装器实现 ```java public class XssShieldRequestWrapper extends HttpServletRequestWrapper { private static final Pattern[] XSS_PATTERNS = { Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE), Pattern.compile("src[\r\n]*=[\r\n]*\\'(.*?)\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("</script>", Pattern.CASE_INSENSITIVE), Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE), Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE), Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL) }; public XssShieldRequestWrapper(HttpServletRequest request) { super(request); } @Override public String[] getParameterValues(String parameter) { return Arrays.stream(super.getParameterValues(parameter)) .map(this::sanitize) .toArray(String[]::new); } @Override public String getParameter(String parameter) { return sanitize(super.getParameter(parameter)); } @Override public String getHeader(String name) { return sanitize(super.getHeader(name)); } private String sanitize(String value) { if (value == null) return null; // 保留原始值用于日志 String original = value; // 应用XSS过滤规则 for (Pattern pattern : XSS_PATTERNS) { value = pattern.matcher(value).replaceAll(""); } // 记录可疑输入 if (!original.equals(value)) { SecurityLogger.logXssAttempt(original, value); } return value; } } ``` #### 3. JSON 请求体深度过滤 ```java public class JsonXssFilter extends OncePerRequestFilter { private final ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { if (isJsonRequest(request)) { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); String jsonBody = new String(wrappedRequest.getContentAsByteArray(), StandardCharsets.UTF_8); if (!jsonBody.isBlank()) { // 递归处理JSON所有字段 JsonNode root = objectMapper.readTree(jsonBody); sanitizeJsonNode(root); String sanitizedJson = objectMapper.writeValueAsString(root); wrappedRequest.setContent(sanitizedJson.getBytes()); } chain.doFilter(wrappedRequest, response); } else { chain.doFilter(request, response); } } private void sanitizeJsonNode(JsonNode node) { if (node.isObject()) { ObjectNode objectNode = (ObjectNode) node; Iterator<Map.Entry<String, JsonNode>> fields = objectNode.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> entry = fields.next(); if (entry.getValue().isTextual()) { // 对文本字段进行消毒 String sanitized = new XssShieldRequestWrapper(null) .sanitize(entry.getValue().textValue()); objectNode.put(entry.getKey(), sanitized); } else { sanitizeJsonNode(entry.getValue()); } } } else if (node.isArray()) { for (JsonNode element : node) { sanitizeJsonNode(element); } } } } ``` #### 4. 持久层二次消毒 (JPA 实体监听器) ```java @Entity public class User { @Id private Long id; @XssSafe // 自定义注解 private String username; private String bio; @PrePersist @PreUpdate public void sanitizeFields() { // 对标记字段进行最终消毒 this.username = XssUtils.deepSanitize(username); this.bio = XssUtils.deepSanitize(bio); } } public class XssUtils { public static String deepSanitize(String input) { if (input == null) return null; return input.replaceAll("[\"']", "") // 移除引号 .replaceAll("\\s+", " ") // 标准化空格 .replaceAll("<([a-z][a-z0-9]*)>", "") // 移除简单标签 .replaceAll("&#", ""); // 移除HTML实体开头 } } ``` ### 存储型 XSS 防御策略 1. **分层防御原则**: - 网络层:基础过滤和请求阻断 - 应用层:参数级精确消毒 - 业务层:上下文感知处理 - 持久层:最终值消毒 2. **内容敏感度分级**: ```java public enum ContentSecurityLevel { PLAIN_TEXT, // 普通文本:严格过滤 RICH_TEXT, // 富文本:使用JSoup消毒 CODE_SNIPPET, // 代码片段:仅允许安全字符 MARKDOWN // Markdown:限制HTML标签 } ``` 3. **富文本特殊处理**: ```java public class RichTextSanitizer { private static final Whitelist SAFE_WHITELIST = Whitelist.basic() .addTags("div", "span", "p", "br", "h1", "h2", "h3") .addAttributes("a", "href", "title") .addProtocols("a", "href", "http", "https"); public static String sanitize(String html) { return Jsoup.clean(html, SAFE_WHITELIST); } } ``` ### 监控与审计机制 ```java @Aspect @Component public class XssAuditAspect { @Before("execution(* com.example.repository.*.save(..)) && args(entity)") public void auditBeforeSave(Object entity) { // 检查实体字段是否包含可疑内容 Arrays.stream(entity.getClass().getDeclaredFields()) .filter(f -> f.getAnnotation(XssSafe.class) != null) .forEach(f -> { try { f.setAccessible(true); String value = (String) f.get(entity); if (XssDetector.isSuspicious(value)) { SecurityLogger.logSuspiciousData( entity.getClass().getSimpleName(), f.getName(), value ); } } catch (IllegalAccessException e) { // 处理异常 } }); } } ``` ### 最佳实践总结 1. **输入验证**:使用正则表达式验证格式(邮箱、电话等) 2. **输出编码**:在渲染时根据上下文进行HTML/JS/URL编码 3. **内容安全策略(CSP)**:设置严格的HTTP头 ``` Content-Security-Policy: default-src 'self'; script-src 'nonce-{random}' ``` 4. **安全编码**:避免使用 `innerHTML`,优先使用 `textContent` 5. **定期扫描**:使用OWASP ZAP进行自动化XSS检测 > **关键原则**:存储前消毒 + 输出时编码 + 严格CSP = 全面XSS防护
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值