CSRF, XSS and SQL Injection attack prevention in JSF
I have a web application built on JSF with MySQL as DB. I have already implemented the code to prevent CSRF in my application.
Now since my underlying framework is JSF, I guess I don't have to handle XSS attack as it is already handled by UIComponent
. I am not using any JavaScript in any of the view pages. Even if I use do I really need to implement code to prevent XSS attacks?
For DB we are using prepared statements and stored procedures in all DB interactions.
Is there anything else needs to be handled for preventing these 3 common attacks? I have already been through the OWASP site and their cheat sheets.
Do I need to take care of any other potential attack vectors?
XSS
JSF is designed to have builtin XSS prevention. You can safely redisplay all user-controlled input (request headers (including cookies!), request parameters (also the ones which are saved in DB!) and request bodies (uploaded text files, etc)) using any JSF component.
<h:outputText value="#{user.name}" />
<h:outputText value="#{user.name}" escape="true" />
<h:inputText value="#{user.name}" />
etc...
Note that when you're using JSF 2.0 on Facelets, then you can use EL in template text like so:
<p>Welcome, #{user.name}</p>
This will also implicitly be escaped. You don't necessarily need <h:outputText>
here.
Only when you're explicitly unescaping user-controlled input using escape="false"
:
<h:outputText value="#{user.name}" escape="false" />
then you've a potential XSS attack hole!
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. The HTML parser Jsoup is very helpful in this.
itemLabelEscaped
bug in Mojarra < 2.2.6
Older Mojarra versions before 2.2.6 had the bug wherein <f:selectItems itemLabel>
incorrectly renders the label unescaped when provided a List<T>
via <f:selectItems var>
instead of List<SelectItem>
or SelectItem[]
as value (issue 3143). In other words, if you're redisplaying user-controlled data as item labels via a List<T>
, then you've a potential XSS hole. If upgrading to at least Mojarra 2.2.6 is not an option, then you need to explicitly set itemLabelEscaped
attribute to true
to prevent that.
<f:selectItems value="#{bean.entities}" var="entity" itemValue="#{entity}"
itemLabel="#{entity.someUserControlledProperty}" itemLabelEscaped="true" />
CSRF
JSF 2.x has already builtin CSRF prevention in flavor of javax.faces.ViewState
hidden field in the form when using server side state saving. In JSF 1.x this value was namely pretty weak and too easy predictable (it was actually never intended as CSRF prevention). In JSF 2.0 this has been improved by using a long and strong autogenerated value instead of a rather predictable sequence value and thus making it a robust CSRF prevention.
In JSF 2.2 this is even be further improved by making it a required part of the JSF specification, along with a configurable AES key to encrypt the client side state, in case client side state saving is enabled. See also JSF spec issue 869 and Reusing ViewState value in other session (CSRF). New in JSF 2.2 is CSRF protection on GET requests by <protected-views>
.
Only when you're using stateless views as in <f:view transient="true">
, or there's somewhere a XSS attack hole in the application, then you've a potential CSRF attack hole.
SQL injection
This is not JSF's responsibility. How to prevent this depends on the persistence API you're using (raw JDBC, modern JPA or good ol' Hibernate), but all boils down that you should never concatenate user-controlled input into SQL strings like so
String sql = "SELECT * FROM user WHERE username = '" + username + "' AND password = md5(" + password + ")";
String jpql = "SELECT u FROM User u WHERE u.username = '" + username + "' AND u.password = md5('" + password + "')";
Imagine what would happen if the enduser chooses the following name:
x'; DROP TABLE user; --
You should always use parameterized queries where applicable.
String sql = "SELECT * FROM user WHERE username = ? AND password = md5(?)";
String jpql = "SELECT u FROM User u WHERE u.username = ?1 AND u.password = md5(?2)";
In plain JDBC you need to use PreparedStatement
to fill the parameter values and in JPA (and Hibernate), the Query
object offers setters for this as well.
I am not using any JavaScript in any of the view pages. Even if I use do I really need to implement code to bypass XSS Attack.
You can be vulnerable to XSS even if you don't use JavaScript in your pages. XSS occurs when you incorporate content controlled by an attacker without properly encoding it.
Anytime you do something like
response.write("<b>" + x + "</b>")
where an attacker can cause x
to contain HTML that contains JavaScript, then you are vulnerable to XSS.
The solution is usually not to write large amounts of code. Typically the solution is to encode $x
and any other values controlled by an attacker before including them in the HTML you generate.
response.write("<b>" + escapePlainTextToHtml(x) + "</b>")
Filtering or sanitizing inputs can help provide an additional layer of protection.
<shameless-plug>
You can also use a template language that encodes output automatically to protect against XSS.
Closure Template is one such option for Java.
Contextual autoescaping works by augmenting Closure Templates to properly encode each dynamic value based on the context in which it appears, thus defending against XSS vulnerabilities in values that are controlled by an attacker.
EDIT
Since you are using JSF you should read up on XSS mitigation in JSF:
Escape output text
<h:outputText/>
and<h:outputLabel/>
by default has the escape attribute set to True. By using this tag to display outputs, you are able to mitigate majority of the XSS vulnerability.SeamTextParser and
<s:formattedText/>
If you would like to allow users to utilise some of the basic html tags to customise their inputs, JBoss Seam provides a
<s:formattedText/>
tag that allows some basic html tags and styles specified by users.
When using <h:outputText escape="false">
with unescaped values (for example coming from html text editors) you're open for a nasty XSS attacks. In such cases I'm using a JSF converter which uses Jsoup to remove javascript from text leaving HTML intact. Converter can be used to sanitize user inputs as well. You can use it like this:
<h:outputText value="{bean.value}" escape="false" converter="htmlSanitizingConverter"/>
And the converter itself:
/**
* Prevents from XSS attack if output text is not escaped.
*/
@FacesConverter("htmlSanitizingConverter")
public class HtmlSanitizingConverter implements Converter {
private static final Whitelist JSOUP_WHITELIST = Whitelist.relaxed()
.preserveRelativeLinks(true)
.addAttributes(":all","style");
/*
Optionally - add support for hyperlinks and base64 encoded images.
.addTags("img")
.addAttributes("img", "height", "src", "width")
.addAttributes("a", "href")
.addProtocols("img", "src", "http", "https", "data");
*/
@Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
return (submittedValue != null) ? Jsoup.clean(submittedValue, JSOUP_WHITELIST) : null;
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value != null) ? Jsoup.clean(value.toString(), JSOUP_WHITELIST) : "";
}
}
Note:
When you're using JSF with PrimeFaces, beware of <p:textEditor>
- older versions (prior to 6.2) by default didn't sanitize user input.