Add validation to server methods (#49104)

This PR adds a `key` param to the IPC server to validate if a request is from a child process or not.
This commit is contained in:
Shu Ding 2023-05-02 20:46:13 +02:00 committed by GitHub
parent 692d28b193
commit f3068a5bbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 21 additions and 2 deletions

View file

@ -1271,9 +1271,10 @@ export default class DevServer extends Server {
private async invokeIpcMethod(method: string, args: any[]): Promise<any> {
const ipcPort = process.env.__NEXT_PRIVATE_ROUTER_IPC_PORT
const ipcKey = process.env.__NEXT_PRIVATE_ROUTER_IPC_KEY
if (ipcPort) {
const res = await invokeRequest(
`http://${this.hostname}:${ipcPort}?method=${
`http://${this.hostname}:${ipcPort}?key=${ipcKey}&method=${
method as string
}&args=${encodeURIComponent(JSON.stringify(args))}`,
{

View file

@ -2,6 +2,7 @@ import type NextServer from '../next-server'
import { genExecArgv, getNodeOptionsWithoutInspect } from './utils'
import { deserializeErr, errorToJSON } from '../render'
import { IncomingMessage } from 'http'
import crypto from 'crypto'
import isError from '../../lib/is-error'
// we can't use process.send as jest-worker relies on
@ -12,11 +13,23 @@ export async function createIpcServer(
): Promise<{
ipcPort: number
ipcServer: import('http').Server
ipcValidationKey: string
}> {
// Generate a random key in memory to validate messages from other processes.
// This is just a simple guard against other processes attempting to send
// traffic to the IPC server.
const ipcValidationKey = crypto.randomBytes(32).toString('hex')
const ipcServer = (require('http') as typeof import('http')).createServer(
async (req, res) => {
try {
const url = new URL(req.url || '/', 'http://n')
const key = url.searchParams.get('key')
if (key !== ipcValidationKey) {
return res.end()
}
const method = url.searchParams.get('method')
const args: any[] = JSON.parse(url.searchParams.get('args') || '[]')
@ -61,12 +74,14 @@ export async function createIpcServer(
return {
ipcPort,
ipcServer,
ipcValidationKey,
}
}
export const createWorker = (
serverPort: number,
ipcPort: number,
ipcValidationKey: string,
isNodeDebugging: boolean | 'brk' | undefined,
type: 'pages' | 'app',
useServerActions?: boolean
@ -88,6 +103,7 @@ export const createWorker = (
.trim(),
__NEXT_PRIVATE_RENDER_WORKER: type,
__NEXT_PRIVATE_ROUTER_IPC_PORT: ipcPort + '',
__NEXT_PRIVATE_ROUTER_IPC_KEY: ipcValidationKey,
NODE_ENV: process.env.NODE_ENV,
...(type === 'app'
? {

View file

@ -268,11 +268,12 @@ export default class NextNodeServer extends BaseServer {
this.renderWorkersPromises = new Promise<void>(async (resolveWorkers) => {
try {
this.renderWorkers = {}
const { ipcPort } = await createIpcServer(this)
const { ipcPort, ipcValidationKey } = await createIpcServer(this)
if (this.hasAppDir) {
this.renderWorkers.app = createWorker(
this.port || 0,
ipcPort,
ipcValidationKey,
options.isNodeDebugging,
'app',
this.nextConfig.experimental.serverActions
@ -281,6 +282,7 @@ export default class NextNodeServer extends BaseServer {
this.renderWorkers.pages = createWorker(
this.port || 0,
ipcPort,
ipcValidationKey,
options.isNodeDebugging,
'pages'
)