Link Search Menu Expand Document

Broken Authentication in Go Lang

Vulnerable example

The following snippet is of a web application, written in Go Lang, which has an administrative functionality that allows a user list to be shown when a specific URL is called.

func Users(w http.ResponseWriter, r * http.Request) {
  cookieUser, _: = r.Cookie("username")
  if cookieUser == nil {
      http.Redirect(w, r, "/login?errMsg=User+Not+Authorized", http.StatusFound)
      return
  }
  // show user list
}

The /admin/users URI is available to everyone who provides a cookie with the name set to username. A malicious actor could manipulate the HTTP request and add the Cookie header without performing a successful authentication:

GET /login HTTP/1.1
Host: www.vulnerableapp.com
Cookie: username=admin

Since the authentication routine only checks for the presence of the username cookie, access is granted:

HTTP/1.1 200 OK
Content-type: text/html

... sensitive data ...

Prevention

The Go standard library does not have baked-in support for sessions or session handling. Apply the recommended authentication and authorization mechanisms depending on the web framework of choice.

If you’re not using a web framework, you can consider implementing session management using the Gorilla’s Sessions package as demonstrated below:

package main
import (
  "fmt"
  "net/http"
  "github.com/gorilla/sessions"
)
var (
  key = [] byte(GenerateRandomKey(16)) 
  store = sessions.NewCookieStore(key)
)
func users(w http.ResponseWriter, r * http.Request) {
  session, _: = store.Get(r, "go-session")
  // Check if user is authenticated
  if auth, ok: = session.Values["authenticated"].(bool);
  !ok || !auth {
      http.Error(w, "Forbidden", http.StatusForbidden)
      return
    }
    // List Users
    fmt.Fprintln(w, userList)
}
func login(w http.ResponseWriter, r * http.Request) {
  session, _: = store.Get(r, "cookie-name")
  username: = r.Form["username"][0]
  password: = r.Form["password"][0]
  if areUserCredentialsCorrect(username, password) {
    // Set user as authenticated
    session.Values["authenticated"] = true
    session.Values["username"] = username
    session.Save(r, w)
    // redirect user to authenticated page
    return
  }
  http.Error(w, "Forbidden", http.StatusForbidden)
  return
}
func logout(w http.ResponseWriter, r * http.Request) {
  session, _: = store.Get(r, "cookie-name")
  // Revoke users authentication
  session.Values["authenticated"] = false
  session.Save(r, w)
}
func main() {
  http.HandleFunc("/admin/users", users)
  http.HandleFunc("/login", login)
  http.HandleFunc("/logout", logout)
  http.ListenAndServe(":8050", nil)
}

When the user performs an authentication attempt, the browser will issue the HTTP request below:

POST /login HTTP/1.1
Host: vulnerableapp.com
Content-type: application/urlencoded

username=user&password=secret

Upon successful authentication, the application returns the session cookie go-session which is a random base64 encoded string. The string does not contain user information. The user’s session information is stored server-side in a Map object, where the value of the go-session cookie is the key.

HTTP/1.1 302 Found
Location: /admin/users
Set-Cookie: go-session=MTU2MzkxNzIyM3xEdi1CQkFFQ180SUFBUkFCRUFBQVJfLUNBQUlHYzNSeWFXNW5EQW9BQ0hWelpYSnVZVzFsQm5OMGNtbHVad3dHQUFSMWMyVnlCbk4wY21sdVp3d1BBQTFoZFhSb1pXNTBhV05oZEdWa0JHSnZiMndDQWdBQnxqctGY_S5vt53QTJRqfZXNtXOvdcHrAyjJG3IvXZh7YQ==;

References

MITRE - CWE 287: Improper Authentication OWASP - Testing for Bypassing Authentication Schema Gorilla Toolkit - Sessions OWASP - Go Secure Coding Practices