Ssrf Server Side in Fiber with Firestore
Ssrf Server Side in Fiber with Firestore
Server-side request forgery (SSRF) in a Fiber application that interacts with Google Firestore can occur when user-supplied input is used to drive HTTP requests or resource resolution without strict validation. In this context, an attacker may coerce the backend into making arbitrary requests to internal metadata services or internal Firestore endpoints, potentially leaking service metadata or IAM-bound credentials that are reachable only from within the environment.
Consider a scenario where an endpoint accepts a document path or URL to preload related data. If the application uses that input to construct Firestore client operations or to call external services (for example, to import remote configuration), an attacker can supply a malicious URI that causes the server to reach internal endpoints such as the Compute Engine metadata service (http://metadata.google.internal) or misconfigured Firestore emulators. Because the scan tests unauthenticated attack surfaces across 12 checks including SSRF, such patterns are surfaced as findings with severity and guidance.
Firestore itself does not make outbound HTTP requests in typical get/put operations, but application code that combines Firestore reads/writes with URL fetching can create an SSRF vector. For example, using the Firestore document ID to determine a target host for an HTTP fetch, or using a Firestore-stored URL to drive background data synchronization, can expose internal services if input validation is weak. The scanner evaluates input validation and SSRF checks in parallel, highlighting cases where dynamic values reach network calls without allowlisting or canonicalization.
An illustrative vulnerable Fiber endpoint might look like this, where a URL query parameter is used without validation to load an external resource before writing metadata to Firestore:
// DO NOT USE: vulnerable example
app.Get("/import", func(c *fiber.Ctx) error {
rawURL := c.Query("url")
// Unsafe: rawURL can point to internal metadata or SSRF targets
resp, err := http.Get(rawURL)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
// Store something derived from the external response into Firestore
client, err := firestore.NewClient(context.Background(), os.Getenv("GCP_PROJECT"))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
_, err = client.Collection("imports").Doc("logs").Set(context.Background(), map[string]interface{}{
"source": rawURL,
"body": string(data),
"time": time.Now(),
})
return c.SendStatus(fiber.StatusOK)
})
In this pattern, rawURL reaches an HTTP call and is subsequently stored in Firestore. An SSRF attacker could provide a URL pointing to the metadata service to obtain service account tokens or to a restricted Firestore instance if the runtime environment permits. The scanner flags this as a high-severity SSRF finding and recommends strict input validation, allowlisting known domains, and avoiding user-controlled URLs in backend requests.
Firestore-specific remediation focuses on ensuring that any dynamic values used in client initialization or HTTP calls are constrained and never reflect attacker-controlled data without strict validation. This includes avoiding concatenation of user input into resource identifiers (document paths, collection names) without allowlisting, and never using external URLs to drive Firestore writes unless those URLs are vetted and fetched server-side with tightly scoped network controls.
Firestore-Specific Remediation in Fiber
Remediation centers on strict validation, allowlisting, and isolating external data from Firestore operations. Do not use raw user input as document IDs, collection names, or query parameters that influence Firestore client behavior. Instead, map user input to a controlled set of identifiers and perform external fetches only through server-side, vetted endpoints.
Use a fixed set of allowed origins or IDs, and validate before any Firestore interaction. For example, if your application imports data from a predefined set of sources, accept only an enum rather than a raw URL:
// Safe pattern: restrict source to known integrations
var source string
switch c.Query("source") {
case "partnerA", "partnerB":
source = c.Query("source")
default:
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid source"})
}
client, err := firestore.NewClient(context.Background(), os.Getenv("GCP_PROJECT"))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
defer client.Close()
// Use a deterministic document ID derived from a safe source reference
_, err = client.Collection("imports").Doc(source).Set(context.Background(), map[string]interface{}{
"ingestedAt": time.Now(),
"status": "queued",
})
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.SendStatus(fiber.StatusAccepted)
If you must fetch remote data before writing to Firestore, perform the fetch on the server using a strict allowlist of domains and with outbound network controls (for example, egress allowlists at the infrastructure level). Do not forward user-supplied URLs to the runtime environment:
// Only fetch from preapproved endpoints
const allowedHost = "https://config.partner.com/schema.json"
func isValidURL(u string) bool {
return strings.HasPrefix(u, allowedHost)
}
userURL := c.Query("config_url")
if !isValidURL(userURL) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "config not allowed"})
}
resp, err := http.Get(userURL)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
}
defer resp.Body.Close()
var payload map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid config"})
}
client, err := firestore.NewClient(context.Background(), os.Getenv("GCP_PROJECT"))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
_, err = client.Collection("configs").Doc("current").Set(context.Background(), payload)
These patterns ensure that Firestore operations remain deterministic and that external inputs do not influence resource paths or trigger unintended network calls. Combine this with the scanner’s input validation and SSRF checks to verify that no dangerous paths remain. The dashboard can track your scans over time, the CLI can integrate into scripts for quick checks, and the GitHub Action can enforce a minimum security score before merges.
Finally, map findings to frameworks such as OWASP API Top 10 and compliance regimes relevant to your environment. The scanner reports specific guidance for each finding, enabling developers to remediate without needing to build custom detection logic.