How can I prevent XSS attacks in a JSP/Servlet web application?
XSS can be prevented in JSP by using JSTL <c:out>
tag or fn:escapeXml()
EL function when (re)displaying user-controlled input. This includes request parameters, headers, cookies, URL, body, etc. Anything which you extract from the request object. Also the user-controlled input from previous requests which is stored in a database needs to be escaped during redisplaying.
For example:
<p><c:out value="${bean.userControlledValue}"></p>
<p><input name="foo" value="${fn:escapeXml(param.foo)}"></p>
This will escape characters which may malform the rendered HTML such as <
, >
, "
, '
and &
into HTML/XML entities such as <
, >
, "
, '
and &
.
Note that you don't need to escape them in the Java (Servlet) code, since they are harmless over there. Some may opt to escape them during request processing (as you do in Servlet or Filter) instead of response processing (as you do in JSP), but this way you may risk that the data unnecessarily get double-escaped (e.g. &
becomes &amp;
instead of &
and ultimately the enduser would see &
being presented), or that the DB-stored data becomes unportable (e.g. when exporting data to JSON, CSV, XLS, PDF, etc which doesn't require HTML-escaping at all). You'll also lose social control because you don't know anymore what the user has actually filled in. You'd as being a site admin really like to know which users/IPs are trying to perform XSS, so that you can easily track them and take actions accordingly. Escaping during request processing should only and only be used as latest resort when you really need to fix a train wreck of a badly developed legacy web application in the shortest time as possible. Still, you should ultimately rewrite your JSP files to become XSS-safe.
If you'd like to redisplay user-controlled input as HTML wherein you would like to allow only a specific subset of HTML tags like <b>
, <i>
, <u>
, etc, then you need to sanitize the input by a whitelist. You can use a HTML parser like Jsoup for this. But, much better is to introduce a human friendly markup language such as Markdown (also used here on Stack Overflow). Then you can use a Markdown parser like CommonMark for this. It has also builtin HTML sanitizing capabilities. See also Markdown or HTML.
Please note that the term "sanitizing" such as Jsoup/Markdown/Owasp does concerns a very different thing than the term "escaping" such as <c:out>
does. HTML sanitizers basically clean up a string containing possibly malicious HTML so that it can be used as safe HTML without the need to escape. I.e. when you actually intend to interpret user-controlled input literally as HTML, including tags like <div>
, <p>
, <img>
, etc. HTML escapers basically prevent them to be interpreted at all, so that they show as plain text. In other words, you don't at all need to beforehand sanitize any HTML which you already are going to escape nonetheless.
The only concern in the server side with regard to databases is SQL injection prevention. You need to make sure that you never string-concatenate user-controlled input straight in the SQL or JPQL query and that you're using parameterized queries all the way. In JDBC terms, this means that you should use PreparedStatement
instead of Statement
. In JPA terms, use Query
.
An alternative would be to migrate from JSP/Servlet to Java EE's MVC framework JSF. It has built-in XSS (and CSRF!) prevention all over place so that you don't need to manually fiddle with <c:out>
and friends. See also CSRF, XSS and SQL Injection attack prevention in JSF.