Broken Access Control in Gorilla Mux with Basic Auth
Broken Access Control in Gorilla Mux with Basic Auth — how this specific combination creates or exposes the vulnerability
Broken Access Control occurs when API endpoints do not properly enforce authorization checks, allowing one user to access or modify resources belonging to another. When Basic Auth is used with Gorilla Mux, the risk is that authentication (who you are) is often conflated with authorization (what you are allowed to do). Basic Auth sends credentials in each request header (base64-encoded, not encrypted), and if the server only validates credentials without scoping access per user or role, any authenticated user may be able to reach endpoints or data they should not see.
In Gorilla Mux, routes are typically registered with methods and paths, and handlers often read credentials from the request header. If authorization logic is missing or applied inconsistently—such as checking authentication once but not re-checking per-resource or per-action—an attacker who obtains a valid credential can traverse the API beyond intended boundaries. Common patterns that lead to Broken Access Control include:
- Using a single middleware that only verifies the presence of a valid username/password but does not enforce per-user resource ownership (e.g., /users/{id} where {id} is not validated against the authenticated user).
- Failing to apply role-based or scope-based checks on specific HTTP verbs (PUT/DELETE) or sensitive paths (admin routes).
- Relying on Basic Auth over HTTP (not HTTPS), which exposes credentials in transit and enables replay or interception.
- Not validating route variables against the authenticated identity, enabling Insecure Direct Object References (IDOR) that manifest as Broken Access Control.
Because middleBrick tests unauthenticated attack surfaces and also runs authenticated-style probes where credentials are supplied, it can detect whether authorization checks are missing after authentication. Findings typically highlight missing per-resource ownership checks and lack of role validation, mapped to OWASP API Top 10 A01:2023 — Broken Access Control.
Basic Auth-Specific Remediation in Gorilla Mux — concrete code fixes
To fix Broken Access Control with Basic Auth in Gorilla Mux, you must combine authentication with per-request authorization checks, validate route parameters against the authenticated identity, and scope data access accordingly. Below are concrete, working examples that demonstrate a secure pattern.
1. Authenticate and extract identity on each request
Use a middleware that parses the Basic Auth header, validates credentials against a data store, and injects the authenticated user’s identity into the request context for downstream handlers to use.
import (
"encoding/base64"
"net/http"
"strings"
)
type UserContextKey string
const CurrentUser UserContextKey = "currentUser"
func BasicAuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
auth := r.Header.Get("Authorization")
if auth == "" || !strings.HasPrefix(auth, "Basic ") {
http.Error(w, `{"error": "authorization required"}`, http.StatusUnauthorized)
return
}
payload, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic "))
if err != nil {
http.Error(w, `{"error": "invalid authorization header"}`, http.StatusUnauthorized)
return
}
pair := strings.SplitN(string(payload), ":", 2)
if len(pair) != 2 {
http.Error(w, `{"error": "invalid credentials format"}`, http.StatusUnauthorized)
return
}
username, password := pair[0], pair[1]
// Replace with secure credential lookup (e user validation, e.g., constant-time compare)
user, ok := validateUser(username, password)
if !ok {
http.Error(w, `{"error": "invalid credentials"}`, http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), CurrentUser, user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func validateUser(username, password string) (*User, bool) {
// Example: in production, use a secure user store and constant-time password comparison
if username == "alice" && password == "correcthorse" {
return &User{ID: "u-123", Username: "alice", Role: "user"}, true
}
if username == "admin" && password == "secret" {
return &User{ID: "u-456", Username: "admin", Role: "admin"}, true
}
return nil, false
}
type User struct {
ID string
Username string
Role string
}
2. Enforce ownership and role-based checks in handlers
In your route handlers, always validate that the authenticated user is allowed to access or modify the target resource. For user-specific endpoints, compare the route variable with the user ID from context and ensure roles are checked for privileged operations.
func GetUserProfile(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
targetID := vars["id"]
user, _ := r.Context().Value(CurrentUser).(*User)
// Enforce ownership: users can only access their own profile unless admin
if user.Role != "admin" && user.ID != targetID {
http.Error(w, `{"error": "forbidden"}`, http.StatusForbidden)
return
}
// Fetch profile for targetID (guaranteed to be accessible)
profile, err := fetchProfile(targetID)
if err != nil {
http.Error(w, `{"error": "not found"}`, http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(profile)
}
func DeleteResource(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
resourceID := vars["id"]
user, _ := r.Context().Value(CurrentUser).(*User)
// Admin-only operation
if user.Role != "admin" {
http.Error(w, `{"error": "forbidden"}`, http.StatusForbidden)
return
}
if err := deleteResource(resourceID); err != nil {
http.Error(w, `{"error": "not found"}`, http.StatusNotFound)
return
}
w.WriteHeader(http.StatusNoContent)
}
3. Apply middleware globally and per-route as needed
Attach the authentication middleware to routes that require it, and ensure authorization logic is inside handlers or further scoped middleware. For admin routes, add an additional role-check middleware.
r := mux.NewRouter()
// Public endpoint
r.HandleFunc("/health", HealthCheck).Methods("GET")
// Authenticated user endpoint
r.HandleFunc("/users/{id}", GetUserProfile).Methods("GET")
// Authenticated and admin-only endpoint
adminRoutes := r.PathPrefix("/admin").Subrouter()
adminRoutes.Use(BasicAuthMiddleware)
adminRoutes.Use(RoleAdminMiddleware)
adminRoutes.HandleFunc("/resources/{id}", DeleteResource).Methods("DELETE")
http.ListenAndServe(":8080", r)
func RoleAdminMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, _ := r.Context().Value(CurrentUser).(*User)
if user.Role != "admin" {
http.Error(w, `{"error": "forbidden"}`, http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
These steps ensure that authentication via Basic Auth is followed by proper authorization checks per resource and role, effectively mitigating Broken Access Control in Gorilla Mux APIs.