On Sep 15, 8:42 pm, Mike Samuel <
mikesam...@gmail.com> wrote:
> package exp/template/html, which makesHTMLtemplates robust against
> XSS, is feature complete.
I think exp/template/html is now usable.
I am still working to get some other security-weenies to bang on it so
I cannot call it solid.
I wanted to get some experience with it on a real app, so I reworked
godoc (
http://codereview.appspot.com/5133048/ ) to use it instead of
explicit escaping directives.
The first thing I did was to turn on auto-escaping when godoc is run
with -html:
t, err := template.New(name).Funcs(fmap).Parse(string(data))
+ if *html && err == nil {
+ t, err = htmltemplate.Escape(t)
+ }
then I removed all the escaping directives. For example, even after
{{with .Title}}
- <h1 id="generatedHeader">{{html .}}</h1>
+ <h1 id="generatedHeader">{{.}}</h1>
{{end}}
the page /<script>alert(1337)</script>.html still shows a properly
escaped error message:
<h1 id="generatedHeader">File <script>alert(1337)</
script>.html</h1>
so htmltemplate prevents at least one reflected XSS.
Then I wanted to make sure there was no over-escaping. I set up two
godoc, one unpatched and one patched, so I could curl a bunch of files
and diff the output the two produce. (See the codereview.appspot URL
for the exact command line.)
Godoc has one template, lib/godoc/godoc.html, that provides a common
header, footer, and sidebar. It interpolates Content from another
template execution.
<!-- Content is HTML-escaped elsewhere -->
- {{printf "%s" .Content}}
+ {{.Content}}
so I made sure to mark the output of templates used to generate HTML
as known-safe HTML.
+func applyHTMLTemplate(t *template.Template, name string, data
interface{}) htmltemplate.HTML {
+ return htmltemplate.HTML(applyTemplate(t, name, data))
+}
...
- contents := applyTemplate(packageHTML, "packageHTML", info)
+ contents := applyHTMLTemplate(packageHTML, "packageHTML", info)
servePage(w, title, subtitle, "", contents)
The type of content changed too
- Content []byte
+ Content htmltemplate.HTML
though if some callers pass text content and some pass known-safe
HTML, it could just as easily be
- Content []byte
+ Content interface{}
I then reworked some template functions that produce known safe HTML.
{{comment_html .Doc}} is used in the package docs and changed:
-func comment_htmlFunc(comment string) string {
+func comment_htmlFunc(comment string) htmltemplate.HTML {
var buf bytes.Buffer
// TODO(gri) Provide list of words (e.g. function parameters)
// to be emphasized by ToHTML.
doc.ToHTML(&buf, []byte(comment), nil) // does html-escaping
- return buf.String()
+ return htmltemplate.HTML(buf.String())
}
That left only minor differences. Escape elides comments to prevent
internal project details from leaking. For example, the patched godoc
produces this difference:
- <!-- The Table of Contents is automatically inserted in this <div>.
- Do not delete this <div>. -->
+
<div id="nav"></div>
which is not normally a problem, except that the common header uses
special comments to embed IE only styles.
<!--[if lt IE 8]>
<link rel="stylesheet" href="/doc/ie.css" type="text/css">
<![endif]-->
CL
http://codereview.appspot.com/4999042/ explains how
htmltemplate.HTML could also solve this problem, but I decided to fix
it using an auditable exemption:
-<!--[if lt IE 8]>
+{{noescape "<!--[if lt IE 8]>"}}
<link rel="stylesheet" href="/doc/ie.css" type="text/css">
-<![endif]-->
+{{noescape "<![endif]-->"}}
With autoescape on, you're secure by default, but noescape allows
exemptions to this policy that can easily be found and audited via
grep.