Commit 59d9489
authored
chore(deps): update dependency koa to v3.1.2 [security] (#430)
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Adoption](https://docs.renovatebot.com/merge-confidence/) |
[Passing](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|
| [koa](https://koajs.com)
([source](https://redirect.github.com/koajs/koa)) | [`3.1.1` →
`3.1.2`](https://renovatebot.com/diffs/npm/koa/3.1.1/3.1.2) |

|

|

|

|
### GitHub Vulnerability Alerts
####
[CVE-2026-27959](https://redirect.github.com/koajs/koa/security/advisories/GHSA-7gcc-r8m5-44qm)
## Summary
Koa's `ctx.hostname` API performs naive parsing of the HTTP Host header,
extracting everything before the first colon without validating the
input conforms to RFC 3986 hostname syntax. When a malformed Host header
containing a `@` symbol (e.g., `evil.com:fake@legitimate.com`) is
received, `ctx.hostname` returns `evil.com` - an attacker-controlled
value. Applications using `ctx.hostname` for URL generation, password
reset links, email verification URLs, or routing decisions are
vulnerable to Host header injection attacks.
## Details
The vulnerability exists in Koa's hostname getter in `lib/request.js`:
```javascript
// Koa 2.16.1 - lib/request.js
get hostname() {
const host = this.host;
if (!host) return '';
if ('[' === host[0]) return this.URL.hostname || ''; // IPv6 literal
return host.split(':', 1)[0];
}
```
The `host` getter retrieves the raw header value with HTTP/2 and proxy
support:
```javascript
// Koa 2.16.1 - lib/request.js
get host() {
const proxy = this.app.proxy;
let host = proxy && this.get('X-Forwarded-Host');
if (!host) {
if (this.req.httpVersionMajor >= 2) host = this.get(':authority');
if (!host) host = this.get('Host');
}
if (!host) return '';
return host.split(',')[0].trim();
}
```
### The Problem
The parsing logic simply splits on the first `:` and returns the first
segment. There is no validation that the resulting string is a valid
hostname per RFC 3986 Section 3.2.2.
**RFC 3986 Section 3.2.2** defines the host component as:
```
host = IP-literal / IPv4address / reg-name
reg-name = *( unreserved / pct-encoded / sub-delims )
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
```
The `@` character is explicitly NOT permitted in the host component - it
is the delimiter separating userinfo from host in the authority
component.
### Attack Vector
When an attacker sends:
```
Host: evil.com:fake@legitimate.com:3000
```
Koa parses this as:
| API | Returns | Notes |
|-----|---------|-------|
| `ctx.get('Host')` | `"evil.com:fake@legitimate.com:3000"` | Raw header
|
| `ctx.hostname` | `"evil.com"` | **Attacker-controlled** |
| `ctx.host` | `"evil.com:fake@legitimate.com:3000"` | Raw header value
|
| `ctx.origin` | `"http://evil.com:fake@legitimate.com:3000"` | Protocol
+ malformed host |
The `ctx.hostname` API returns `evil.com` because the parser splits on
the first `:` without understanding that `evil.com:fake@legitimate.com`
is a malformed authority component where `evil.com:fake` would be
interpreted as userinfo by a proper URI parser.
### Additional Concern: `ctx.origin`
Koa's `ctx.origin` property concatenates protocol and host without
validation:
```javascript
// lib/request.js
get origin() {
return `${this.protocol}://${this.host}`;
}
```
Applications using `ctx.origin` for URL generation receive the full
malformed Host header value, creating URLs with embedded credentials
that browsers may interpret as userinfo.
### HTTP/2 Consideration
Koa explicitly checks `httpVersionMajor >= 2` to read the `:authority`
pseudo-header:
```javascript
if (this.req.httpVersionMajor >= 2) host = this.get(':authority');
```
The same vulnerability applies - malformed `:authority` values
containing userinfo would be accepted and parsed identically.
## PoC
### Setup
```javascript
// server.js
const Koa = require('koa');
const app = new Koa();
// Simulates password reset URL generation (common vulnerable pattern)
app.use(async ctx => {
if (ctx.path === '/forgot-password') {
const resetToken = 'abc123securtoken';
const resetUrl = `${ctx.protocol}://${ctx.hostname}/reset?token=${resetToken}`;
ctx.body = {
message: 'Password reset link generated',
resetUrl: resetUrl,
debug: {
rawHost: ctx.get('Host'),
parsedHostname: ctx.hostname,
origin: ctx.origin,
protocol: ctx.protocol
}
};
}
});
app.listen(3000, () => console.log('Server on http://localhost:3000'));
```
### Exploit
```bash
curl -H "Host: evil.com:fake@localhost:3000" http://localhost:3000/forgot-password
```
### Result
```json
{
"message": "Password reset link generated",
"resetUrl": "http://evil.com/reset?token=abc123securtoken",
"debug": {
"rawHost": "evil.com:fake@localhost:3000",
"parsedHostname": "evil.com",
"origin": "http://evil.com:fake@localhost:3000",
"protocol": "http"
}
}
```
The password reset URL points to `evil.com` instead of the legitimate
server. In a real attack:
1. Attacker requests password reset for victim's email with malicious
Host header
2. Server generates reset link using `ctx.hostname` →
`https://evil.com/reset?token=SECRET`
3. Victim receives email with poisoned link
4. Victim clicks link, token is sent to attacker's server
5. Attacker uses token to reset victim's password
### Additional Test Cases
```bash
# Basic injection
curl -H "Host: evil.com:x@legitimate.com" http://localhost:3000/forgot-password
# Result: hostname = "evil.com"
# With port preservation attempt
curl -H "Host: evil.com:443@​legitimate.com:3000" http://localhost:3000/forgot-password
# Result: hostname = "evil.com"
# Unicode/encoded variations
curl -H "Host: evil.com:x%40legitimate.com" http://localhost:3000/forgot-password
# Result: hostname = "evil.com"
```
### Deployment Consideration
For this attack to succeed in production, the malicious Host header must
reach the Koa application. This occurs when:
1. **No reverse proxy** - Application directly exposed to internet
2. **Misconfigured proxy** - Proxy doesn't override/validate Host header
3. **Proxy trust enabled** (`app.proxy = true`) - `X-Forwarded-Host` can
be injected
4. **Default virtual host** - Server is the catch-all for unrecognized
Host headers
## Impact
### Vulnerability Type
- CWE-20: Improper Input Validation
- CWE-644: Improper Neutralization of HTTP Headers for Scripting Syntax
### Attack Scenarios
**1. Password Reset Poisoning (High Severity)**
- Attacker hijacks password reset tokens by poisoning reset URLs
- Requires victim to click link in email
- Results in account takeover
**2. Email Verification Bypass**
- Attacker poisons email verification links
- Can verify attacker-controlled email on victim accounts
**3. OAuth/SSO Callback Manipulation**
- Applications using `ctx.hostname` for OAuth redirect URIs
- Attacker redirects OAuth callbacks to malicious server
- Results in token theft
**4. Web Cache Poisoning**
- If responses are cached without Host in cache key
- Poisoned URLs served to all users
- Persistent XSS/phishing via cached responses
**5. Server-Side Request Forgery (SSRF)**
- Internal routing decisions based on `ctx.hostname`
- Attacker manipulates which backend receives requests
### Who Is Impacted
- **Direct impact**: Any Koa application using `ctx.hostname` or
`ctx.origin` for URL generation without additional validation
- **Common patterns**: Password reset, email verification, webhook URL
generation, multi-tenant routing, OAuth implementations
---
### Release Notes
<details>
<summary>koajs/koa (koa)</summary>
###
[`v3.1.2`](https://redirect.github.com/koajs/koa/releases/tag/v3.1.2)
[Compare
Source](https://redirect.github.com/koajs/koa/compare/v3.1.1...v3.1.2)
#### What's Changed
- fix: typo in troubleshooting.md by
[@​WuMingDao](https://redirect.github.com/WuMingDao) in
[#​1916](https://redirect.github.com/koajs/koa/pull/1916)
- build(deps-dev): bump qs from 6.14.0 to 6.14.1 by
[@​dependabot](https://redirect.github.com/dependabot)\[bot] in
[#​1921](https://redirect.github.com/koajs/koa/pull/1921)
- build(deps): bump http-errors from 2.0.0 to 2.0.1 by
[@​dependabot](https://redirect.github.com/dependabot)\[bot] in
[#​1919](https://redirect.github.com/koajs/koa/pull/1919)
- build(deps): bump mime-types from 3.0.1 to 3.0.2 by
[@​dependabot](https://redirect.github.com/dependabot)\[bot] in
[#​1918](https://redirect.github.com/koajs/koa/pull/1918)
- docs: use correct term "Server-Sent Events" in guide by
[@​jtr860830](https://redirect.github.com/jtr860830) in
[#​1920](https://redirect.github.com/koajs/koa/pull/1920)
- build(deps): bump content-disposition from 0.5.4 to 1.0.1 by
[@​dependabot](https://redirect.github.com/dependabot)\[bot] in
[#​1917](https://redirect.github.com/koajs/koa/pull/1917)
- build(deps-dev): bump js-yaml from 4.1.0 to 4.1.1 by
[@​dependabot](https://redirect.github.com/dependabot)\[bot] in
[#​1922](https://redirect.github.com/koajs/koa/pull/1922)
- fix(security): Host Header Injection via `ctx.hostname` by
[@​killagu](https://redirect.github.com/killagu)
<GHSA-7gcc-r8m5-44qm>
#### New Contributors
- [@​WuMingDao](https://redirect.github.com/WuMingDao) made their
first contribution in
[#​1916](https://redirect.github.com/koajs/koa/pull/1916)
- [@​jtr860830](https://redirect.github.com/jtr860830) made their
first contribution in
[#​1920](https://redirect.github.com/koajs/koa/pull/1920)
**Full Changelog**:
<koajs/koa@v3.1.1...v3.1.2>
</details>
---
### Configuration
📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no
schedule defined).
🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/mnahkies/openapi-code-generator).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My40My4yIiwidXBkYXRlZEluVmVyIjoiNDMuNDMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>1 parent 2038ffe commit 59d9489
2 files changed
+9
-15
lines changedSome generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
| |||
0 commit comments