Insecure Design in Hanami with Firestore
Insecure Design in Hanami with Firestore — how this specific combination creates or exposes the vulnerability
Insecure Design in the context of a Hanami application using Google Cloud Firestore arises when application-level decisions allow an attacker to bypass intended access controls or manipulate data flows across Firestore documents and collections. Hanami encourages explicit, domain-driven modeling, but if the domain model does not enforce ownership and authorization checks at the use-case level, Firestore’s flexible document structure can be exploited.
One common pattern is mapping Firestore documents directly to domain entities without scoping reads and writes to the correct tenant or user. For example, a Hanami entity might expose a Post aggregate that maps to a Firestore collection named posts. If queries retrieve documents by an identifier provided directly from the client (e.g., a public ID) without verifying that the requesting user has permission to view that document, this can lead to Insecure Design, often manifesting as Insecure Direct Object References (IDOR) or Broken Function Level Authorization (BFLA).
Firestore security rules are not a substitute for application-level design. Rules can restrict access by UID, but if Hanami use cases do not validate ownership or role within the domain, a client can craft requests that technically pass Firestore rules but violate business logic. For instance, an endpoint that accepts a document_id parameter and performs a Firestore get without confirming the document belongs to the current tenant enables horizontal privilege escalation. Attackers can iterate over known IDs to harvest data belonging to other users or organizations.
Another insecure design pattern involves batching operations without authorization checks. Hanami use cases may call Firestore get_all or where queries using client-supplied filters. If these filters are not constrained by tenant ID or role, an attacker can inject query parameters that return datasets they should not see. This aligns with BOLA/IDOR and BFLA classes of vulnerabilities, where the API surface exposes collection endpoints that should be scoped per user or per organization.
Design-time decisions also affect logging and error handling. If Hanami applications surface Firestore document IDs or internal paths in error messages, this can lead to Data Exposure. Additionally, if the application embeds Firestore write results into downstream processes without validating intent, it may enable SSRF or unsafe consumption patterns when those results are used to construct further requests.
Using the OpenAPI/Swagger analysis capability of middleBrick, teams can cross-reference their Hanami API spec with runtime behavior to detect endpoints where Firestore document identifiers are accepted without proper authorization context. This helps highlight insecure design decisions before they are exploited in the wild.
Firestore-Specific Remediation in Hanami
Remediation focuses on enforcing authorization within Hanami use cases and ensuring Firestore queries are constrained by tenant and ownership context. Always validate that the current user has permission to access or modify a specific Firestore document, regardless of Firestore security rules.
First, scope Firestore queries by user ID or organization ID. Instead of retrieving a document by a client-provided ID alone, derive the document path from the authenticated user’s context. For example, if each user’s data resides under a collection group organizations/{org_id}/posts, construct the query using the organization ID from the session or token, not from user input.
require "google/cloud/firestore"
class PostRepository
def initialize(project_id:)
@firestore = Google::Cloud::Firestore.new(project: project_id)
end
def for_user_in_organization(user_id, org_id)
@firestore.collection("organizations")
.doc(org_id)
.collection("posts")
.get
end
def find_by_id(user_id, org_id, post_id)
doc_ref = @firestore.collection("organizations")
.doc(org_id)
.collection("posts")
.doc(post_id)
doc = doc_ref.get
raise EntityNotFound unless doc.exists?
# Ensure the retrieved document belongs to the expected org
raise Forbidden unless doc.data["org_id"] == org_id
doc
end
end
Second, enforce ownership checks in Hanami use cases before performing reads or writes. Do not rely on Firestore rules alone; confirm that the authenticated user matches the owner stored in the document. This prevents horizontal privilege escalation where one user accesses another user’s document by guessing or iterating IDs.
class CreatePost
def initialize(repo: PostRepository.new(project_id: ENV["FIRESTORE_PROJECT"]))
@repo = repo
end
def call(params:, current_user:)
org = OrganizationRepository.new.find(params[:org_id])
raise Forbidden unless org.user_has_role?(current_user, :member)
post = @repo.create(
org_id: org.id,
title: params[:title],
content: params[:content],
author_id: current_user.id
)
{ success: true, post: post }
end
end
Third, avoid exposing raw Firestore document IDs in URLs or client-generated references. Use opaque identifiers or map internal IDs to UUIDs that are not predictable. If you must accept document IDs from clients, re-validate them against the scoped query derived from the authenticated user’s context.
Fourth, structure Firestore documents to minimize risk. Store sensitive fields in subcollections that can be more strictly controlled, and avoid placing secrets in top-level fields that might be returned in broader queries. Design your Hanami entities to encapsulate Firestore access patterns, so that changes in document structure are isolated to repositories rather than leaking into controllers or views.
Finally, integrate middleBrick into your workflow to continuously validate your API surface. By running the CLI (middlebrick scan <url>) or adding the GitHub Action to your CI/CD pipeline, you can detect endpoints that accept Firestore identifiers without proper authorization. The Pro plan’s continuous monitoring can alert you when new endpoints introduce insecure design patterns, and the MCP Server allows you to scan APIs directly from your AI coding assistant during development.