Mass Assignment in Fiber
How Mass Assignment Manifests in Fiber
Mass assignment vulnerabilities in Fiber applications occur when user-supplied data is automatically bound to struct fields without proper authorization checks. In Go's Fiber framework, this typically happens when using the Context.Bind or Context.BindJSON methods to unmarshal request bodies directly into model structs.
Consider this common Fiber pattern:
type User struct {
ID string `json:"id"`
Email string `json:"email"`
Password string `json:"password"`
Role string `json:"role"`
IsActive bool `json:"is_active"`
}
func UpdateUser(c *fiber.Ctx) error {
var user User
if err := c.BindJSON(&user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
// Update database with all fields from user struct
if err := db.UpdateUser(user.ID, user); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"message": "User updated"})
}
The vulnerability here is that an attacker can craft a JSON payload like:
{
"id": "admin123",
"email": "hacker@example.com",
"password": "newpassword",
"role": "admin",
"is_active": true
}
This payload attempts to modify the user's role to admin, potentially escalating privileges. The BindJSON method indiscriminately maps all JSON fields to struct fields, including those the user shouldn't control.
Another Fiber-specific manifestation occurs with query parameters and form data:
func CreateProduct(c *fiber.Ctx) error {
var product Product
if err := c.Bind(&product); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
// Directly save all fields from request
result, err := db.CreateProduct(product)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(result)
}
Here, an attacker could manipulate fields like is_published, price, or category_id that should be controlled by the system, not the user.
Fiber-Specific Detection
Detecting mass assignment vulnerabilities in Fiber applications requires examining both code patterns and runtime behavior. Here's how to identify these issues:
Code Review Patterns:
Look for these red flags in your Fiber codebase:
// Dangerous: direct binding without field filtering
func DangerousHandler(c *fiber.Ctx) error {
var model MyModel
c.BindJSON(&model) // <-- Vulnerability here
// ... uses model directly
}
// Safer: explicit field selection
func SafeHandler(c *fiber.Ctx) error {
var input struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := c.BindJSON(&input); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
// Only use the fields you explicitly expect
user := User{
Name: input.Name,
Email: input.Email,
}
// ... continue processing
}
Using middleBrick for Automated Detection:
middleBrick's black-box scanning approach can detect mass assignment vulnerabilities without requiring source code access. The scanner tests for:
- Property Authorization: Attempts to modify fields that should be immutable or system-controlled
- Input Validation: Tests for unexpected field injection
- BOLA (Broken Object Level Authorization): Checks if users can modify objects they shouldn't access
To scan a Fiber API with middleBrick:
npm install -g middlebrick
middlebrick scan https://api.yourapp.com/user/update
The scanner will attempt to modify sensitive fields like role, admin, permissions, and system-controlled properties, reporting any successful modifications as mass assignment vulnerabilities.
Runtime Testing:
You can also test your Fiber endpoints manually:
# Test for mass assignment on user update endpoint
curl -X PUT https://api.yourapp.com/users/123 \
-H "Content-Type: application/json" \
-d '{
"name": "Test User",
"email": "test@example.com",
"role": "admin",
"is_active": true,
"created_at": "2000-01-01",
"updated_at": "2000-01-01"
}'
If the response shows that role, is_active, or timestamp fields were modified, you have a mass assignment vulnerability.
Fiber-Specific Remediation
Securing Fiber applications against mass assignment requires explicit field control and validation. Here are Fiber-specific remediation strategies:
1. Use Explicit Binding with Struct Filtering:
type UserUpdateInput struct {
Name string `json:"name"`
Email string `json:"email"`
}
func UpdateUser(c *fiber.Ctx) error {
var input UserUpdateInput
if err := c.BindJSON(&input); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
// Fetch existing user first
userID := c.Params("id")
user, err := db.GetUser(userID)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
}
// Only update allowed fields
user.Name = input.Name
user.Email = input.Email
if err := db.UpdateUser(user); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"message": "User updated"})
}
2. Use Field-Level Authorization Tags:
type User struct {
ID string `json:"id" db:"id" auth:"read-only"`
Email string `json:"email" db:"email" auth:"user-can-update"`
Password string `json:"password" db:"password" auth:"admin-only"`
Role string `json:"role" db:"role" auth:"admin-only"`
IsActive bool `json:"is_active" db:"is_active" auth:"admin-only"`
}
func UpdateUser(c *fiber.Ctx) error {
var input map[string]interface{}
if err := c.BindJSON(&input); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
userID := c.Params("id")
user, err := db.GetUser(userID)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
}
// Check permissions for each field
for field, value := range input {
authLevel, exists := getAuthLevel(field)
if !exists {
continue // Ignore unknown fields
}
if !hasPermission(c, authLevel) {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Permission denied for field: " + field})
}
// Update only authorized fields
updateUserField(user, field, value)
}
if err := db.UpdateUser(user); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"message": "User updated"})
}
3. Use Middleware for Field Validation:
func FieldAuthorizationMiddleware(allowedFields []string) fiber.Handler {
return func(c *fiber.Ctx) error {
var input map[string]interface{}
if err := c.BindJSON(&input); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
// Filter input to only allowed fields
filtered := make(map[string]interface{})
for _, field := range allowedFields {
if value, exists := input[field]; exists {
filtered[field] = value
}
}
// Store filtered input for next handler
c.Locals("filtered_input", filtered)
return c.Next()
}
}
// Usage
app.Put("/users/:id", FieldAuthorizationMiddleware([]string{"name", "email"}), UpdateUserHandler)
func UpdateUserHandler(c *fiber.Ctx) error {
filteredInput := c.Locals("filtered_input").(map[string]interface{})
// Rest of handler uses only filtered fields
userID := c.Params("id")
user, err := db.GetUser(userID)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
}
// Apply filtered updates
if name, exists := filteredInput["name"]; exists {
user.Name = name.(string)
}
if email, exists := filteredInput["email"]; exists {
user.Email = email.(string)
}
if err := db.UpdateUser(user); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"message": "User updated"})
}
4. Integration with middleBrick for Continuous Security:
Integrate middleBrick into your Fiber development workflow to catch mass assignment vulnerabilities early:
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install middleBrick
run: npm install -g middlebrick
- name: Scan API endpoints
run: |
middlebrick scan https://staging.yourapp.com/api --fail-threshold C
env:
MIDDLEBRICK_API_KEY: ${{ secrets.MIDDLEBRICK_API_KEY }}
This GitHub Action configuration will scan your Fiber API endpoints on every pull request and fail the build if any endpoint receives a C grade or lower, ensuring mass assignment vulnerabilities are caught before production deployment.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |