If you have done any web programming, chances are you have heard of or used templates at one point or another. Put simply, templates are basically text files that can be used to create dynamic content. For example, you might have a template for your website’s navigation bar, and part of the dynamic content might be whether to show a signin or logout button depending on whether the current user is logged in or not.
Go provides two pretty amazing templating libraries - text/template and html/template. How you use these templates is identical, but behind the scenes the html/template
package will do some encoding to help prevent code injection. The coolest part about this encoding is that it is contextual, meaning that it can happen inside of HTML, CSS, JavaScript, or even in URLs and the template library will determine how to properly encode the text.
What is code injection?
Code injection is a common exploit where users try to get your server or visitors of your page to execute code that they write and input into your application. This typically becomes an issue when you don’t process or escape input in order to make it invalid.
For example, if someone were to sign up for your web app with the username <script>alert("Hi!");</script>
and you simply inserted their username on their profile page this could lead to code injection. In this case, anytime someone visited that user’s profile page they would see a javascript alert with the text “hi” in it. This becomes dangerous because javascript is typically attached to a user’s session, so if you were to instead run some code that visits the /delete-my-account
page, it could delete the account of any user who visits the injected user’s profile page.
Go combats this by encoding text in html templates so that it can’t execute. For example, the username <script>alert("Hi!");</script>
would get encoded to be <script>alert("Hi!");</script>
which will render as the original username, but won’t be executed by anyone visiting the page as actual code.
Since both template libraries utilize the same interface, everything covered in this article could be used for either package, but most of the examples will be using the html/template
package to generate HTML snippets.
First lets go ahead and create a main function that executes a really simple template so we can see it in action. Open up your editor and navigate to wherever you plan to write your Go code and store your templates. The path should look something like this: $GOPATH/src/???/
(the ??? can be whatever you want).
We are going to name our files with the .gohtml
extension because it is commonly used by editors to indicate that you want Go HTML template syntax highlighting. Both Atom and Sublime Text have Go plugins that recognize this extension by default. That said, you can use .html
or any other extension you want.
Create the files hello.gohtml
and main.go
, then add the following code to hello.gohtml
:
<h1>Hello, {{.Name}}!</h1>
And then add the following code to main.go
:
package main
import (
"html/template"
"os"
)
func main() {
t, err := template.ParseFiles("hello.gohtml")
if err != nil {
panic(err)
}
data := struct {
Name string
}{"John Smith"}
err = t.Execute(os.Stdout, data)
if err != nil {
panic(err)
}
}
Now go ahead and run your code with go run main.go
. You should see the following output:
<h1>Hello, John Smith!</h1>
You have successfully created your first template! Now lets explore how Go’s template libraries handles encoding.
I mentioned before that Go’s html/template
package does encoding based on the context of the code, and in this section we are going to demonstrate what that encoding actually looks like in different contexts. Create another template named context.gohtml
and add the following code to it.
{{.Title}}
{{.HTML}}
{{.SafeHTML}}
{{.}}
<a title="{{.Title}}">
<a title="{{.HTML}}">
<a href="{{.HTML}}">
<a href="?q={{.HTML}}">
<a href="{{.Path}}">
<a href="?q={{.Path}}">
<!-- Encoding even works on non-string values! -->
<script>
var dog = {{.Dog}};
var map = {{.Map}};
doWork({{.Title}});
</script>
Then update main.go
to have the following code.
package main
import (
"html/template"
"os"
)
type Test struct {
HTML string
SafeHTML template.HTML
Title string
Path string
Dog Dog
Map map[string]string
}
type Dog struct {
Name string
Age int
}
func main() {
t, err := template.ParseFiles("context.gohtml")
if err != nil {
panic(err)
}
data := Test{
HTML: "<h1>A header!</h1>",
SafeHTML: template.HTML("<h1>A Safe header</h1>"),
Title: "Backslash! An in depth look at the \"\\\" character.",
Path: "/dashboard/settings",
Dog: Dog{"Fido", 6},
Map: map[string]string{
"key": "value",
"other_key": "other_value",
},
}
err = t.Execute(os.Stdout, data)
if err != nil {
panic(err)
}
}
Then go ahead and run your code with go run main.go
. You should see output that looks like this:
|
|
There is a lot going on here, so lets take a minute to look it over. We’ll start with the first four lines.
|
|
In these first few lines we are encoding values inside of the HTML context. As a result, html/template
encodes any characters that need encoded to be rendered correctly. Specifically, the <
and >
characters get encoded.
On the third line we are outputting a value of the type template.HTML
, which is how you tell the html/template
package that the string is safe to skip encoding. This means that you should NOT use this type when dealing with user input, as it could lead to code injection.
The next two lines (6-7) are anchor tags that show how values are encoded when put inside of an attribute like title
. This is here mostly to demonstrate that the html/template
package is aware of the attribute, and you will see in the next few lines that when values are inside of the href
attribute they are encoded differently.
|
|
Lines 9 through 12 demonstrate values being encoded as a query parameter or as a raw path in an href
attribute. There are two examples here because I wanted to demonstrate that different values are encoded here than in the HTML context, but the two have differences. For example, query paramters have the forward slash (/
) character encoded, but when the value is inserted as the path the slash is not encoded.
|
|
Next up we would expect the comment line (look in context.gohtml
for it), but as you can see the html/template
package handles removing any HTML comments for us.
Comments being removed is useful most of the time, but sometimes you want comments to persist. If you would like to see how that is done, I suggest you check out the HTML safe strings and HTML comments section of the third article, Using Functions Inside Go Templates. In that section we discuss ways to preserve comments in your HTML templates.
Finally we have the JavaScript code, lines 14 throuhg 18. This is, in my opinion, the coolest section.
While most template libraries don’t do anything too fancy here, Go’s html/template
does an amazing job of determining the proper context and what you likely intended. For example, in the first two lines the struct and map will be expanded into JSON objects, which is very likely what a developer would intend to do. In the last example we are inserting a string, and as you can see this was also encoded into it’s JSON equivalent which is just a string with quotes around it. As a result, we don’t have to wrap the variable in quotes like doWork("{{.Title}}")
.
|
|
This post should have given you a pretty good overview of the different contexts the html/template
package can handle, as well as how to use variables inside your templates. In future posts we are going to expand on this as we learn to use more features of the template library in Go.
In the next post, Template Actions and Nested Templates in Go, we will start with simple templates and build our way up to more complex ones using actions. The third post, Using Functions Inside Go Templates, looks at how to use both built-in and custom functions inside of your go templates. The fourth and final post, Creating the V in MVC, will put much of what we learned together with new concepts in order to build reusable views that can simplify the way the rest of your application renders HTML pages. Even if you don’t use MVC, this is likely to be an insightful read.
This article is part of the series, An Introduction to Templates in Go.
In my course - Web Development with Go - we use the html/template
package to build out an entire view layer for a realistic application. If you have ever wondered how all of these pieces fit together in the scope of a complete web application, I suggest you check out the course.
If you sign up for my mailing list (down there ↓over there →) I'll send you a FREE sample so you can see if it is for you. The sample includes over 2.5 hours of screencasts and the first few chapters from the book.
You will also receive notifications when I release new articles, updates on upcoming courses (including FREE ones), and I'll let you know when my paid courses are on sale.
Jon Calhoun is a full stack web developer who teaches about Go, web development, algorithms, and anything programming. If you haven't already, you should totally check out his Go courses.
Previously, Jon worked at several statups including co-founding EasyPost, a shipping API used by several fortune 500 companies. Prior to that Jon worked at Google, competed at world finals in programming competitions, and has been programming since he was a child.
More in this series
This post is part of the series, An Introduction to Templates in Go.
Spread the word
Did you find this page helpful? Let others know about it!
Sharing helps me continue to create both free and premium Go resources.
Want to discuss the article?
See something that is wrong, think this article could be improved, or just want to say thanks? I'd love to hear what you have to say!
You can reach me via email or via twitter.
©2024 Jonathan Calhoun. All rights reserved.