Identification Failures in Hanami with Firestore
Identification Failures in Hanami with Firestore — how this specific combination creates or exposes the vulnerability
Identification failures occur when an application fails to properly establish and enforce identity and permissions across a request lifecycle. In a Hanami application using Cloud Firestore as the backend, this typically manifests as insufficient checks on resource ownership or overly permissive queries that allow one user to access or modify another user’s documents. Hanami’s explicit, object-oriented domain model can help structure authorization, but if developers rely only on route parameters or client-supplied IDs without validating the owning entity, the unauthenticated or under-authorized access path remains.
Firestore’s security model is rule-based and does not inherently understand application-level ownership. If security rules are written to allow broad read or write access (for example, permitting authenticated users to update any document in a collection), and the Hanami layer does not enforce per-user scoping, an IDOR (Insecure Direct Object Reference) condition arises. An attacker can enumerate predictable document IDs or manipulate parameters to access or modify data that should be isolated to another user. This is an identification failure because the system does not correctly bind a subject (user) to the object (document) across both the runtime domain and the storage rules.
These failures are especially relevant to the BOLA/IDOR checks in middleBrick’s 12 security checks, which test whether endpoints properly enforce ownership. When Firestore rules are not scoped by user identity and Hanami operations do not validate that the current user matches the document’s owner field, the attack surface expands. For example, a PUT /accounts/:id endpoint that updates a Firestore document without confirming that the authenticated user’s ID matches the document’s user_id field enables privilege escalation via insecure direct object reference. The scanner’s tests for Property Authorization and BOLA/IDOR highlight this by submitting modified identifiers and verifying whether access is improperly granted.
Additionally, Firestore’s flexible querying can inadvertently expose data if queries lack strict filters on ownership. A Hanami service that retrieves documents by a non-unique attribute without also filtering on user_id may return records belonging to other users. Such misconfigurations are often discovered through runtime tests that include a second user’s identifiers, checking whether data is returned or modified. middleBrick’s runtime checks complement the OpenAPI/Swagger analysis by comparing spec definitions with actual responses, ensuring that per-object authorization is reflected both in design and behavior.
In practice, identification failures in this stack stem from assuming that Firestore rules alone provide sufficient isolation, while the application layer omits consistent ownership checks. Defense requires aligning Firestore security rules with Hanami’s domain logic so that every query and mutation enforces user-level scoping. This includes validating the current user’s identity against document fields, using server-side UID resolution rather than trusting client input, and structuring rules to deny access when ownership cannot be verified.
Firestore-Specific Remediation in Hanami — concrete code fixes
Remediation centers on enforcing user ownership at both the Firestore rules level and the Hanami domain and service layers. Never rely solely on Firestore rules for identification; always enforce ownership in application logic. Below are concrete patterns and code examples tailored to Hanami and Firestore.
Firestore Rules: Scope writes by authenticated UID
Ensure rules tie mutations to the requesting user’s UID. Use request.auth.uid and validate that incoming document fields match.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /accounts/{accountId} {
allow read, write: if request.auth != null && request.auth.uid == request.resource.data.user_id;
}
}
}
Hanami Domain Model: Explicit ownership entity
Define an Account entity that includes user_id and enforce checks in operations.
# entity/account.rb
module Finance::Entities
class Account < Hanami::Entity
attributes :id, :user_id, :balance
end
end
Hanami Service: Validate ownership before update
In your service, resolve the current user’s UID (from session or token) and scope the query.
# services/update_account.rb
module Finance::Services
class UpdateAccount
def initialize(current_user_id)
@current_user_id = current_user_id
end
def call(account_id, changes)
account = fetch_account(account_id)
raise Errors::AuthorizationError unless account.user_id == @current_user_id
# Proceed with safe update
repository(:accounts).update(account_id, changes.merge(user_id: @current_user_id))
end
private
def fetch_account(account_id)
account_repo = Repository(:accounts)
account_repo.find(account_id) or raise Errors::NotFound
end
end
end
Firestore query: Always filter by user_id
When listing or retrieving documents, include user_id in the query filters.
# repositories/account_repo.rb
module Finance::Repositories
class AccountRepo
def for_current_user(user_id)
collection = Firestore.new.collection('accounts')
snapshot = collection.where(:user_id, '==', user_id).get
snapshot.map { |doc| Finance::Entities::Account.new(id: doc.id, **doc.data) }
end
end
end
Hanami controller: Bind current user and pass UID to services
Ensure the controller provides the authenticated UID to services and does not trust URL parameters for ownership.
# web/controllers/accounts/update.rb
module Finance::Web::Controllers::Accounts
class Update
include Finance::Import["repo.account_repo", "service.update_account"]
def call(params)
user_id = current_user.id # obtain from session/auth
result = update_account.call(user_id, params[:id], params.except(:id))
if result.success?
redirect to routes.accounts_path
else
self.status = 422
render "accounts/edit"
end
end
private
def current_user
# implementation specific to your auth setup
@current_user ||= Auth::UserGateway.new(current_session).fetch
end
end
end
Defense-in-depth: Combine rule checks with application checks
Use Firestore rules as a safety net, but ensure Hanami services consistently validate ownership. Avoid constructing queries that return documents across users, and never expose internal document IDs directly to the client without contextual validation.