Come impedire alle persone di eseguire XSS in Spring MVC?

Cosa devo fare per evitare XSS in Spring MVC? In questo momento sto solo mettendo tutti i posti in cui metto in output il testo utente nelle funzioni JSTL o fn:escapeXml() , ma questo sembra incline all’errore perché potrei perdere un posto.

C’è un modo semplice e sistematico per prevenire questo? Forse come un filtro o qualcosa del genere? Sto raccogliendo input specificando i parametri @RequestParam sui miei metodi di controller.

In spring puoi sfuggire all’html dalle pagine JSP generate dai tag

. Questo chiude molte strade per gli attacchi XSS e può essere fatto automaticamente in tre modi:

Per l’intera applicazione nel file web.xml :

  defaultHtmlEscape true  

Per tutti i moduli su una determinata pagina nel file stesso:

  

Per ogni modulo:

  

Prova XSSFilter .

Quando stai cercando di prevenire XSS, è importante pensare al contesto. Ad esempio, come e cosa sfuggire è molto diverso se si stanno scaricando dati all’interno di una variabile in uno snippet di javascript piuttosto che l’output di dati in un tag HTML o in un attributo HTML.

Ne ho un esempio qui: http://erlend.oftedal.no/blog/?blogid=91

Controlla anche il foglio dei trucchi di prevenzione XSS OWASP: http://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet

Quindi la risposta breve è, assicurarsi di sfuggire all’output come suggerito da Tendayi Mawushe, ma prestare particolare attenzione quando si stanno trasmettendo dati in attributi HTML o javascript.

Uso Hibernate Validator tramite @Valid per tutti gli oggetti di input (binding e @RequestBody json, vedere https://dzone.com/articles/spring-31-valid-requestbody ). So @org.hibernate.validator.constraints.SafeHtml è una buona soluzione per me.

Hibernate SafeHtmlValidator dipende da org.jsoup , quindi è necessario aggiungere altre dipendenze del progetto:

  org.jsoup jsoup 1.10.1  

Per User bean con campo

 @NotEmpty @SafeHtml protected String name; 

per il tentativo di aggiornamento con valore nel controller

 @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) public void update(@Valid @RequestBody User user, @PathVariable("id") int id) 

o

 @PostMapping public void createOrUpdate(@Valid User user) { 

viene generato BindException per l’associazione e MethodArgumentNotValidException per @RequestBody con il messaggio predefinito:

 name may have unsafe html content 

Il validatore funziona anche per il binding, come prima di persistere. Le app potrebbero essere testate su http://topjava.herokuapp.com/

Come stai raccogliendo l’input dell’utente in primo luogo? Questa domanda / risposta può essere d’aiuto se stai usando un FormController :

Spring: escape dell’ingresso quando si esegue il binding al comando

Controlla sempre manualmente i metodi, i tag che usi e assicurati che sfuggano sempre (una volta) alla fine. I framework hanno molti bug e differenze in questo aspetto.

Una panoramica: http://www.gablog.eu/online/node/91

Invece di fare affidamento solo su , dovrebbe essere usata anche una libreria antixss, che non solo codificherà ma disinfetterà anche script malevoli in input. Una delle migliori librerie disponibili è OWASP Antisamy , è altamente flessibile e può essere configurata (usando i file delle politiche xml) come da requisiti.

Ad esempio, se un’applicazione supporta solo l’input di testo, è ansible utilizzare la maggior parte dei file di criteri generici forniti da OWASP che sanifica e rimuove la maggior parte dei tag html. Allo stesso modo se l’applicazione supporta editor html (come tinymce) che necessitano di tutti i tipi di tag html, può essere usata una policy più flessibile come il file di policy di ebay

 **To avoid XSS security threat in spring application** 

la soluzione al problema XSS consiste nel filtrare tutti i campi di testo nel modulo al momento dell’invio del modulo.

  It needs XML entry in the web.xml file & two simple classs. java code :- The code for the first class named CrossScriptingFilter.java is : package com.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; public class CrossScriptingFilter implements Filter { private static Logger logger = Logger.getLogger(CrossScriptingFilter.class); private FilterConfig filterConfig; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void destroy() { this.filterConfig = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("Inlter CrossScriptingFilter ..............."); chain.doFilter(new RequestWrapper((HttpServletRequest) request), response); logger.info("Outlter CrossScriptingFilter ..............."); } } 

La seconda class del codice denominata RequestWrapper.java è:

pacchetto com.filter;

 import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.log4j.Logger; public final class RequestWrapper extends HttpServletRequestWrapper { private static Logger logger = Logger.getLogger(RequestWrapper.class); public RequestWrapper(HttpServletRequest servletRequest) { super(servletRequest); } public String[] getParameterValues(String parameter) { logger.info("InarameterValues .. parameter ......."); String[] values = super.getParameterValues(parameter); if (values == null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = cleanXSS(values[i]); } return encodedValues; } public String getParameter(String parameter) { logger.info("Inarameter .. parameter ......."); String value = super.getParameter(parameter); if (value == null) { return null; } logger.info("Inarameter RequestWrapper ........ value ......."); return cleanXSS(value); } public String getHeader(String name) { logger.info("Ineader .. parameter ......."); String value = super.getHeader(name); if (value == null) return null; logger.info("Ineader RequestWrapper ........... value ...."); return cleanXSS(value); } private String cleanXSS(String value) { // You'll need to remove the spaces from the html entities below logger.info("InnXSS RequestWrapper ..............." + value); //value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); //value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); //value = value.replaceAll("'", "& #39;"); value = value.replaceAll("eval\\((.*)\\)", ""); value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); value = value.replaceAll("(?i)", ""); value = value.replaceAll("(?i)< .*?javascript:.*?>.*?", ""); value = value.replaceAll("(?i)< .*?\\s+on.*?>.*?", ""); //value = value.replaceAll("", ""); logger.info("OutnXSS RequestWrapper ........ value ......." + value); return value; } 

L’unica cosa rimasta è la voce XML nel file web.xml:

   XSS XSS  com.filter.CrossScriptingFilter   XSS /*  

Il / * indica che per ogni richiesta effettuata dal browser, chiamerà la class CrossScriptingFilter. Che analizzerà tutti i componenti / elementi provenienti dalla richiesta e sostituirà tutti i tag javascript messi da hacker con una stringa vuota cioè