[racket] Escaping things in templates?
[back to the list...]
Thanks for the clarification, Jay. xexpr->string is much cleaner.
What bothers me most is that I didn't even know this was
something to watch out for. I suspect new users are similarly
likely to be bitten, especially if they're coming from other
frameworks that protect them from potential issues like this.
(This is a testimonial, not a criticism.)
Here's a first stab at some potential docs for templates.scrbl.
(Sorry if it doesn't work; I've never written any scribble before
and am not sure how to test it without rebuilding all of racket):
- - - - - - - - - - - - - - - - - - - - - - - - -
@section{Escaping}
Because templates are useful for many things (scripts, CSS, HTML,
etc), the web server does not know what kind of escaping to apply to
user-supplied input. This means that when templates are expanded, no
escaping is done by default. Beware of @emph{cross-site scripting}
vulnerabilities! For example, suppose a servlet serves the following
template where @racket[some-variable] is an input string supplied by
the client:
@verbatim[#:indent 2]{
<html>
<head><title>Fastest Templates in the West!</title></head>
<body>
@some-variable
</body>
</html>
}
If the server contains something like the following:
@racketblock{
(let ([some-variable (get-input-from-user)])
(include-template "static.htm"))
}
There is nothing to prevent an attacker from entering
@litchar["<script type=\"text/javascript\">...</script>"] to make the
template expand into:
@verbatim[#:indent 2]{
<html>
<head><title>Fastest Templates in the West!</title></head>
<body>
<script type="text/javascript">...</script>
</body>
</html>
}
Now the server will send the attacker's code to millions of innocent
users. To keep this from happening when serving HTML, use the
@xexpr->string function from the @xml module.
This can be done in the server logic:
@racketblock{
(require xml)
(let ([some-variable (xexpr->string (get-input-from-user))])
(include-template "static.htm"))
}
Alternatively, make the template responsible for its own escaping:
@verbatim[#:indent 2]{
<html>
<head><title>Fastest Templates in the West!</title></head>
<body>
@(xexpr->string some-variable)
</body>
</html>
}
The improved version renders as:
@verbatim[#:indent 2]{
<html>
<head><title>Fastest Templates in the West!</title></head>
<body>
<script type=\"text/javascript\">...</script>
</body>
</html>
}
When writing templates, always remember to escape user-supplied input.
- - - - - - - - - - - - - - - - - - - - - - - - -
3 hours ago, Jay McCarthy wrote:
> web-server/template and Scribble produce strings, that's why you
> create a response by plopping in the string from include-template. The
> Web Server has no idea what kind of string you making, so it imposes
> no special XML rules.
>
> The template-less version your servlet does exactly what you seem to want:
>
> #lang web-server/insta
>
> (require web-server/templates)
>
> (define (start req)
> (let ([some-input
> (if (exists-binding? 'some-input (request-bindings req))
> (extract-binding/single 'some-input (request-bindings req))
> "nothing yet")])
> (response/xexpr
> `(html
> (head (title "Testing"))
> (body
> (h1 "You said:" ,some-input)
> (form ([method "get"] [action "#"])
> (input ([name "some-input"] [type "text"]))
> (input ([type "submit"]))))))))
>
> But if you want to use templates to produce XML strings and get XML
> quoting, then you have to quote explicitly by treating your user's
> string as if it is a string Xexpr (the representation for XML in the
> Web Server):
>
> #lang web-server/insta
>
> (require web-server/templates
> xml)
>
> (define (start req)
> (let ([some-input
> (xexpr->string
> (if (exists-binding? 'some-input (request-bindings req))
> (extract-binding/single 'some-input (request-bindings req))
> "nothing yet"))])
> (response/full 200 #"Okay" (current-seconds) TEXT/HTML-MIME-TYPE
> empty
> (list (string->bytes/utf-8 (include-template
> "static.htm"))))))
>
> Notice the addition of the (require xml) and the single call to
> (xexpr->string ...) around the user's input.
>
> Jay
>
> On Sat, Oct 22, 2011 at 8:38 PM, Michael W <mwilber at uccs.edu> wrote:
> > Hello! Thanks for making racket! It's the coolest.
> >
> > The web server template documentation:
> > http://docs.racket-lang.org/web-server/templates.html has several
> > great examples of templates, but it doesn't mention whether
> > strings included in templates are entity-escaped XML or not.
> >
> > More seriously, searching for "escape" in the help desk doesn't
> > even mention anything useful. It looks like the only way of
> > creating templates that escape the variables included in them is
> > by adding (require xml/private/writer) to the top of your script
> > and saying @escape[foo escape-table] every single time you use a
> > variable in your templates! Surely there must be a better way?
> >
> > Am I doing it wrong? Take a look at this simple servlet, which
> > simply spits out what you type in a form:
> > https://gist.github.com/1306759 Try typing <u>test</u> for
> > example, or perhaps some javascript.
> >
> > Certain web frameworks try to implicitly protect you from these
> > kinds of mistakes. Take Django, for example.
> > https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs
> > The Django team are so upfront about their template system's
> > escaping behavior that it's literally the first thing they
> > mention in the first section of their documentation.
> >
> > Other template systems intentionally make it harder to include
> > unescaped content by using syntactic differentiation. Mustache,
> > for example, requires you to use triple braces like
> > {{{some-input}}} if you want to include some-input as unescaped.
> > https://github.com/janl/mustache.js/
> >
> > What's the best way of solving this? Obviously we can't change
> > scribble, but could we have, say, (include-template) add an
> > @escape[...] function to the namespace just before it evaluates
> > the template?
> >
> > If I'm missing something, please feel free to flame away. ;) If
> > not, I'd love to help fix this by writing a patch or some
> > documentation or something.
> >
> > Thanks again for such a nice framework.
> >
> > --
> > For the Future!
> > _mike
> > _________________________________________________
> > For list-related administrative tasks:
> > http://lists.racket-lang.org/listinfo/users
> >
>
--
Live long and prosper,
_mike