Documentation

Security Guide

The Play framework is designed with security in mind – but it is impossible to prevent developers using the framework to mess up and open holes. The guide describes the common security issues in web applications and how to avoid them in Play applications.

Sessions

Often you need to keep information associated with a user, in particular the logged-in status. Without a session, the user would need to pass credentials with each request.

That’s what sessions are for: a set of cookies stored in the user’s browser that identify him or her to the web site, and provide other information your web application may choose to store there rather than in the data layer; for example the language.

Keep your secret… secret

The session is a hash of key/values, signed but not encrypted. That means that as long as your secret is safe, it is not possible for a third-party to forge sessions.

The secret is stored in conf/application.conf. It is very important to keep it private: do not commit it in a public repository, and when you install an application written by someone else change the secret key to your own. You can do that with the command play secret.

Don’t store critical data

However, since it is not encrypted, you should not store critical data in the session. It can be seen by looking at the user cookie, by sniffing the connection on a local network or over wifi.

The session is stored in a cookie, and cookies are limited to 4 KB. In addition to this limit, only strings can be stored.

Cross-Site Scripting

Cross-site scripting is one of the most common vulnerabilities in web applications. It consists of injecting malicious JavaScript into web pages using forms that your application provides.

Let’s say you’re writing a blog, and anyone can add a comment. If you blindly include what commenters have written into your HTML page, you’re opening your site to attacks. It can be:

Consequently it is very important to be protected from those attacks.

Play’s template engine automatically escapes strings. If you really need to insert unescaped HTML in your templates, you can do so using the raw() Java extension on the string. But if the string comes from a user input, you need to make sure it is sanitized first.

When sanitizing user inputs, always prefer whitelisting (only allow a list of safe tags) to blacklisting (forbid a list of unsafe tags and allow all the rest).

More on cross-site scripting

SQL Injection

SQL injection is an exploit consisting in using user input to execute a SQL query that was not intended by the developer. This can be used to destroy data, or to get access to data that shouldn’t be visible to the current user.

When you are using high level “find” methods, you should be covered against SQL injection. When you build your own queries manually, you need to be careful not to concatenate strings with + but use ? placeholders.

This is good:

createQuery("SELECT * from Stuff WHERE type= ?1").setParameter(1, theType);

This is NOT:

createQuery("SELECT * from Stuff WHERE type=" + theType;

Cross-Site Request Forgery

The CSRF attack can be really problematic in web applications:

This attack method works by including malicious code or a link in a page that accesses a web application that the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands.

To prevent this attack, the first thing to do is to use GET and POST methods appropriately. That means that only the POST method should be used to change the application’s state.

For POST requests the only way to secure critical actions properly is to issue an authenticity token. Play now has a built-in helper to handle that:

So for example:

public static destroyMyAccount() {
    checkAuthenticity();
    …
}

Will only work when called from a form including a proper authenticity token:

<form method="post" action="/account/destroy">
    #{authenticityToken /}
    <input type="submit" value="destroy my account">
</form>

For POST requests, Play’s form tag automatically generates the authenticity token:

#{form @destroyMyAccount()}
    <input type="submit" value="destroy my account">
#{/form}

You can of course add the checkAuthenticity() method call as a before filter if you want to protect all actions of a hierarchy of controllers.

More on cross-site request forgery

Continuing the discussion

Next: Play modules.