From 7cd2bb69ead5b13208bb969f8353bbf9584c5537 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Wed, 7 Jun 2017 11:37:28 +0530 Subject: [PATCH] Squashed commit of the following: commit ced48c3fcfd1880016b08590e1b50827247b7cfb Author: Arunoda Susiripala Date: Wed Jun 7 11:28:43 2017 +0530 Make sure XSS fix doesn't break our 404 support. commit d40c833aa16660d547df785bb52e69ecf9a717f2 Author: Arunoda Susiripala Date: Wed Jun 7 09:08:41 2017 +0530 Prevent url path based XSS attacks. --- package.json | 3 ++- server/render.js | 3 +++ .../integration/production/test/index.test.js | 20 ++++++++++++++++++- yarn.lock | 4 ++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 90bcfdd4..4212ff55 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,8 @@ "webpack": "2.5.1", "webpack-dev-middleware": "1.10.2", "webpack-hot-middleware": "2.18.0", - "write-file-webpack-plugin": "4.0.2" + "write-file-webpack-plugin": "4.0.2", + "xss-filters": "1.2.7" }, "devDependencies": { "babel-eslint": "7.2.3", diff --git a/server/render.js b/server/render.js index 06ceadbe..b1bbab9a 100644 --- a/server/render.js +++ b/server/render.js @@ -12,6 +12,7 @@ import { loadGetInitialProps } from '../lib/utils' import Head, { defaultHead } from '../lib/head' import App from '../lib/app' import ErrorDebug from '../lib/error-debug' +import xssFilters from 'xss-filters' export async function render (req, res, pathname, query, opts) { const html = await renderToHTML(req, res, pathname, opts) @@ -133,6 +134,8 @@ export async function renderScript (req, res, page, opts) { export async function renderScriptError (req, res, page, error, customFields, opts) { // Asks CDNs and others to not to cache the errored page res.setHeader('Cache-Control', 'no-store, must-revalidate') + // prevent XSS attacks by filtering the page before printing it. + page = xssFilters.uriInSingleQuotedAttr(page) if (error.code === 'ENOENT') { res.setHeader('Content-Type', 'text/javascript') diff --git a/test/integration/production/test/index.test.js b/test/integration/production/test/index.test.js index f8f798a3..40eb47a1 100644 --- a/test/integration/production/test/index.test.js +++ b/test/integration/production/test/index.test.js @@ -6,7 +6,8 @@ import { nextBuild, startApp, stopApp, - renderViaHTTP + renderViaHTTP, + waitFor } from 'next-test-utils' import webdriver from 'next-webdriver' import fetch from 'node-fetch' @@ -59,4 +60,21 @@ describe('Production Usage', () => { browser.close() }) }) + + describe('With XSS Attacks', () => { + it('should prevent URI based attaks', async () => { + const browser = await webdriver(appPort, '/\',document.body.innerHTML="HACKED",\'') + // Wait 5 secs to make sure we load all the client side JS code + await waitFor(5000) + + const bodyText = await browser + .elementByCss('body').text() + + if (/HACKED/.test(bodyText)) { + throw new Error('Vulnerable to XSS attacks') + } + + browser.close() + }) + }) }) diff --git a/yarn.lock b/yarn.lock index b1bb7473..b717e5ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5243,6 +5243,10 @@ xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" +xss-filters@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/xss-filters/-/xss-filters-1.2.7.tgz#59fa1de201f36f2f3470dcac5f58ccc2830b0a9a" + "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"