Skip to content

Security: afosto/graphql-playground

Security

SECURITY.md

Known Vulnerabilities

XSS Reflection Vulnerability

the origin of the vulnerability is in renderPlaygroundPage, found in graphql-playground-html

Impact

When using

  • renderPlaygroundPage(),
  • koaPlayground()
  • expressPlayground()
  • koaPlayground()
  • lambdaPlayground()
  • any downstream dependent packages that use these functions

without sanitization of user input, your application is vulnerable to an XSS Reflecton Attack. This is a serious vulnerability that could allow for exfiltration of data or user credentials, or to disrupt systems.

We've provided 'an example of the xss using the express middleware

Impacted Packages

All versions of these packages are impacted until those specified below, which are now safe for user defined input:

  • graphql-playground-html: ☔ safe @ 1.6.22
  • graphql-playground-express ☔ safe @ 1.7.16
  • graphql-playground-koa ☔ safe @ 1.6.15
  • graphql-playground-hapi ☔ safe @ 1.6.13
  • graphql-playground-lambda ☔ safe @ 1.7.17

Static input was always safe

These examples are safe for all versions because input is static

with express and renderPlaygroundPage:

app.get('/playground', (req) => {
  res.html(
    renderPlaygroundPage({
      endpoint: `/our/graphql`,
    }),
  )
  next()
})

with expressPlayground:

// params
app.get('/playground', (req) =>
  expressPlayground({
    endpoint: `/our/graphql`,
    settings: { 'editor.theme': req.query.darkMode ? 'dark' : 'light' },
  }),
)

with koaPlayground:

const koa = require('koa')
const koaRouter = require('koa-router')
const koaPlayground = require('graphql-playground-middleware-koa')

const app = new koa()
const router = new koaRouter()

router.all('/playground', koaPlayground({ endpoint: '/graphql' }))

Vulnerable Examples

Here are some examples where the vulnerability would be present before the patch, because of unfiltered user input

const express = require('express')
const expressPlayground = require('graphql-playground-middleware-express')
  .default

const app = express()

app.use(express.json())

// params
app.get('/playground/:id', (req) =>
  expressPlayground({
    endpoint: `/our/graphql/${req.params.id}`,
  }),
)

// params
app.get('/playground', (req) =>
  expressPlayground({
    endpoint: `/our/graphql`,
    // any settings that are unsanitized user input, not just `endpoint`
    settings: { 'editor.fontFamily': req.query.font },
  }),
)

See a proof of concept to understand the vulnerability better

Workaround

To fix this issue without the update, you can sanitize however you want.

We suggest using xss (what we use for our own fix)

For example, with graphql-playground-middleware-express:

const express = require('express')
const { filterXSS } = require('xss')
const expressPlayground = require('graphql-playground-middleware-express')
  .default


const app = express()

const filter = (val) => filterXSS(val, {
  whitelist: [],
  stripIgnoreTag: true,
  stripIgnoreTagBody: ['script']
})

// simple example
app.get('/playground/:id', (req) =>
  expressPlayground({ endpoint: `/graphql/${filter(req.params.id)}` })

// advanced params
app.get('/playground', (req) =>
  expressPlayground(JSON.parse(filter(JSON.stringify(req.query))))

See a proof of concept workaround, example #3

There aren’t any published security advisories