closure-builder
Version:
Simple Closure, Soy and JavaScript Build system
373 lines (290 loc) • 17 kB
Markdown
# Security
Many web applications suffer from [cross-site
scripting](http://en.wikipedia.org/wiki/Cross-site_scripting) (XSS)
vulnerabilities. XSS was number 3 in [OWASP's top 10 application security
risks](http://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project) in 2013.
`Closure Templates` has features to automatically prevent XSS in your application.
This page describes how to use `Closure Templates`'s security features. See the
[Security Reference](../reference/security) page for more details about how the
security features work.
[TOC]
## Autoescaping in `Closure Templates` {#autoescaping}
XSS vulnerabilities typically occur when dynamic text from an untrusted source
is embedded into an HTML document. To prevent these vulnerabilities, *escaping*
is used. Escaping is the process of converting text to be properly displayed in
its context, such as turning angle brackets into `<` and `>` so they are
not interpreted as tags.
`Closure Templates` automatically determines and performs the type of escaping
needed based on the context in the document where the value appears. For
example, a value that appears inside a `<style>` tag needs to be escaped
differently than a value that appears in a URI.
`Closure Templates`'s *autoescaping* ensures that every dynamic value is escaped in
a context-appropriate way.
## Anatomy of an XSS Hack (and its prevention) {#example}
Template systems make it easy to compose content from static HTML and dynamic
values. `Closure Templates`'s autoescaping makes it even easier by letting you use
the same values in many contexts without having to explicitly specify encoding.
### The hack
An enterprising hacker might try to sneak a malicious value into your template
to take it over via XSS. Perhaps using
```js
{ x: 'javascript:/*</style></script>/**//<script>1/(alert(1337))//</script>' }
```
Say it's passed into a naive template, like:
```soy
{template .foo autoescape="deprecated-contextual"}
<a href="{$x|noAutoescape}"
onclick="{$x|noAutoescape}"
>{$x|noAutoescape}</a>
<script>var x = '{$x|noAutoescape}'</script>
<style>
p {
font-family: "{$x|noAutoescape}";
background: url(/images?q={$x|noAutoescape});
left: {$x|noAutoescape}
}
</style>
{/template}
```
This attack succeeds. The above template produces:
```html
<a href="javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script>"
onclick="javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script>"
>javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script></a>
<script>var x = 'javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script></script>
<style>
p {
font-family: "javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script>";
background: url(/images?q=javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script>);
left: javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script>
}
</style>
```
The page now triggers 6 "1337" alerts, and a seventh if you click the link.
### The explanation
Let's take another look at that malicious input to figure out why the attack
succeeds:
* `javascript:` - At the beginning of a URL, this changes the rest of the
content into JavaScript. In a script statement, this is just an unused
label.
* `/*</style></script>/**/` - This breaks out of any `style` or `script`
element. If already in a script attribute value, this just looks like a
comment. It prematurely ends any unquoted attribute value and its containing
tag.
* `/<script>1/` - If outside a script, this starts a script tag with a useless
division. Inside a script, this is a self-contained regular expression
literal.
* `(alert(1337))` - If preceded by a regular expression literal, this tries to
call it, but only after executing the real malicious code, `alert(1337)`.
* `//</script>` - If inside a script tag, this closes it correctly. If inside
a `javascript:` URL attribute or event handler attribute, this is a harmless
comment.
### The prevention
Many of the pieces of that malicious input depend on being interpreted different
ways by different parts of a browser. Autoescaping defangs this and other
malicious inputs by choosing a single consistent meaning for a dynamic value,
and choosing an escaping scheme that makes sure the browser will interpret it
the same way.
So if we pass that same malicious input to an autoescaped template: (Note that
only the `|noAutoescape`'s have been removed.)
```soy
{template .foo autoescape="deprecated-contextual"}
<a href="{$x}"
onclick="{$x}"
>{$x}</a>
<script>var x = '{$x}'</script>
<style>
p {
font-family: "{$x}";
background: url(/images?q={$x});
left: {$x}
}
</style>
{/template}
```
We get a very different output; one that is altogether saner:
```html
<a href="#zSoyz"
onclick="'javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script>'"
>javascript:/*</style></script>/**/ /<script>1/(alert(1337))//</script></a>
<script>var x = 'javascript:/*\x3c/style\x3e\x3c/script\x3e/**/ /\x3cscript\x3e1/(alert(1337))//\x3c/script\x3e'</script>
<style>
p {
font-family: "javascript:/*\3c /style\3e \3c /script\3e /**/ /\3c script\3e 1/(alert(1337))//\3c /script\3e ";
background: url(/images?q=javascript%3A%2F%2A%3E%2Fstyle%3C%3E%2Fscript%3C%2F%2A%2A%2F%20%2F%3Escript%3C1%2F%28alert%281337%29%29%2F%2F%3E%2Fscript%3C);
left: zSoyz
}
</style>
```
* When `{$x}` appeared inside HTML text, we entity-encoded it (< → \<).
* When `{$x}` appeared inside a URL or as a CSS quantity, we rejected it
because it had a protocol `javascript:` that was not `http` or `https`, and
instead output a safe value `#zSoyz`. Had `{$x}` appeared in the query
portion of a URL, we would have percent-encoded it instead of rejecting it
outright (< → %3C).
* When `{$x}` appeared in JavaScript, we wrapped it in quotes (if not already
inside quotes) and escaped HTML special characters (< → \x3c).
* When `{$x}` appeared inside CSS quotes, we did something similar to
JavaScript, but using CSS escaping conventions (< → \3c).
The malicious output was defanged.
## Strict Autoescaping {#strict}
The most secure way to use `Closure Templates` is with strict autoescaping. Strict
templates are recursively guaranteed not to underescape the output. Every last
dynamic value is printed with the correct escaping technique.
The output of a strict template is not a plain string, but a `SanitizedContent`
object, which associates a *content kind* with the text. The content kind
represents how the content is intended to be used, and the type of escaping, if
any, that has already been applied to it. This information is particularly
important in cases where the output of one template is used as an input
parameter to another template.
For every dynamic value that appears in the output of a template, Closure
Templates identifies the *output context* at the point of use, determined by the
surrounding text.
These two factors (content kind and output context) determine what kind of
escaping is applied to the text. For example, if the text has already been
URI-escaped, and it's being used in a URI context, then there's no need to
escape it again. This prevents "double-escaping" of the text.
### Content Kinds {#content_kinds}
The different kinds of content are:
Content kind | Description | Example | Notes
-------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | -----
html | HTML markup | `<div>Hello!</div>` |
attributes | HTML attribute-value pairs | `class="foo" width="100%"` | Represents the combination of both attribute names and attribute values, and must include the quotation marks around the attribute value. If the template output is intended to be just an attribute value alone (the part inside the quotes) then use either the text or html content kind.
text | Plain text, not yet escaped | `Hello!` |
uri | URIs | `http://www.google.com/search?q=android` |
css | Stylesheet text | `.myClass{ color: red; display: block; }` |
js | JavaScript or JSON | `{"a": 1, "b": 2}` |
trusted_resource_uri | A URL which is under application control and from which script, CSS, and other resources that represent executable code can be fetched. | `https://www.google.com/test.js` | Currently Soy requires trusted_resource_uri for script srcs. In the future, this may apply to other kinds of resources, such as stylesheets.
The content kind isn't a compiler type; you won't get an error or warning if you
use a `text` kind in a `css` context or vice versa. Rather, the content kind is
an indication that the text is safe for a given context and therefore does not
need additional escaping.
For input values that are not `SanitizedContent` objects, a strict template
coerces the value to a `text` string, and then applies escaping based on the
context.
### Usage
Strict autoescaping is on by default for all templates. To disable strict
autoescaping, use `{template autoescape=""}` with one of the `deprecated-*`
values but note that this decreases security.
By default, the output of a strict template has kind `html`. If your template
produces a different kind of content, you must add `kind` attributes to your
template. For example, a strict template that produces a URI might look like
this:
```soy
{template .googleUri kind="uri"}
http://www.google.com/
{/template}
```
The `kind` attribute can be added to the following `Closure Templates` commands:
| Command | Notes |
| ----------- | --------------------------------------------------------- |
| template | Optional. Assumed to be kind="html" if omitted. |
| deltemplate | Optional. Assumed to be kind="html" if omitted. All |
: : matching delegates must have the same kind. :
| let | Required only for [block form let |
: : statements](../reference/let). :
| param | Required only for [block form param |
: : statements](../reference/calls#construct-values-to-pass). :
The following example illustrates the usage of the `kind` attribute:
```soy
{template .foo kind="text"}
// Block-form 'let' command, 'kind' is required.
{let $message kind="text"}
{msg}Hi, {$name}!{/msg}
{/let}
// Short form 'let', no 'kind' attribute.
{let $category: $categoryList[0] /}
{call .bar}
// Block-form 'param' command, kind is required.
{param attributes kind="attributes"}
title="{$message}"{sp}
onclick="foo('{$message}')"
{/param}
{param content kind="html"}
<b>{$message}</b>
{/param}
// Short-form 'param' command, no 'kind' attribute.
{param visible: true /}
{/call}
{/template}
```
Short-form commands don't need the `kind` attribute because they pass values
rather than constructing strings, and values keep whatever kind they already
have.
Strict autoescaping can be turned on for some templates and not for others, so
you do not need to change all your templates at once. However, it is a good idea
to eventually make all your templates use strict autoescaping.
### Passing parameters to strict templates {#passing_params}
For ordinary content that doesn't contain markup, you can just pass in the
string values as template parameters as before, and they will get escaped.
Soy treats `SafeHtml` and the other safe contract types (`SafeStyle`, `SafeUrl`,
etc.) as exempt from re-escaping and filtering.
## Deprecated Contextual Autoescaping
The template attribute `autoescape="deprecated-contextual"` causes
`Closure Templates` to use an older strategy for escaping templates. It largely
employs the same strategy as strict escaping defined above with the following
differences.
* All templates are implicitly `kind="html"` (and setting a `kind` is only
possible for strict templates).
* Instead of returning safe strings types, `deprecated-contextual` templates
only render strings.
* `deprecated-contextual` may only call other `deprecated-contextual`
templates from an HTML context and can only call strict templates from a
compatible context (for example, can only call a `kind="attributes"`
template from an HTML attribute context).
If these limitations are problematic please consider migrating to strict
autoescaping.
### How to migrate to strict
Migrating to strict is fairly straightfoward if a little tedious. You will need
to:
* Remove all uses of the `|noAutoescape` directive
* Delete all `autoescape="deprecated-contextual"` attributes
* Add explicit `kind="..."` attributes to `param` and `let` blocks and to
`templates` that aren't HTML (the default `kind` for templates)
* Fix HTML errors in templates. Strict templates enable
[html validation](../reference/html) by default. You will either need to fix
the errors or explicitly disable validation by setting `stricthtml="false"`
on your templates.
Once this is done, you will need to inspect your callsites into Soy.
* In JavaScript, your templates will now return `SafeHtml` objects instead of
Strings. The `SafeHtml` objects have a reasonable `toString` method so
depending on the use you may not need to do anything, but it may require
changes in callers to handle the new types.
* In `Java`, you will need to call `SoySauce.Renderer.setExpectedContentKind`
with the correct `ContentKind` at your call sites.
## Content Security Policy (CSP) {#content_security_policy}
{{product_name_templates}} supports [Content Security
Policy](http://www.w3.org/TR/CSP/) nonces. CSP nonces are a defense-in-depth
technique for restricting the execution of `<script>` and `<style>` blocks. With
CSP nonces, even if an attacker can inject scripts into your document, they will
be unable to execute unless they can also guess the CSP nonce. (See [this
article](https://blog.mozilla.org/security/2014/10/04/csp-for-the-web-we-have)
for a good overview.)
With CSP nonces in `Closure Templates`, autoescaped templates get `nonce="..."`
added to the following tags:
* `<script>`
* `<style>`
* `<link rel="import">`
* `<link rel="preload" as="script">`
* `<link rel="preload" as="style">`
For example:
```html
<script>...</script>
```
becomes
```soy
<script{if $csp_nonce} nonce="{$csp_nonce}"{/if}>...</script>
```
### Configuring CSP nonces
To configure CSP nonces with `Closure Templates`:
1. Configure your web server to compute nonces and send them in CSP response
headers.
2. Make the nonces computed in step 1 available to your templates.
Step 1 is outside the scope of this document. General considerations for nonces
include generating strong random numbers
([article](https://www.securecoding.cert.org/confluence/display/java/MSC02-J.+Generate+strong+random+numbers))
and not reusing nonces
([article](https://www.securecoding.cert.org/confluence/display/java/MSC59-J.+Limit+the+lifetime+of+sensitive+data)).
For step 2, render with an [injected data](../concepts/ij-data) bundle that
includes an `$csp_nonce` value that is a
[valid nonce](https://w3c.github.io/webappsec/specs/content-security-policy/#source-list-valid-nonces).