Hono’s ipRestriction() middleware fails to handle IPv4-mapped IPv6 addresses properly. Developers using IPv4-only allow or deny lists in Node.js environments risk letting unauthorized traffic through. A client connecting from 127.0.0.1 appears as ::ffff:127.0.0.1, bypassing rules like denyList: ['127.0.0.1'].
This bug surfaced in a recent security advisory rated medium severity. Hono, a lightweight web framework built for edge runtimes like Cloudflare Workers, Deno, Bun, and Node.js, powers thousands of deployments. Its ipRestriction() middleware classifies client IPs by string format: anything with a colon counts as IPv6. IPv4-mapped addresses, standard in Node.js dual-stack sockets, get treated as full IPv6, skipping IPv4 CIDR matches like 127.0.0.0/8 or 10.0.0.0/8.
Why Node.js Does This
Node.js defaults to dual-stack IPv6 sockets. When an IPv4 client connects, the OS maps it to an IPv6 address prefixed with ::ffff:. Run net.createServer() and inspect socket.remoteAddress—pure IPv4 yields the mapped form. This setup simplifies code but breaks naive string-based IP filters.
Test it yourself. Spin up a basic Node.js HTTP server:
$ node -e "
const http = require('http');
http.createServer((req, res) => {
console.log(req.socket.remoteAddress);
res.end('OK');
}).listen(3000);
"
Connect from localhost via IPv4 (curl http://127.0.0.1:3000). Output: ::ffff:127.0.0.1. Hono’s middleware sees this string, detects the colon, routes to IPv6 logic, and ignores your IPv4 rules.
This isn’t Hono’s invention. Similar issues plague other frameworks and libraries relying on string compares without normalization. Express.js users hit it years ago with custom middleware; Nginx configs need explicit real_ip_header tweaks for proxies. But Hono markets speed and simplicity—ironic when a core security primitive falters on basics.
Impact: Silent Bypasses in Production
Affected apps include any using ipRestriction({allowList: ['127.0.0.1'], denyAll: true}) or equivalents. Attackers from blocked IPv4 ranges slip past deny lists. Legit localhost admin panels block developers. In cloud setups like Vercel or Fly.io, where Node.js dominates, this hits APIs, dashboards, and rate limiters.
Scale matters: Hono claims sub-microsecond latencies, drawing high-traffic services. A 2023 Bun benchmark showed Hono handling 1.5 million req/s on a single core—far outpacing Express. If your service restricts by IP for compliance (GDPR, PCI-DSS) or ops (internal tools), failed matches mean audit failures or breaches.
Worse, it compounds with proxies. Cloudflare or AWS ALB forward client IPs via X-Forwarded-For, but Hono must parse those correctly too. Mismatched formats amplify risks.
Fixes exist. Hono patched in v3.11.4 (October 2024): normalize mapped addresses to IPv4 equivalents before matching. Update now:
npm update hono
Workarounds pre-patch: use ipaddr.js or Node’s os.networkInterfaces() for parsing. Canonicalize manually:
import { parse as ipaddrParse } from 'ipaddr.js';
function normalizeIP(ip) {
const addr = ipaddrParse(ip);
return addr ? addr.toNormalizedString() : ip;
}
// In middleware
const clientIP = normalizeIP(req.header('x-forwarded-for') || req.socket.remoteAddress);
Vendor response: prompt disclosure, quick fix. Fair play—they didn’t hide it. But docs glossed over dual-stack quirks until now. Developers: audit your IP logic. String matches suffice for prototypes, not prod security.
Broader lesson: IP restrictions make shaky access controls anyway. Spoofable via proxies, useless against VPNs or Tor. Pair with JWTs, mTLS, or API keys. This bug underscores why: one mapping glitch undoes your whitelist.
Check your stack. If Hono + Node.js + IPv4 rules, test with curl --interface lo from localhost. No match? You’re exposed. Patch, normalize, or ditch IP bans for real auth.