Skip to content

docs: Unsafe example for converting a message.url to an URL #52494

Description

@mlegenhausen

Affected URL(s)

https://nodejs.org/api/http.html#messageurl

Description of the problem

Following the proposed way of converting request.url to an URL object you can easily come up with the following implementation:

import * as http from 'node:http';

const server = http.createServer((req, res) => {
  console.log(new URL(req.url, `http://${req.headers.host}`));
  res.end();
});

server.listen(3000);

This simple implementation lacks two issues.

  1. You can crash the server via
curl http://localhost:3000//

with following message

TypeError: Invalid URL
    at new URL (node:internal/url:775:36)
    at Server.<anonymous> (file:///server.js:3:26)
    at Server.emit (node:events:518:28)
    at parserOnIncoming (node:_http_server:1151:12)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:119:17) {
  code: 'ERR_INVALID_URL',
  input: '//',
  base: 'http://localhost:3000'
}
  1. You can change the host to whatever you want
curl http://localhost:3000//evil.com/foo/bar

this generates following URL

URL {
  href: 'http://evil.com/foo/bar',
  origin: 'http://evil.com',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'evil.com',
  hostname: 'evil.com',
  port: '',
  pathname: '/foo/bar',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}

This might have security implications if you e. g. base your CSRF protection on the parsed URL host value.

A better approach would it be to concatenate the host with the req.url instead of using the baseUrl parameter from the URL constructor.

import * as http from 'node:http';

const server = http.createServer((req, res) => {
  console.log(new URL(`http://${req.headers.host}${req.url}`));
  res.end();
});

server.listen(3000);

If you now repeat both curls. You get

  1. No crash
URL {
  href: 'http://localhost:3000//',
  origin: 'http://localhost:3000',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost:3000',
  hostname: 'localhost',
  port: '3000',
  pathname: '//',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}
  1. No host change
URL {
  href: 'http://localhost:3000//evil.com/foo/bar',
  origin: 'http://localhost:3000',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost:3000',
  hostname: 'localhost',
  port: '3000',
  pathname: '//evil.com/foo/bar',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    docIssues and PRs related to Node.js documentation.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions