Enforcing good typography with Play Framework

Straight quotation marks and apostrophes (", ') are easy to type but typographically they aren’t so great:

Straight quotes come to us from the type­writer. In tra­di­tional print­ing, all quo­ta­tion marks were curly. But type­writer char­ac­ter sets were lim­ited by me­chan­i­cal con­straints and phys­i­cal space. By re­plac­ing the curly open­ing and clos­ing quotes with am­bidex­trous straight quotes, two slots be­came avail­able for other characters.

Word proces­sors are not lim­ited in this way. You can al­ways get curly quotes. Com­pared to straight quotes, curly quotes are more leg­i­ble on the page and match the other char­ac­ters bet­ter. There­fore, straight quotes should never, ever ap­pear in your documents.

The same applies to the web. We should prefer curly quotation marks (“—”, ‘—’) and apostrophes (’). Unfortunately, it can be easy to type the straight characters and forget to replace them with their curly equivalents. When using a text editor or an IDE, we can’t rely on the smart-quotes feature of a word processor to replace them for us automatically. We can improve the situation in a Play Framework project by writing a Specs2 unit specification to forbid the use of " and ' in the English messages file. This technique requires us to externalize our messages, but that’s not such a bad thing because doing so is a good idea anyway.

Here’s the unit specification:

Now our tests will fail if any of our messages contain straight quotes.

HTTP Security Headers

In this post I’ll discuss the HTTP headers we can use to improve a web site’s security and mitigate certain attack types. I’ll use this blag as my example.

First up we’ll look at X-Frame-Options. This header helps prevent clickjacking by indicating to a browser that it shouldn’t render the page in a frame (or an iframe or object). We’ll use the strictest setting: DENY. Here’s how to set it in .htaccess:

Next we’ll add two Internet Explorer-only headers: X-XSS-Protection and X-Content-Type-Options.

X-XSS-Protection helps mitigate Cross-site scripting (XSS) attacks. We’ll use the strictest setting again here: “1; block”. This will instruct IE to not even render the page if it detects an XSS attack.

X-Content-Type-Options only has one value“nosniff”which instructs IE not to sniff mime types, preventing attacks related to mime-sniffing. You should be fine to use this header unless you’re serving files with bad Content-Type headers (don’t do that).

Here’s what we need to add to .htaccess for these two headers:

Now we’ll take a look at HTTP Strict Transport Security (HSTS). HSTS is a way for the server to instruct the browser that it (the browser) should only communicate with the server over HTTPS. This helps avoid man-in-the-middle attacks over insecure HTTP. The first thing we need to do is ensure that we’re redirecting all HTTP connections to the equivalent HTTPS URL. Here’s one way to achieve that in .htaccess:

Now we can set the HSTS header. I’m using a max-age equivalent to 30 days.

We must not send this header over plain HTTP, so we add env=HTTPS.

Next we’ll implement the Content Security Policy (CSP) header. CSP helps mitigate XSS attacks by whitelisting the allowed sources of content such as scripts, styles and images. Using WordPress 3.9.1, Accessible Zen 1.1.3 and Crayon 2.6.5, this is as tight as I could make the CSP header for this blag. I’ve inserted line breaks for readability.

Some observations:

  • We block all objects (such as Flash) with object-src ‘none’. Good riddance.
  • We unfortunately have to allow inline scripts and styles using ‘unsafe-inline’ for both script-src and style-src.
  • We only allow https and data schemes, we don’t allow any insecure http.

Finally we’ll look at X-Permitted-Cross-Domain-Policies. Setting this header to “master-only” will instruct Flash and PDF files that they should only read the master crossdomain.xml file from the root of the website.

Here’s everything we’ve added to .htaccess so far:

With that .htaccess in place, here’s what the response to a HEAD request looks like:

Unfortunately, something in the WordPress backend uses eval, so we have to include the ‘unsafe-eval’ source in our CSP script-src directive. Fortunately, we can get away with only including ‘unsafe-eval’ in an .htaccess in the wp-admin directory. This setup allows eval in the WordPress backend but blocks it for frontend pages. The CSP header in wp-admin’s .htaccess adds ‘unsafe-eval’ but is otherwise identical to that in the root .htaccess:

And that’s it for security headers on danielnixon.org. With the unfortunate exception of those unsafe-inline and unsafe-eval sources in the CSP header, these six headers lock things down pretty well. It’s worth stressing that none of this is a replacement for writing secure code in the first place; think of these headers as just another layer of protection.

As an aside, Play Framework’s SecurityHeadersFilter provides all the above (except HSTS), with each header defaulting to a secure default value.

While we’re on the topic of HTTP headers and security, there are also some headers that should be removed. Server and X-Powered-By are two common headers that reveal information about a website that could be useful to attackers, so we should remove them if we can. Unfortunately, we’re stuck with the Server header on Apache. What we can do is shrink it down to simply “Apache”, omitting the Apache version, the OS, the OS version, etc.

You can test your own site’s security headers at https://securityheaders.com/. This site scores 90%, with that pesky Server: Apache header costing me 10%. Maybe you can beat me?

Play Framework: Adding a Lang Attribute

If we create a new Play Framework project

and then take a look at main.scala.html, we’ll see something like this.

Notice that the html element doesn’t have a lang attribute. That’s a problem.

WCAG 2.0 Success Criterion 3.1.1 says:

3.1.1 Language of Page: The default human language of each Web page can be programmatically determined. (Level A)

And technique H57: Using language attributes on the html element says:

The objective of this technique is to identify the default language of a document by providing the lang and/or xml:lang attribute on the html element.

Identifying the language of the document is important for a number of reasons:

  • It allows braille translation software to substitute control codes for accented characters, and insert control codes necessary to prevent erroneous creation of Grade 2 braille contractions.
  • Speech synthesizers that support multiple languages will be able to orient and adapt to the pronunciation and syntax that are specific to the language of the page, speaking the text in the appropriate accent with proper pronunciation.
  • Marking the language can benefit future developments in technology, for example users who are unable to translate between languages themselves will be able to use machines to translate unfamiliar languages.
  • Marking the language can also assist user agents in providing definitions using a dictionary.

To fix main.scala.html, all we have to do is add lang="en" to the html element. We use the lang attribute because it is an HTML5 document, and we use the ISO 639-1 language code for English (“en”) because the play-scala project uses English.

If your project uses only a single language, that’s all there is to it. It’s a shame Play’s templates don’t include the lang attribute by default.

If your project supports multiple languages, you can take advantage of Play’s i18n support to assign the right language code to the lang attribute.

If you have an implicit Request in the scope, it will provide an implicit Lang value corresponding to the preferred language extracted from the Accept-Language header and matching one of the application supported languages. You should add a Lang implicit parameter to your template like this: @()(implicit lang: Lang).

We need to make two changes:

  1. Add the implicit Lang parameter to main.scala.html, and
  2. use its language member to get the ISO language code for the lang attribute.

Here’s what the finished product looks like:

This approach will work even if you don’t support multiple languages. The only supported language by default is English, so regardless of what the request’s Accept-Language header contains, the template’s Lang parameter will be English. Once the template is rendered this will result in lang="en", as if we had hard-coded it in the first place. Take a look at application.config to see why this is the case:

In fact, doing it this way is probably superior to hard-coding lang="en" because it is DRYer. If your project ever evolves to support multiple languages, you need only change application.config and your lang attribute will be set to the right value for free.