Skip to content

Commit

Permalink
fix(server): Security fix: only listen to private network address by …
Browse files Browse the repository at this point in the history
…default

See also vitejs#2820 which makes this problem critically worse.

Up until now, Vite has been listening on all public IP addresses
by default, which could be a potential security vulnerability.

This fixes the default behavior, so Vite only listens on 127.0.0.1.

You can get the old behavior back (listen to all IPs) by running
with the --listen-public CLI flag, or setting

```
export default defineConfig({
  server: {
    listenPublic: true
  }
  // ... more config here
})
```

in the Vite config file.
  • Loading branch information
eirslett committed Apr 5, 2021
1 parent d73c1fa commit 2bafd99
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 9 deletions.
12 changes: 12 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,18 @@ export default ({ command, mode }) => {

Set to `true` to exit if port is already in use, instead of automatically try the next available port.

### server.listenPublic

- **Type:** `boolean`
- **Default:** `false`

If you set this to true, the server will listen not only on 127.0.0.1 (localhost), but
accept connections from your machine's public IP address. This means other machines on the same network
(or potentially the rest of the internet) may be able to access your dev server.

This option can be practical for showcasing your work across devices, but has security implications, so
it is turned off by default. Make sure you understand the potential risks before you choose to turn it on!

### server.https

- **Type:** `boolean | https.ServerOptions`
Expand Down
10 changes: 9 additions & 1 deletion packages/vite/src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ cli
.option('--host <host>', `[string] specify hostname`)
.option('--port <port>', `[number] specify port`)
.option('--https', `[boolean] use TLS + HTTP/2`)
.option('--listen-public', `[boolean] listen to public network interfaces`)
.option('--open [path]', `[boolean | string] open browser on startup`)
.option('--cors', `[boolean] enable CORS`)
.option('--strictPort', `[boolean] exit if specified port is already in use`)
Expand Down Expand Up @@ -186,11 +187,16 @@ cli
cli
.command('preview [root]')
.option('--port <port>', `[number] specify port`)
.option('--listen-public', `[boolean] listen to public network interfaces`)
.option('--open [path]', `[boolean | string] open browser on startup`)
.action(
async (
root: string,
options: { port?: number; open?: boolean | string } & GlobalCLIOptions
options: {
port?: number
listenPublic?: boolean
open?: boolean | string
} & GlobalCLIOptions
) => {
try {
const config = await resolveConfig(
Expand All @@ -200,10 +206,12 @@ cli
configFile: options.config,
logLevel: options.logLevel,
server: {
listenPublic: options.listenPublic,
open: options.open
}
},
'serve',

'development'
)
await preview(config, options.port)
Expand Down
15 changes: 11 additions & 4 deletions packages/vite/src/node/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ export async function preview(config: ResolvedConfig, port = 5000) {

const options = config.server || {}
const hostname = options.host || 'localhost'
const listenPublic = options.listenPublic || false
const protocol = options.https ? 'https' : 'http'
const logger = config.logger
const base = config.base

httpServer.listen(port, () => {
const listenHostname = listenPublic ? options.host || '0.0.0.0' : '127.0.0.1'
httpServer.listen(port, listenHostname, () => {
logger.info(
chalk.cyan(`\n vite v${require('vite/package.json').version}`) +
chalk.green(` build preview server running at:\n`)
Expand All @@ -57,12 +59,17 @@ export async function preview(config: ResolvedConfig, port = 5000) {
type: detail.address.includes('127.0.0.1')
? 'Local: '
: 'Network: ',
host: detail.address.replace('127.0.0.1', hostname)
host: detail.address.replace('127.0.0.1', hostname),
disabled: !listenPublic && !detail.address.includes('127.0.0.1')
}
})
.forEach(({ type, host }) => {
.forEach(({ type, host, disabled }) => {
const url = `${protocol}://${host}:${chalk.bold(port)}${base}`
logger.info(` > ${type} ${chalk.cyan(url)}`)
logger.info(
` > ${type} ${chalk.cyan(url)}${
disabled ? chalk.red(' (disabled)') : ''
}`
)
})
)

Expand Down
23 changes: 19 additions & 4 deletions packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ import { createMissingImporterRegisterFn } from '../optimizer/registerMissing'
export interface ServerOptions {
host?: string
port?: number
/**
* Listen to public interfaces
* true: the dev server will be available on public IP addresses
* false: the dev server will only be available on localhost
*/
listenPublic?: boolean
/**
* Enable TLS + HTTP/2.
* Note: this downgrades to TLS only when the proxy option is also used.
Expand Down Expand Up @@ -532,6 +538,7 @@ async function startServer(
let port = inlinePort || options.port || 3000
let hostname = options.host || 'localhost'
if (hostname === '0.0.0.0') hostname = 'localhost'
const listenPublic = options.listenPublic || false
const protocol = options.https ? 'https' : 'http'
const info = server.config.logger.info
const base = server.config.base
Expand All @@ -554,7 +561,10 @@ async function startServer(

httpServer.on('error', onError)

httpServer.listen(port, options.host, () => {
const listenHostname = listenPublic
? options.host || '0.0.0.0'
: '127.0.0.1'
httpServer.listen(port, listenHostname, () => {
httpServer.removeListener('error', onError)

info(
Expand All @@ -573,12 +583,17 @@ async function startServer(
type: detail.address.includes('127.0.0.1')
? 'Local: '
: 'Network: ',
host: detail.address.replace('127.0.0.1', hostname)
host: detail.address.replace('127.0.0.1', hostname),
disabled: !listenPublic && !detail.address.includes('127.0.0.1')
}
})
.forEach(({ type, host }) => {
.forEach(({ type, host, disabled }) => {
const url = `${protocol}://${host}:${chalk.bold(port)}${base}`
info(` > ${type} ${chalk.cyan(url)}`)
info(
` > ${type} ${chalk.cyan(url)}${
disabled ? chalk.red(' (disabled)') : ''
}`
)
})
)

Expand Down

0 comments on commit 2bafd99

Please sign in to comment.