Nosql Injection in Fiber with Firestore
Nosql Injection in Fiber with Firestore — how this specific combination creates or exposes the vulnerability
NoSQL injection in a Fiber application that uses Google Firestore typically occurs when user-controlled input is concatenated into queries or map-like structures that Firestore interprets as field paths, operators, or keys. Unlike SQL, Firestore does not have a single query language; injection-like behavior arises when input influences property names, map keys, or filter values in ways that bypass intended access patterns.
Consider a Fiber endpoint that retrieves a user profile by ID directly from query parameters:
// Dangerous: user input used to construct a Firestore document reference
userID := c.Params("userID")
docRef := client.Collection("profiles").Doc(userID)
If userID is attacker-controlled and not validated, an input such as profiles/abc&where=__name__+==+"users/xyz" is not interpreted as a document ID by Firestore, but the application logic may misuse it to build references or maps that later feed into Firestore operations. More relevant are cases where input shapes map-like structures used in Where, OrderBy, or document updates.
For example, a search handler that builds a Firestore query based on JSON query parameters can be steered into unintended behavior:
// Risk: attacker-controlled keys/values used in query construction
var q struct {
Filters map[string]interface{} `json:"filters"`
}
if err := c.BodyParser(&q); err != nil {
c.Status(400)
return
}
query := client.Collection("products")
for field, value := range q.Filters {
query = query.Where(field, "==", value)
}
iter := query.Documents(ctx)
If an attacker supplies {"filters":{"$where":"true==true"}} or manipulates field names to include path traversal or reserved Firestore operators, the resulting query may expose more data than intended or bypass logical access boundaries. Firestore does not support server-side JavaScript $where-style evaluation, but application code that misinterprets error handling may leak information via timing or error messages. Similarly, using input as map keys in batched reads or document merges can lead to reads/writes outside the intended namespace, effectively an insecure direct object reference (IDOR) facilitated by untrusted input shaping.
Another vector is dynamic collection or document naming. If a handler uses user input to decide which collection to query without strict allowlisting:
// Risk: collection name derived from user input
collectionName := c.Params("collection")
coll := client.Collection(collectionName)
_ = coll.Doc("doc1").Get(ctx)
An input like users__roles__admin or a path with special semantics may cause the application to query a sensitive collection. While Firestore will treat the string as a literal collection name, the lack of allowlisting means the attacker may probe collections that are otherwise not referenced by the app, leading to information exposure.
These patterns illustrate that NoSQL injection with Firestore in Fiber is less about query language injection and more about improper handling of user input in query construction, document paths, map keys, and update maps. The impact often maps to OWASP API Top 10 A01:2023 Broken Object Level Authorization and A05:2023 Security Misconfiguration, where attackers read or manipulate data they should not access.
Firestore-Specific Remediation in Fiber — concrete code fixes
Remediation focuses on strict input validation, allowlisting, and avoiding dynamic query construction from untrusted data. Treat all user input as untrusted and never directly embed it into Firestore paths, map keys, or filter structures.
1. Validate and sanitize document IDs and collection names
Use a strict allowlist or regex to ensure IDs contain only safe characters. Avoid using raw user input as document references.
// Safer: validate document ID format before use
userID := c.Params("userID")
matched, _ := regexp.MatchString(`^[a-zA-Z0-9_-]{1,100}$`, userID)
if !matched {
c.Status(400).JSON(fiber.Map{"error": "invalid user ID"})
return
}
docRef := client.Collection("profiles").Doc(userID)
2. Use allowlists for field names in queries
Do not trust incoming field names. Map them to known safe fields before building queries.
// Define safe fields
var safeFields = map[string]bool{
"name": true, "price": true, "category": true,
}
var q struct {
Filters map[string]interface{} `json:"filters"`
}
if err := c.BodyParser(&q); err != nil {
c.Status(400).JSON(fiber.Map{"error": "invalid body"})
return
}
query := client.Collection("products")
for field, value := range q.Filters {
if !safeFields[field] {
continue // or return an error
}
query = query.Where(field, "==", value)
}
iter := query.Documents(ctx)
3. Avoid dynamic collection names; use a mapping if necessary
Do not trust parameters that name collections. If you need to route to different collections, map allowed values explicitly.
// Map allowed collection names to Firestore collections
allowedCollections := map[string]string{
"profiles": "profiles",
"products": "products",
}
collectionName := c.Params("collection")
colName, ok := allowedCollections[collectionName]
if !ok {
c.Status(400).JSON(fiber.Map{"error": "invalid collection"})
return
}
coll := client.Collection(colName)
doc := coll.Doc("doc1")
4. Use Firestore transactions for read-modify-write
To prevent inconsistent state when multiple clients update documents, use transactions rather than building updates purely from unchecked input.
// Safe update within a transaction
profileRef := client.Collection("profiles").Doc(userID)
err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
doc, err := tx.Get(profileRef)
if err != nil {
return err
}
// Apply changes based on validated input
tx.Set(profileRef, map[string]interface{}{
"lastSeen": time.Now(),
}, firestore.MergeAll)
return nil
})
5. Leverage Firestore security rules as a secondary control
While not a substitute for secure coding, rules can restrict which document paths and fields a request may access. Ensure rules align with the principle of least privilege.
By combining strict input validation, allowlisting, and safe Firestore patterns, you reduce the risk of NoSQL injection and IDOR-like issues in Fiber applications using Firestore.