XSS in user supplied title in nuxt/nuxt
Reported on
Feb 7th 2023
Issue
The useHead
function does not sanitize tags inserted in each property, including the title
property.
Context
The useHead
repository is a wrapper around vueuse/head
which wraps unjs/unhead
which wraps harlan-zw/zhead
. The possibility of XSS is not described as being a vulnerability in the root repositories (see here and here).
Therefore nuxt
, and some nuxt packages (content, bridge) are misusing/misdocumenting this feature.
The fact that the useHead
function is vulnerable to XSS appears to be known by the community:
- https://github.com/nuxt/nuxt/issues/15668
- https://github.com/harlan-zw/zhead/issues/12
- https://github.com/vueuse/head/issues/173
But the these dangers are not documented directly on nuxt
or vueuse
or unjs/head
docs. You have to look at the lowest level utility zhead
to understand that this may be dangerous. Even at that point it's not completely obvious that possibility of XSS when using title
within useHead
, as this seems like a safe feature, compared to scripts which are obviously inherently dangerous.
This lack of documentation means that It may not be obvious to developers that useHead title
is an unsafe feature, and you can find instances of developers making this mistake if you search. I would definitely say most developers would assume setting the page title will be safe, setting title in nuxt2 does not have this issue.
User supplied data within the title is a common practice.
Proof of Concept
<template>
<div>Hello world!</div>
</template>
<script setup lang="ts">
const r = useRoute()
useHead({
title: r.query.title as string
})
</script>
Create a production build, start the build, navigate to the url http://site/?title=</title><script>alert(1)</script>
This vulnerability only occurs during SSR.
Recommendation
It seems like there is a plan to implement a safe version of this function in vueuse
. I think this weakness should be documented for the moment, or creating a wrapper useHead
that HTML encodes title.
Impact
XSS can give an attacker access to sensitive information or actions.
Vulnerability has been fixed in downstream component https://github.com/unjs/unhead/issues/97
Fixed upstream! https://github.com/nuxt/nuxt/commit/88b895a23a855135c5c8ee53183677e17aad1a12
Thank you for this - great work. <title>
XSS is resolved. As far as potential XSS in innerHTML
I think we can document the risks of setting html directly, and we plan to provide a sanitisation option to make it easy for devs.