6.4 Preventing session hijacking

Session hijacking is a common yet serious security threat. Clients use session id's for validation and other purposes when communicating with servers. Unfortunately, malicious third parties can sometimes track these communications and figure out the client session id.

In this section, we are going to show you how to hijack a session for educational purposes.

The session hijacking process

The following code is a counter for the count variable:

    func count(w http.ResponseWriter, r *http.Request) {
        sess := globalSessions.SessionStart(w, r)
        ct := sess.Get("countnum")
        if ct == nil {
            sess.Set("countnum", 1)
        } else {
            sess.Set("countnum", (ct.(int) + 1))
        t, _ := template.ParseFiles("count.gtpl")
        w.Header().Set("Content-Type", "text/html")
        t.Execute(w, sess.Get("countnum"))

The content of count.gtpl is as follows:

Hi. Now count:{{.}}

We can see the following content in the browser:

Figure 6.4 count in browser.

Keep refreshing until the number becomes 6, then open the browser's cookie manager (I use chrome here). You should be able to see the following information:

Figure 6.5 cookies saved in a browser.

This step is very important: open another browser (I use firefox here), copy the URL to the new browser, open a cookie simulator to create a new cookie and input exactly the same value as the cookie we saw in our first browser.

Figure 6.6 Simulate a cookie.

Refresh the page and you'll see the following:

Figure 6.7 hijacking the session has succeeded.

Here we see that we can hijack sessions between different browsers, and actions performed in one browser can affect the state of a page in another browser. Because HTTP is stateless, there is no way of knowing that the session id from firefox is simulated, and chrome is also not able to know that it's session id has been hijacked.

prevent session hijacking

Through this simple example of hijacking a session, you can see that it's very dangerous because it allows attackers to do whatever they want. So how can we prevent session hijacking?

The first step is to only set session id's in cookies, instead of in URL rewrites. Also, we should set the httponly cookie property to true. This restricts client-side scripts from gaining access to the session id. Using these techniques, cookies cannot be accessed by XSS and it won't be as easy as we demonstrated to get a session id from a cookie manager.

The second step is to add a token to every request. Similar to the manner in which we dealt with repeating form submissions in previous sections, we add a hidden field that contains a token. When a request is sent to the server, we can verify this token to prove that the request is unique.

    h := md5.New()
    if r.Form["token"]!=token{
        // ask to log in

Session id timeout

Another solution is to add a create time for every session, and to replace expired session id's with new ones. This can prevent session hijacking under certain circumstances such as when the hijack is attempted too late.

    createtime := sess.Get("createtime")
    if createtime == nil {
        sess.Set("createtime", time.Now().Unix())
    } else if (createtime.(int64) + 60) < (time.Now().Unix()) {
        globalSessions.SessionDestroy(w, r)
        sess = globalSessions.SessionStart(w, r)

We set a value to save the create time and check if it's expired (I set 60 seconds here). This step can often thwart session hijacking attempts.

By combining the two solutions set out above you will be able to prevent most session hijacking attempts from succeeding. On the one hand, session id's that are frequently reset will result in an attacker always getting expired and useless session id's; on the other hand, by setting the httponly property on cookies and ensuring that session id's can only be passed via cookies, all URL based attacks are mitigated. Finally, we set MaxAge=0 on our cookies, which means that the session id's will not be saved in the browser history.