[défense xss] Comment le projet Springboot se défend contre les attaques XSS

Qu'est-ce que XSS

Le Cross-Site Scripting (attaque de script intersite), appelé XSS, est une attaque par injection de code. Les attaquants injectent des scripts malveillants dans le site Web cible afin qu'ils puissent s'exécuter sur le navigateur de l'utilisateur. À l'aide de ces scripts malveillants, les attaquants peuvent obtenir des informations sensibles des utilisateurs telles que les cookies, l'ID de session, etc., mettant ainsi en danger la sécurité des données.

Afin de le distinguer du CSS, la première lettre de l'attaque est remplacée par X, on l'appelle donc XSS.

L'essence de XSS est que le code malveillant n'est pas filtré ni mélangé au code normal du site Web ; le navigateur ne peut pas déterminer quels scripts sont dignes de confiance, ce qui entraîne l'exécution du script malveillant.

Puisqu'il est exécuté directement sur le terminal de l'utilisateur, le code malveillant peut obtenir directement les informations de l'utilisateur, ou utiliser ces informations pour usurper l'identité de l'utilisateur afin d'initier des requêtes définies par l'attaquant sur le site Web.

Dans certains cas, en raison de limitations de saisie, le script malveillant injecté est plus court. Cependant, des stratégies d'attaque plus complexes peuvent être mises en œuvre en introduisant des scripts externes et en les exécutant par le navigateur.

Une compréhension simple signifie que XSS signifie qu'un attaquant insère du code de script malveillant dans une page Web. Lorsqu'un utilisateur parcourt la page, le code de script intégré dans le Web sera analysé et exécuté, atteignant ainsi l'objectif d'attaquer l'utilisateur de manière malveillante. La foule d'attaque XSS est une attaque au niveau de l'utilisateur !

Classification des vulnérabilités XSS

XSS stocké

XSS stocké, par exemple, certains sites Web remplissent des codes de script dans le contenu du texte lors de l'édition et de la soumission d'informations personnelles ou de la publication d'articles, de commentaires, etc. S'il n'y a pas de filtrage ou si le filtrage n'est pas strict, alors ces codes seront stockés dans le serveur de base de données back-end et l'utilisateur accédera à la page.déclenchera l'exécution du code. Ce type de XSS est plus dangereux et peut facilement provoquer des vers et des vols de cookies.

 XSS réfléchi

Non persistant, vous devez inciter l'utilisateur à cliquer sur le lien pour déclencher le code XSS (il n'existe pas de page ni de contenu de ce type sur le serveur), et il est généralement facile d'apparaître sur la page de recherche

 DOM type XSS

Les paramètres d'URL construits n'ont pas besoin d'être envoyés au serveur, ce qui peut contourner WAF et éviter la détection du serveur. La vulnérabilité DOM-XSS est Document Objeet Modelune vulnérabilité basée sur le modèle d'objet de document (,DOM). DOM-XSS est déclenché en transmettant des paramètres via l'URL. En fait, il s'agit également d'un XSS réfléchi.

Exemple de code d'attaque

<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>

Recommandations de protection XSS

  • Restreindre la saisie utilisateur pour spécifier le type de valeur d'entrée. Par exemple, l'âge ne peut être qu'un entier et le nom peut être une combinaison alphanumérique.
  • Si l'épissage HTML est nécessaire, vous devez utiliser une bibliothèque d'échappement appropriée pour encoder les données en HTML.
  • Filtrez ou supprimez les balises HTML spéciales.
  • Balises qui filtrent les événements javascript.
  • Les données de rendu front-end sont séparées de la vue (bien sûr, le framework front-end actuel Vue dispose déjà de ce mécanisme)

 Comment défendre le projet Springboot

Ajouter des dépendances au fichier pom

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

Ajouter un filtre pour envelopper l'objet de requête

@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) {

            }
        };
    }


}

Ajoutez des annotations à la classe de démarrage pour analyser le filtre correspondant.

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

Article de référence

Série sur la sécurité frontale (1) : Comment prévenir les attaques XSS ? - Pépites

Attaque de cross-site scripting – XSS – Nuggets

Cette fois, comprenez bien les attaques XSS - Nuggets

Je suppose que tu aimes

Origine blog.csdn.net/run_boy_2022/article/details/131519000
conseillé
Classement