Hono Security Audit
Audit Hono apps. Hono is a small, fast framework targeting Workers/Bun/Deno/Node/Lambda — each runtime has its own security context.
When this skill applies
- Reviewing Hono route handlers and middleware
- Auditing JWT and auth helpers
- Reviewing env bindings across runtimes
- Checking CORS, helmet-equivalent setup
- Confirming runtime-specific concerns (Workers, Lambda, etc.)
Workflow
Follow ../_shared/audit-workflow.md.
Phase 1: Stack detection
grep -E '"hono":' package.json
grep -nE 'import.*hono' src/ | head -5
# Detect runtime
grep -E '"wrangler"|"@cloudflare/workers-types"' package.json && echo "Cloudflare Workers"
grep -E '"@types/bun"|"bun"' package.json && echo "Bun"
grep -E '"@types/aws-lambda"' package.json && echo "AWS Lambda"
Phase 2: Inventory
# Routes and handlers
grep -rn 'app\.\(get\|post\|put\|delete\|use\)' src/ | head -50
# Middleware imports
grep -rn 'from .hono/(jwt|cors|csrf|secure-headers|logger|cache)' src/
# Env access
grep -rn 'c\.env\.' src/
# Variables (per-request context)
grep -rn 'c\.set\|c\.var' src/
Phase 3: Detection — the checks
Middleware setup
- HNO-MW-1
secureHeaders() middleware from hono/secure-headers applied — Hono's equivalent of helmet.
- HNO-MW-2
cors() from hono/cors configured with specific origin allowlist, not * for credentialed requests.
- HNO-MW-3
logger() middleware doesn't log sensitive headers/bodies.
- HNO-MW-4 Order: secureHeaders → cors → auth → routes.
import { Hono } from 'hono';
import { secureHeaders } from 'hono/secure-headers';
import { cors } from 'hono/cors';
import { jwt } from 'hono/jwt';
const app = new Hono();
app.use('*', secureHeaders());
app.use('*', cors({
origin: ['https://app.yourorg.com'],
credentials: true,
}));
// Auth on /api/* except /api/auth/*
app.use('/api/*', async (c, next) => {
if (c.req.path.startsWith('/api/auth/')) return next();
return jwt({ secret: c.env.JWT_SECRET })(c, next);
});
JWT middleware
- HNO-JWT-1
c.env.JWT_SECRET (or per-runtime equivalent) used, not hardcoded.
- HNO-JWT-2 Algorithm specified (
alg: 'HS256' or 'RS256'); never none.
- HNO-JWT-3
c.get('jwtPayload') accessed in downstream handlers; trust scoped to verified claims only.
- HNO-JWT-4 Token expiry validated by the middleware (Hono's JWT helper does this by default; verify).
Environment bindings (Cloudflare Workers)
type Bindings = {
JWT_SECRET: string;
DATABASE_URL: string;
KV: KVNamespace;
R2: R2Bucket;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/api/data', (c) => {
const value = c.env.JWT_SECRET; // ← typed; server-side only
});
- HNO-ENV-1 Bindings types declared so
c.env.X is type-checked.
- HNO-ENV-2 Secrets bound via
wrangler secret put, not in wrangler.toml plaintext.
- HNO-ENV-3 See
cloudflare-workers-security for binding-level concerns.
Validators
- HNO-VAL-1 Input validated via
@hono/zod-validator or similar:import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const schema = z.object({ name: z.string().min(1).max(100), email: z.string().email() });
app.post('/users', zValidator('json', schema), async (c) => {
const data = c.req.valid('json'); // validated, typed
// ...
});
- HNO-VAL-2 Path params and query strings also validated when used.
CSRF
- HNO-CSRF-1 If cookies are used for auth:
csrf() middleware from hono/csrf applied. Hono's CSRF protection uses origin check by default.
- HNO-CSRF-2 For pure Bearer token APIs (no auth cookies), CSRF not needed.
Cookie handling
- HNO-CK-1 Cookies set via
setCookie(c, name, value, { httpOnly: true, secure: true, sameSite: 'Lax' }) — secure defaults explicit.
- HNO-CK-2 Cookie reading via Hono's
getCookie(c) — handles parsing safely.
Error handling
- HNO-ERR-1
app.onError((err, c) => ...) handler returns generic errors in production; logs detail server-side.
- HNO-ERR-2
app.notFound((c) => ...) returns minimal info.
Runtime-specific
Cloudflare Workers:
- See
cloudflare-workers-security for binding/KV/Durable Object concerns.
Node:
- See
nodejs-express-security for body parser limits, prototype pollution, etc.
- Hono on Node uses
@hono/node-server — confirm version current.
Bun:
- Bun runtime concerns: keep Bun version current; verify
bun.lockb reflects intended packages.
Lambda:
- See
aws-lambda-security for cold start, env, IAM concerns.
Dependencies
- HNO-DEP-1 Hono version current (4.x line).
- HNO-DEP-2
@hono/* companion packages match Hono major.
Phase 4: Triage
Critical: missing auth on routes that should be protected; secrets in wrangler.toml; JWT verification accepting none; CORS open with credentials.
Phase 5: Report
Use ../_shared/findings-schema.md. Prefix IDs with HNO-.
1---2name: hono-security3description: Security audit for Hono applications running on Cloudflare Workers, Bun, Deno, Node, or AWS Lambda — covering middleware setup, JWT helper safety, environment binding handling (c.env), CORS, secret management across runtimes, and Hono-specific patterns. Use this skill whenever the user mentions Hono, hono framework, c.req, c.json, c.env, Hono middleware, Hono on Cloudflare/Bun/Node, or asks "audit my Hono app", "Hono security". Trigger when the codebase contains `hono` in package.json.4---56# Hono Security Audit78Audit Hono apps. Hono is a small, fast framework targeting Workers/Bun/Deno/Node/Lambda — each runtime has its own security context.910## When this skill applies1112- Reviewing Hono route handlers and middleware13- Auditing JWT and auth helpers14- Reviewing env bindings across runtimes15- Checking CORS, helmet-equivalent setup16- Confirming runtime-specific concerns (Workers, Lambda, etc.)1718## Workflow1920Follow `../_shared/audit-workflow.md`.2122### Phase 1: Stack detection2324```bash25grep -E '"hono":' package.json26grep -nE 'import.*hono' src/ | head -527# Detect runtime28grep -E '"wrangler"|"@cloudflare/workers-types"' package.json && echo "Cloudflare Workers"29grep -E '"@types/bun"|"bun"' package.json && echo "Bun"30grep -E '"@types/aws-lambda"' package.json && echo "AWS Lambda"31```3233### Phase 2: Inventory3435```bash36# Routes and handlers37grep -rn 'app\.\(get\|post\|put\|delete\|use\)' src/ | head -503839# Middleware imports40grep -rn 'from .hono/(jwt|cors|csrf|secure-headers|logger|cache)' src/4142# Env access43grep -rn 'c\.env\.' src/4445# Variables (per-request context)46grep -rn 'c\.set\|c\.var' src/47```4849### Phase 3: Detection — the checks5051#### Middleware setup5253- **HNO-MW-1** `secureHeaders()` middleware from `hono/secure-headers` applied — Hono's equivalent of helmet.54- **HNO-MW-2** `cors()` from `hono/cors` configured with specific `origin` allowlist, not `*` for credentialed requests.55- **HNO-MW-3** `logger()` middleware doesn't log sensitive headers/bodies.56- **HNO-MW-4** Order: secureHeaders → cors → auth → routes.5758```ts59import { Hono } from 'hono';60import { secureHeaders } from 'hono/secure-headers';61import { cors } from 'hono/cors';62import { jwt } from 'hono/jwt';6364const app = new Hono();6566app.use('*', secureHeaders());67app.use('*', cors({68 origin: ['https://app.yourorg.com'],69 credentials: true,70}));7172// Auth on /api/* except /api/auth/*73app.use('/api/*', async (c, next) => {74 if (c.req.path.startsWith('/api/auth/')) return next();75 return jwt({ secret: c.env.JWT_SECRET })(c, next);76});77```7879#### JWT middleware8081- **HNO-JWT-1** `c.env.JWT_SECRET` (or per-runtime equivalent) used, not hardcoded.82- **HNO-JWT-2** Algorithm specified (`alg: 'HS256'` or `'RS256'`); never `none`.83- **HNO-JWT-3** `c.get('jwtPayload')` accessed in downstream handlers; trust scoped to verified claims only.84- **HNO-JWT-4** Token expiry validated by the middleware (Hono's JWT helper does this by default; verify).8586#### Environment bindings (Cloudflare Workers)8788```ts89type Bindings = {90 JWT_SECRET: string;91 DATABASE_URL: string;92 KV: KVNamespace;93 R2: R2Bucket;94};9596const app = new Hono<{ Bindings: Bindings }>();9798app.get('/api/data', (c) => {99 const value = c.env.JWT_SECRET; // ← typed; server-side only100});101```102103- **HNO-ENV-1** Bindings types declared so `c.env.X` is type-checked.104- **HNO-ENV-2** Secrets bound via `wrangler secret put`, not in `wrangler.toml` plaintext.105- **HNO-ENV-3** See `cloudflare-workers-security` for binding-level concerns.106107#### Validators108109- **HNO-VAL-1** Input validated via `@hono/zod-validator` or similar:110 ```ts111 import { zValidator } from '@hono/zod-validator';112 import { z } from 'zod';113 114 const schema = z.object({ name: z.string().min(1).max(100), email: z.string().email() });115 116 app.post('/users', zValidator('json', schema), async (c) => {117 const data = c.req.valid('json'); // validated, typed118 // ...119 });120 ```121- **HNO-VAL-2** Path params and query strings also validated when used.122123#### CSRF124125- **HNO-CSRF-1** If cookies are used for auth: `csrf()` middleware from `hono/csrf` applied. Hono's CSRF protection uses origin check by default.126- **HNO-CSRF-2** For pure Bearer token APIs (no auth cookies), CSRF not needed.127128#### Cookie handling129130- **HNO-CK-1** Cookies set via `setCookie(c, name, value, { httpOnly: true, secure: true, sameSite: 'Lax' })` — secure defaults explicit.131- **HNO-CK-2** Cookie reading via Hono's `getCookie(c)` — handles parsing safely.132133#### Error handling134135- **HNO-ERR-1** `app.onError((err, c) => ...)` handler returns generic errors in production; logs detail server-side.136- **HNO-ERR-2** `app.notFound((c) => ...)` returns minimal info.137138#### Runtime-specific139140**Cloudflare Workers:**141- See `cloudflare-workers-security` for binding/KV/Durable Object concerns.142143**Node:**144- See `nodejs-express-security` for body parser limits, prototype pollution, etc.145- Hono on Node uses `@hono/node-server` — confirm version current.146147**Bun:**148- Bun runtime concerns: keep Bun version current; verify `bun.lockb` reflects intended packages.149150**Lambda:**151- See `aws-lambda-security` for cold start, env, IAM concerns.152153#### Dependencies154155- **HNO-DEP-1** Hono version current (4.x line).156- **HNO-DEP-2** `@hono/*` companion packages match Hono major.157158### Phase 4: Triage159160Critical: missing auth on routes that should be protected; secrets in `wrangler.toml`; JWT verification accepting `none`; CORS open with credentials.161162### Phase 5: Report163164Use `../_shared/findings-schema.md`. Prefix IDs with `HNO-`.