[racket] Escaping things in templates?

From: Jay McCarthy (jay.mccarthy at gmail.com)
Date: Mon Oct 24 08:50:50 EDT 2011

I have just pushed this change to the docs.

Jay

On Sun, Oct 23, 2011 at 12:45 AM, Michael W <mwilber at uccs.edu> wrote:
> [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>
>    &lt;script type=\"text/javascript\"&gt;...&lt;/script&gt;
>  </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
>



-- 
Jay McCarthy <jay at cs.byu.edu>
Assistant Professor / Brigham Young University
http://faculty.cs.byu.edu/~jay

"The glory of God is Intelligence" - D&C 93



Posted on the users mailing list.