Pii Leakage in Grape with Firestore
Pii Leakage in Grape with Firestore — how this specific combination creates or exposes the vulnerability
Grape is a Ruby API micro-framework commonly used to build RESTful endpoints. When a Grape API uses Google Cloud Firestore as a backend data store, PII leakage can occur if endpoint responses include sensitive document fields and security rules or runtime controls are insufficient. Firestore stores documents in collections; if a query returns entire documents or subcollections that contain fields such as email, phone, national ID, or health information, and the API does not explicitly project or filter those fields, the data is exposed in HTTP responses.
The risk is compounded when Firestore security rules are misconfigured—for example, allowing read access based only on authentication state without validating ownership or context—or when the API directly forwards Firestore query results to clients. Sensitive data may also be exposed through error messages if Firestore exceptions reveal document paths or internal structures. Because Grape endpoints often map resources 1:1 to Firestore documents, missing field-level filtering means any over-permissive rule effectively becomes a data leak. Common root causes include:
- Reading full documents without field selection, so PII such as emails or addresses is returned verbatim.
- Using broad Firestore rules like
allow read: if request.auth != null;without additional constraints per user or per resource. - Pass-through APIs that proxy Firestore responses without normalization or redaction, inadvertently leaking nested fields or metadata.
An attacker who compromises authentication or exploits a logic flaw may enumerate accessible document IDs and retrieve PII directly through the Grape endpoint. MiddleBrick’s scans detect such leakage by correlating OpenAPI paths that expose user data with Firestore access patterns, highlighting endpoints that return sensitive fields without masking or authorization checks at the field level.
Firestore-Specific Remediation in Grape — concrete code fixes
Remediation focuses on minimizing data exposure in both Firestore rules and Grape response construction. Use Firestore queries that explicitly select only required fields and enforce ownership-based rules. In Grape, transform Firestore documents before serialization to strip or mask PII.
Firestore security rules
Rules should scope reads to the requesting user and avoid broad public access. For a users collection where each document ID matches the user’s UID:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
match /profiles/{profileId} {
allow read, write: if request.auth != null && request.auth.uid == profileId;
}
}
}
Avoid rules that allow reading all documents in a collection, and prefer granular matches that also validate request resource data when needed.
Grape endpoint with field-level filtering
Use select in Firestore queries to limit returned fields, and remove PII before sending the response.
require 'google/cloud/firestore'
class EntitiesResource
include Grape::API
format :json
helpers do
def firestore
@firestore ||= Google::Cloud::Firestore.new
end
def current_user_id
# derive from auth token in real use
env['current_user_id']
end
end
desc 'Get public profile data only'
params do
requires :profile_id, type: String, desc: 'Profile document ID'
end
get '/profiles/:profile_id' do
profile_ref = firestore.col('profiles').doc(params[:profile_id])
# Select only non-sensitive fields
snapshot = profile_ref.get(fields: [:display_name, :avatar_url])
not_found! unless snapshot.exists?
{
display_name: snapshot[:display_name],
avatar_url: snapshot[:avatar_url]
}
end
desc 'Get user data with PII masked'
params do
requires :user_id, type: String, desc: 'User document ID'
end
get '/users/:user_id' do
user_ref = firestore.col('users').doc(params[:user_id])
snapshot = user_ref.get
forbidden! unless snapshot.exists? && snapshot[:owner_uid] == current_user_id
{
user_id: snapshot[:user_id],
display_name: snapshot[:display_name],
# PII fields are omitted; if needed, apply controlled transformation
email: nil # do not forward raw email
}
end
end
Additional practices
- Prefer read-time field selection over write-time redaction; less data traverses your API.
- Validate and sanitize inputs to prevent IDOR and ensure Firestore path correctness.
- Audit logs and monitoring can help detect anomalous access patterns that suggest enumeration attempts.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |