Security Concerns When Rendering | MDX Markdown Content #2613
-
Hi MDX team 👋, I'm currently working on a React application that needs to render dynamic MDX content provided by users. I understand that MDX allows embedding JSX within Markdown, which is very powerful—but I'm also aware it raises some potential security concerns, especially when dealing with untrusted user input. I'm specifically concerned about:
Could you please clarify:
Here is the markdown I am trying to test: # XSS Test 1 - Script Tag
<script>alert('XSS1')</script>
# XSS Test 2 - Inline Handler
<button onClick="alert('XSS2')">Click me</button>
# XSS Test 3 - JavaScript URI
<a href="javascript:alert('XSS3')">Malicious link</a>
# Dangerous React CreateElement
{React.createElement('script', {}, 'alert("React CE XSS")')}
# Component Name Injection
<{['a','l','e','r','t'].join('')}>Test</>
# HTML in Markdown
<img src="x" onerror="alert('Markdown HTML XSS')" />
# Code Block Escape
```js
alert('Code block XSS');
\`\`\`
# CSS Injection
<style>{'body { background: red !important }'}</style>
# Dangerous Prop Injection
<InfoBox title={<script>alert('Prop XSS')</script>} />
# Function Injection
<Card onClick={() => fetch('/api/secret')} />
# Object Prototype Pollution
{Object.prototype.polluted = true}
# External Script
<iframe src="https://malicious.site"></iframe>
# Data URI XSS
<img src="data:image/svg+xml;base64,PHN2ZyBvbmxvYWQ9ImFsZXJ0KCdEYXRhIFVSSSBYU1MnKSI+PC9zdmc+" />
# DNS Rebinding
<script src="http://malicious.example.com"></script>
# Hex Encoded
<a href="javascript:alert('Hex XSS')">Click</a>
# Unicode Bypass
<img src="x" on\u0065rror="alert('Unicode XSS')" />
# String.fromCharCode
{String.fromCharCode(60,115,99,114,105,112,116,62,97,108,101,114,116,40,39,79,98,102,117,115,99,97,116,101,100,32,88,83,83,39,41,60,47,115,99,114,105,112,116,62)} Any guidance or official recommendations would be greatly appreciated! Thanks for your work on MDX — it's an amazing tool. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 1 reply
-
Your concerns are legitimate. MDX is a programming language that compiles to JavaScript. Evaluating JavaScript from untrusted sources is dangerous. I recommend to use markdown instead for such use cases. It would be possible forbid certain constructs. For example, the following remark plugin forbids ESM syntax, which is just one of various contenders for security issues. You could pass this to the import { type Root } from 'mdast'
import { type Plugin } from 'unified'
import { visit } from 'unist-util-visit'
const remarkMdxForbidEsm: Plugin<[], Root> = () => (ast, file) => {
visit(ast, 'mdxEsm', (node, index, parent) => {
// Either warn…
file.message('ESM is not allowed')
// … or error…
file.fail('ESM is not allowed')
// … or strip
if (index != null && parent != null) {
parent.children.splice(index, 1)
}
})
} Using this approach, you could allow a subset of MDX features, for example only JSX elements with primitive attributes. But still, you would be evaluating JavaScript at runtime. Whether or not you need to sanitize further, depends on your use case. For example React also does some sanitization. That may or may not be sufficient for you. Try pasting the following content in the MDX playground for example: <style>{'body {background-color: #600}'}</style>
<script>
{"console.log('XSS')"}
</script>
<button onClick="alert('XSS2')">Click me</button>
<a href="javascript:alert('XSS3')">Malicious link</a> |
Beta Was this translation helpful? Give feedback.
-
Welcome @salmanbukhari37 👋 https://mdxjs.com/docs/getting-started/#security are the official recommendations. As @remcohaszing mentions, my top recommendation is, if you don’t trust your authors, consider plain markdown over mdx. |
Beta Was this translation helpful? Give feedback.
-
Hi @ChristianMurphy and @remcohaszing , I've reviewed the documentation, and I’ve switched from standard Markdown to MDX in my application to support embedded custom components. While everything is working well, I’m concerned about the security implications of rendering MDX content, especially since the input may not always be safe. Could you please let me know if MDX provides any built-in security mechanisms for handling potentially unsafe content? Having such a feature would be very helpful for implementing secure rendering in my current project. Thanks |
Beta Was this translation helpful? Give feedback.
MDX is inherently unsafe, it supports full JS, that is by design.
It offers complete flexibility to authors, with the trade off that you need to trust them.
The safe mode is: use markdown.
There are ways to render custom elements in markdown.
For example components in
react-markdown
https://github.com/remarkjs/react-markdown#appendix-b-componentsor directives https://github.com/remarkjs/remark-directive