URL parsing with Qwik uses the new URL(a, b)
constructor. A little-known fact about this constructor is that if an attacker controls a
they have complete control of the finally resolved URL.
For example:
const url = new URL(attacker_value, "http://localhost")
By entering //test.com
, we can change the origin to resolve to http://test.com
.
The getUrl
function is used in production and development node servers link.
export function getUrl(req: IncomingMessage) {
const origin = ORIGIN ?? getOrigin(req);
return new URL((req as any).originalUrl || req.url || '/', origin);
}
This follows the earlier vulnerable pattern, and the origin can be controlled.
Cloudflare, Vercel and other deployments which do not use this mechanism are not vulnerable.
Start any node Qwik Server.
For simplicity, I am using curl to simulate the request from the browser. In this case, the origin is “attacker.com”. An actual attack would use the victim’s web browser to send these requests and be unable to edit headers manually.
Observe curl -X POST http://localhost:5173/ -H "Origin: http://attacker.com"
causes a CSRF error
Observe curl -X POST http://localhost:5173//attacker.com/ -H "Origin: http://attacker.com"
does not cause a CSRF error!
The internal URL now points to http://attacker.com
; Qwik thinks it’s attacker.com
!