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 (
// 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