Link Search Menu Expand Document

Broken Authentication in Go Lang

Play SecureFlag Play Go Lang Labs on this vulnerability with SecureFlag!

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 (
  // In production, load keys from env variables instead.
  authKey = [] byte(GenerateRandomKey(32))
  encryptKey = [] byte(GenerateRandomKey(16))
  store = sessions.NewCookieStore(authKey, encryptKey)
)
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, "go-session")
  username := r.FormValue("username")
  password := r.FormValue("password")
  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, "go-session")
  // 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 a go-session cookie with a base64-encoded value. This value securely stores the user’s session data, signed and encrypted by Gorilla’s CookieStore to ensure both integrity and confidentiality.

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

References

CWE - CWE-287: Improper Authentication

OWASP - A07:2021 - Identification and Authentication Failures

OWASP - Authentication Cheat Sheet

Gorilla Toolkit - Sessions

OWASP - Go Secure Coding Practices