Phishing Api Keys in Adonisjs
How Phishing API Keys Manifests in Adonisjs
Phishing attacks that target API keys often start with a convincing fake login page or a deceptive email that tricks a developer into entering credentials for a service that issues keys (e.g., a payment gateway, a cloud provider, or an internal auth server). Once the attacker obtains the key, they can abuse any Adonisjs endpoint that trusts that key as a bearer token or as a secret stored in environment variables.
In an Adonisjs codebase, the most common places where a stolen key becomes useful are:
- Controller methods that read
Env.get('API_KEY')and forward it to downstream services. - Middleware that logs the full request object (including
Authorizationheaders) to a file or external logging service. - Error handlers that return stack traces or configuration dumps when
debugis enabled in production. - Routes that expose configuration via a health‑check or debug endpoint (e.g.,
/envor/config) without proper authentication.
For example, a developer might accidentally commit a .env file containing API_KEY=sk_live_.... If the repository is public, an attacker can harvest the key and then use it to call any Adonisjs route that trusts the key, such as a webhook endpoint that forwards payments to a third‑party API. Because Adonisjs does not automatically redact secrets from logs or error output, the key can also leak through log aggregation services when an error occurs and the framework prints the request context.
These patterns are not unique to Adonisjs, but the framework’s conventions — Env.get for configuration, the default Logger middleware, and the exceptionHandler — make it easy to introduce the vulnerability if developers are not aware of the data‑flow.
Adonisjs-Specific Detection
Detecting whether an Adonisjs application is leaking or improperly handling API keys involves both static inspection of the codebase and dynamic scanning of the running API. The following checks are practical:
- Search for
Env.getcalls that are directly returned in a response. - Look for logging middleware that logs
ctx.request.headers()without redacting theAuthorizationheader. - Verify that
config/app.jshasdebug: falsein production and that theexceptionHandlerdoes not exposeerr.messageorerr.stackto the client. - Check for any route that returns the contents of
process.envor a configuration object (common in ad‑hoc debug controllers).
Dynamic testing with middleBrick complements these checks. By submitting the base URL of your Adonisjs API, middleBrick performs unauthenticated black‑box scans that include the “Data Exposure” and “Unsafe Consumption” categories. If an API key is reflected in a response body, header, or error message, middleBrick will flag it as a finding with severity “high” and provide the exact endpoint and HTTP status where the leak occurred.
Example CLI usage:
# Install the middleBrick CLI (npm package)
npm i -g middlebrick
# Scan your Adonisjs API
middlebrick scan https://api.example.com
The output will be a JSON report that you can pipe into CI pipelines. In a GitHub Action, you would add:
name: API Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick scan
uses: middlebrick/action@v1
with:
api-url: https://staging.example.com
fail-below: B # fail the job if score drops below B
Similarly, the MCP Server integration lets you invoke a scan directly from your AI coding assistant (e.g., Claude or Cursor) while you are editing an Adonisjs controller, giving immediate feedback if a newly added endpoint inadvertently returns a secret.
Adonisjs-Specific Remediation
Remediation focuses on ensuring that API keys never leave the trusted boundary of the server and that logging or error handling does not inadvertently expose them. Below are concrete Adonisjs‑native fixes.
1. Load secrets through Config and never return them.
Adonisjs encourages storing secrets in .env and accessing them via the Config service. Ensure that any controller that needs to call an external service retrieves the key from config and uses it only for outbound requests.
// start/app.js
const Env = use('Env')
module.exports = {
// ...
providers: [
// ...
'@adonisjs/core/providers/EnvProvider'
]
}
// config/api.js
module.exports = {
externalService: {
apiKey: Env.get('EXTERNAL_API_KEY', '')
}
}
// app/Controllers/Http/PaymentController.js
const Config = use('Config')
const Http = use('Http')
class PaymentController {
async charge ({ request, response }) {
const key = Config.get('api.externalService.apiKey')
if (!key) {
return response.status(500).send({ error: 'Missing API key' })
}
// Use the key only in an outbound request, never echo it back
const externalResp = await Http.post('https://external.api/pay', {
amount: request.input('amount')
}, {
headers: { Authorization: `Bearer ${key}` }
})
return response.send(externalResp.body)
}
}
module.exports = PaymentController
2. Prevent logging of the Authorization header.
Create a custom logger middleware that redacts sensitive headers before passing the request to the default logger.
// app/Middleware/RedactAuthHeader.js
const Logger = use('Logger')
class RedactAuthHeader {
async handle ({ request, response }, next) {
// Clone headers and remove Authorization
const safeHeaders = request.headers()
delete safeHeaders.authorization
Logger.info('Incoming request', {
method: request.method(),
url: request.url(),
headers: safeHeaders
})
await next()
}
}
module.exports = RedactAuthHeader
// start/kernel.js
const Middleware = use('Middleware')
Middleware.global = [
// ...
'App/Middleware/RedactAuthHeader'
]
3. Disable detailed error output in production.
Set debug: false in config/app.js and customize the exception handler to return a generic message.
// config/app.js
module.exports = {
debug: Env.get('NODE_ENV', 'development') !== 'production',
// ...
}
// app/Exceptions/Handler.js
const { HttpContextException } = use('@adonisjs/generic-exceptions')
class Handler {
async error ({ error, response }) {
if (error.code === 'E_ROUTE_NOT_FOUND') {
return response.notFound().send({ error: 'Not found' })
}
// Never send stack trace or internal details to the client
return response.status(error.status || 500).send({ error: 'Internal server error' })
}
}
module.exports = Handler
4. Add .env to .gitignore and provide an .env.example file.
This prevents accidental commits of real keys while still documenting the required variables.
# .gitignore
.env
# .env.example
EXTERNAL_API_KEY=your_key_here
OTHER_SECRET=example
By applying these Adonisjs‑specific controls, you eliminate the most common pathways through which a phishing‑obtained API key could be abused or leaked. After fixing the code, run middleBrick again to confirm that the finding disappears and the security score improves.