Api Key Exposure in Hanami with Postgresql
Api Key Exposure in Hanami with Postgresql — how this specific combination creates or exposes the vulnerability
Hanami is a Ruby web framework that encourages explicit architecture and strict separation of concerns. When Hanami applications interact with Postgresql, developers often store database credentials, external service tokens, or encryption keys in environment variables or initializer files. If those keys are inadvertently logged, serialized, or exposed through an API endpoint, the combination of Hanami’s object-oriented structure and Postgresql’s role-based access control can amplify the impact of a leak.
For example, a Hanami app might define a repository class that reads DATABASE_URL or a custom API_KEY from ENV. If an attacker gains read access to application logs, error messages, or backup artifacts stored in Postgresql tables (for instance, a mistakenly created debug_log table), they can retrieve these keys. Postgresql’s row-level security may not be enforced for certain read paths, especially during development, allowing an unauthorized database user or compromised application process to dump sensitive columns.
Another common scenario involves connection strings embedded in Hanami’s config/environment.rb or via hanami config commands. If these configurations are accidentally serialized into JSON or YAML responses—such as an unguarded admin endpoint that returns system diagnostics—Postgresql can act as a storage layer for those artifacts if backups or audit tables capture them. Because Hanami encourages immutable, value objects, developers may mistakenly assume that storing configuration in plain Ruby constants is safe, but those constants can appear in memory dumps or log entries that ultimately persist in Postgresql-based logging schemas.
The risk is further compounded when Hanami applications use Postgresql’s LISTEN/NOTIFY features for real-time updates. If a notification payload includes an API key or a token, and that channel is not properly restricted, an unauthenticated subscriber could intercept sensitive data. Additionally, ORM layers built on top of Postgresql, such as those using sequel or active_record adapters, might expose keys through misconfigured query logs or through error messages that reveal SQL statements containing literal values.
To detect such exposures with middleBrick, you can submit your Hanami service endpoint for a scan. The tool runs unauthenticated checks across multiple security domains, including Data Exposure and Unsafe Consumption, to identify whether API keys appear in responses, logs, or error payloads. Because middleBrick maps findings to frameworks like OWASP API Top 10 and provides remediation guidance, it helps you understand how a leaked key in a Hanami + Postgresql context could lead to privilege escalation or data exfiltration.
Postgresql-Specific Remediation in Hanami — concrete code fixes
Securing API keys in a Hanami application that uses Postgresql requires changes at the configuration, runtime, and database layers. Below are concrete, actionable steps with valid Postgresql code examples tailored for Hanami.
1. Use environment variables and avoid hardcoding
Never embed API keys directly in Hanami initializers. Instead, rely on environment variables and validate their presence at boot.
# config/environment.rb
require "hanami/config/environment"
module MyApp
class Environment < Hanami::Environment
def initialize
super
# Ensure required keys are present
raise "ENV['DATABASE_URL'] missing" if ENV["DATABASE_URL"].to_s.empty?
raise "ENV['API_KEY'] missing" if ENV["API_KEY"].to_s.empty?
end
end
end
2. Configure Postgresql connections securely
Use connection strings that reference environment variables and avoid storing credentials in code. Apply the principle of least privilege to the Postgresql role used by Hanami.
# config/database.yml (if using YAML-style setup with a DSN)
development:
url: postgresql://<%= ENV["DB_USER"] %>:<%= ENV["DB_PASS"] %>@localhost:5432/<%= ENV["DB_NAME"] %>
# Or via direct URL
# DATABASE_URL=postgresql://lowpriv_user:[email protected]:5432/hanami_prod
In Postgresql, create a restricted role for Hanami:
-- Execute in psql
CREATE ROLE hanami_app WITH LOGIN PASSWORD 'strong_password' NOINHERIT;
GRANT CONNECT ON DATABASE hanami_production TO hanami_app;
GRANT USAGE ON SCHEMA public TO hanami_app;
GRANT SELECT, INSERT, UPDATE ON TABLE users, sessions TO hanami_app;
-- Explicitly deny dangerous privileges
REVOKE CREATE ON DATABASE hanami_production FROM hanami_app;
REVOKE ALL ON SCHEMA public FROM PUBLIC;
3. Avoid logging sensitive values
Ensure that Hanami’s logger and any custom debug tables do not capture API keys or connection strings. If you must store logs in Postgresql, sanitize sensitive fields.
# lib/hanami/logging_filter.rb
module Hanami
module LoggingFilter
FILTER_KEYS = ["api_key", "authorization", "password", "token"].freeze
def call(request)
filtered_params = request.params.map do |key, value|
[key, FILTER_KEYS.include?(key) ? "[FILTERED]" : value]
end.to_h
super(request.dup.update_params(filtered_params))
end
end
end
Create a Postgresql audit table with masked columns:
CREATE TABLE api_logs (
id SERIAL PRIMARY KEY,
endpoint TEXT NOT NULL,
method TEXT NOT NULL,
masked_params JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Function to sanitize before insert
CREATE OR REPLACE FUNCTION log_sanitized_params(params JSONB)
RETURNS JSONB AS $$
DECLARE
sanitized JSONB := params;
key TEXT;
BEGIN
FOREACH key IN ARRAY ARRAY['api_key','token','password']
LOOP
sanitized := jsonb_set(sanitized, ARRAY[key], '"[FILTERED]"'::jsonb, true);
END LOOP;
RETURN sanitized;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
4. Secure handling of notifications and external calls
If your Hanami app uses Postgresql LISTEN/NOTIFY, avoid placing secrets in payloads. If you must transmit sensitive references, use opaque identifiers and resolve them server-side.
# Sending a notification without secrets
NOTIFY channel_name, '{"event":"refresh","record_id":"123"}';
-- Receiver side in Hanami (pseudo-code)
Hanami::Notifications.subscribe("channel_name") do |payload|
data = JSON.parse(payload)
# Fetch sensitive data using a secure service, not from payload
end
5. Continuous validation with middleBrick
Use the middleBrick CLI to regularly scan your Hanami endpoints for exposed keys. The tool checks Data Exposure and Unsafe Consumption checks, and with the Pro plan you can enable continuous monitoring to catch regressions early.
$ middlebrick scan https://api.yourapp.com/health
Frequently Asked Questions
How can I verify that my Postgresql role for Hanami has the minimum required privileges?
\dp to inspect table privileges, and SELECT rolname, rolsuper, rolinherit FROM pg_roles WHERE rolname='hanami_app';. Ensure no CREATE or DROP rights are granted.