[defensa xss] Cómo el proyecto springboot se defiende contra ataques XSS

¿Qué es XSS?

Cross-Site Scripting (ataque de secuencias de comandos entre sitios), conocido como XSS, es un ataque de inyección de código. Los atacantes inyectan scripts maliciosos en el sitio web de destino para que puedan ejecutarse en el navegador del usuario. Al utilizar estos scripts maliciosos, los atacantes pueden obtener información confidencial de los usuarios, como cookies, ID de sesión, etc., poniendo así en peligro la seguridad de los datos.

Para distinguirlo de CSS, la primera letra del ataque se cambia a X, por lo que se llama XSS.

La esencia de XSS es que el código malicioso no se filtra ni se mezcla con el código normal del sitio web; el navegador no puede determinar qué scripts son confiables, lo que provoca que se ejecute el script malicioso.

Dado que se ejecuta directamente en el terminal del usuario, el código malicioso puede obtener directamente la información del usuario o utilizar esta información para hacerse pasar por el usuario e iniciar solicitudes definidas por el atacante al sitio web.

En algunos casos, debido a limitaciones de entrada, el script malicioso inyectado es más corto. Sin embargo, se pueden completar estrategias de ataque más complejas introduciendo scripts externos y ejecutándolos mediante el navegador.

Una comprensión simple significa que XSS significa que un atacante inserta un código de script malicioso en una página web. Cuando un usuario navega por la página, el código de script incrustado en la web se analizará y ejecutará, logrando así el propósito de atacar maliciosamente al usuario. ¡La multitud de ataques XSS es un ataque a nivel de usuario!

Clasificación de vulnerabilidad XSS

XSS almacenado

XSS almacenado, por ejemplo, algunos sitios web completan códigos de script en el contenido del texto al editar y enviar información personal o publicar artículos, comentarios, etc. Si no hay filtrado o el filtrado no es estricto, estos códigos se almacenarán en el El servidor de base de datos back-end y el usuario accederán a la página y activarán la ejecución del código. Este tipo de XSS es más peligroso y puede provocar fácilmente gusanos y robo de cookies.

 XSS reflejado

No persistente, debe engañar al usuario para que haga clic en el enlace para activar el código XSS (no existe tal página ni contenido en el servidor) y, en general, es fácil de aparecer en la página de búsqueda.

 tipo DOM XSS

No es necesario enviar los parámetros de URL construidos al servidor, lo que puede omitir WAF y evitar la detección del servidor. La vulnerabilidad DOM-XSS es Document Objeet Modeluna vulnerabilidad basada en el modelo de objetos de documento (,DOM). DOM-XSS se activa al pasar parámetros a través de la URL. De hecho, también es un XSS reflejado.

Código de muestra de ataque

<html>
    <head>
        <title>DOM Based XSS Demo</title>
        <script>
        function xsstest()
        {
        var str = document.getElementById("input").value;
        document.getElementById("output").innerHTML = "<img
        src='"+str+"'></img>";
        }
        </script>
    </head>
    <body>
    <div id="output"></div>
    <input type="text" id="input" size=50 value="" />
    <input type="button" value="submit" onclick="xsstest()" />
    </body>
</html>

Recomendaciones de protección XSS

  • Restrinja la entrada del usuario para especificar el tipo de valor de entrada. Por ejemplo, la edad solo puede ser int y el nombre puede ser una combinación alfanumérica.
  • Si es necesario unir HTML, debe utilizar una biblioteca de escape adecuada para codificar los datos en HTML.
  • Filtre o elimine etiquetas html especiales.
  • Etiquetas que filtran eventos de JavaScript.
  • Los datos de representación del front-end están separados de la vista (por supuesto, el marco de front-end actual Vue ya tiene este mecanismo)

 Cómo defender el proyecto springboot

Agregar dependencias al archivo pom

      <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.18</version>
        </dependency>

Agregar filtro de filtro para envolver el objeto de solicitud

@Slf4j
@WebFilter(filterName = "xssFilter", urlPatterns = "/*")
public class XssFilter implements Filter {

    @Autowired
    private CsrfUrlConfig csrfUrlConfig;


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

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        String enctype = request.getContentType();
        String requestURI = request.getRequestURI();
      

        //todo 通过配置指定url来过滤不经过xss过滤
    
