4.3 Cross site scripting

Today’s websites have much more dynamic content in order to improve user experience, which means that we must provide dynamic information depending on every individual’s behavior. Unfortunately, dynamic websites are susceptible to malicious attacks known as “Cross site scripting” (known as “XSS”). Static websites are not susceptible to Cross site scripting.

Attackers often inject malicious scripts like JavaScript, VBScript, ActiveX or Flash into those websites that have loopholes. Once they have successfully injected their scripts, user information can be stolen and your website can be flooded with spam. The attackers can also change user settings to whatever they want.

If you wish to prevent this kind of attack, you should combine the following two approaches:

  • Validation of all data from users, which we talked about in the previous section.
  • Carefully handle data that will be sent to clients in order to prevent any injected scripts from running on browsers.

So how can we do these two things in Go? Fortunately, the html/template package has some useful functions to escape data as follows:

  • func HTMLEscape(w io.Writer, b []byte) escapes b to w.
  • func HTMLEscapeString(s string) string returns a string after escaping from s.
  • func HTMLEscaper(args ...interface{}) string returns a string after escaping from multiple arguments.

Let’s change the example in section 4.1:

  1. fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // print at server side
  2. fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password")))
  3. template.HTMLEscape(w, []byte(r.Form.Get("username"))) // responded to clients

If someone tries to input the username as <script>alert()</script>, we will see the following content in the browser:

4.3. Cross site scripting - 图1

Figure 4.3 JavaScript after escaped

Functions in the html/template package help you to escape all HTML tags. What if you just want to print <script>alert()</script> to browsers? You should use text/template instead.

  1. import "text/template"
  2. ...
  3. t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
  4. err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")

Output:

  1. Hello, <script>alert('you have been pwned')</script>!

Or you can use the template.HTML type : Variable content will not be escaped if its type is template.HTML.

  1. import "html/template"
  2. ...
  3. t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
  4. err = t.ExecuteTemplate(out, "T", template.HTML("<script>alert('you have been pwned')</script>"))

Output:

  1. Hello, <script>alert('you have been pwned')</script>!

One more example of escaping:

  1. import "html/template"
  2. ...
  3. t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
  4. err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")

Output:

  1. Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!