         /**
         * 如果请求为上传附件的话,则不用XssHttpServletRequestWrapper来包
         */
        if (StringUtils.isNotBlank(enctype) && enctype.contains("multipart/form-data")) {
            filterChain.doFilter(request, servletResponse);
        } else {
            try {
                XssHttpServletRequestWrapper xssRequestWapper = new XssHttpServletRequestWrapper(request);
             
                filterChain.doFilter(xssRequestWapper, servletResponse);
            } catch (Exception ex) {
                log.error("xssWrap包装过滤处理异常:{}", ex);
                ResultExtMessage resultMessage = new ResultExtMessage();
                resultMessage.setSuccess(false);
                resultMessage.setCode("402");
                resultMessage.setMessage(ex.getMessage());
                resultMessage.setErrorDetailMessage(ex.getMessage());
                resultMessage.setMessagesAsString(ex.getMessage());
                httpResponse.setCharacterEncoding("UTF-8");
                httpResponse.setContentType("application/json; charset=utf-8");
                final PrintWriter writer = httpResponse.getWriter();
                writer.write(JSON.toJSONString(resultMessage));
                writer.flush();
                writer.close();
            }

        }

    }

    @Override
    public void destroy() {

    }
}
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }


    /**
     * 过滤request.getParameter的参数
     */
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (!StrUtil.hasEmpty(value)) {
            value = StrUtil.trim(HtmlUtil.cleanHtmlTag(value));
        }
        return value;
    }


    /**
     * 过滤springmvc中的 @RequestParam 注解中的参数
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                String value = values[i];
                if (!StrUtil.hasEmpty(value)) {
                    value = StrUtil.trim(HtmlUtil.cleanHtmlTag(value));
                }
                values[i] = value;
            }
        }
        return values;
    }


    /**
     * 过滤from表单提交参数
     *
     * @return
     * @throws IOException
     */
    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> parameters = super.getParameterMap();
        LinkedHashMap<String, String[]> map = new LinkedHashMap();
        if (parameters != null) {
            for (String key : parameters.keySet()) {
                String[] values = parameters.get(key);
                for (int i = 0; i < values.length; i++) {
                    String value = values[i];
                    if (!StrUtil.hasEmpty(value)) {
                        value = StrUtil.trim(HtmlUtil.cleanHtmlTag(value));
                    }
                    values[i] = value;
                }
                map.put(key, values);
            }
        }
        return map;
    }


    /**
     * 过滤header头参数
     *
     * @return
     * @throws IOException
     */
    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (!StrUtil.hasEmpty(value)) {
            value = StrUtil.trim(HtmlUtil.cleanHtmlTag(value));
        }
        return value;
    }

    /**
     * 过滤json请求
     *
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        InputStream in = super.getInputStream();
        InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8"));
        BufferedReader buffer = new BufferedReader(reader);
        StringBuffer body = new StringBuffer();
        String line = buffer.readLine();
        while (line != null) {
            body.append(line);
            line = buffer.readLine();
        }
        buffer.close();
        reader.close();
        in.close();

        /**
         * 处理jsonArray的情况
         */
        String resultStr = null;
        //判断第一个字符是不是为[
        String bodyStr = body.toString();
        if (bodyStr.startsWith("[")) {
            List<Map<String, Object>> list = new ArrayList<>();
            JSONUtil.parseArray(bodyStr).stream().forEach(e -> {
                Map<String, Object> map = JSONUtil.parseObj(e);
                Map<String, Object> result = new LinkedHashMap<>();
                for (String key : map.keySet()) {
                    Object val = map.get(key);
                    if (val instanceof String) {
                        if (!StrUtil.hasEmpty(val.toString())) {
                            result.put(key, StrUtil.trim(HtmlUtil.cleanHtmlTag(val.toString())));
                        }
                    } else {
                        result.put(key, val);
                    }
                }
                list.add(result);
            });
            resultStr = JSONUtil.toJsonStr(list);
        } else {
            Map<String, Object> map = JSONUtil.parseObj(bodyStr);
            Map<String, Object> result = new LinkedHashMap<>();
            for (String key : map.keySet()) {
                Object val = map.get(key);
                if (val instanceof String) {
                    if (!StrUtil.hasEmpty(val.toString())) {
                        result.put(key, StrUtil.trim(HtmlUtil.cleanHtmlTag(val.toString())));
                    }
                } else {
                    result.put(key, val);
                }
            }
            resultStr = JSONUtil.toJsonStr(result);
        }
        ByteArrayInputStream bain = new ByteArrayInputStream(resultStr.getBytes());
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bain.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }


}

Agregue anotaciones a la clase de inicio para escanear el filtro correspondiente.

@ServletComponentScan(basePackages = {"com.xx.filter.xss"})

Artículo de referencia

Serie de seguridad front-end (1): ¿Cómo prevenir ataques XSS? - pepitas

Ataque de secuencias de comandos entre sitios - XSS - Nuggets

Esta vez, comprenda a fondo los ataques XSS: Nuggets

Supongo que te gusta

Origin blog.csdn.net/run_boy_2022/article/details/131519000
Recomendado
Clasificación