A markdown renderer focusing on security first
Building upon the strong foundation of GitHub's fork of cmark while adding
additional security precautions to be safe out of the box.
When auditing applications rendering markdown from user input I noticed that many popular markdown implementations are unsafe and vulnerable to XSS with standard configuration.
I am a strong believer in safe by default instead of opt-in security. Therefor I decided to build a library focusing on using the best tools for the job while configuring them to safely render unsanitized user input.
Install through pip:
Any other tool using PyPI works fine as well, I always recommend using virtual
environments.
$ pip install safemd
Render standard markdown:
import safemd
safemd.render(content)
Render GitHub Flavoured Markdown:
import safemd
safemd.render(content, flavour="github")
The same library used for rendering markdown in the official PyPI
Warehouse application is used by safemd. This is based on GitHub's
battle-tested fork of CommonMark. We use this with the safety feature
CMARK_OPT_SAFE
enabled per default, so no one in your team accidentally let
insecure code slip through. As an additional safety layer safemd also pass the
output from cmark through a whitelist with Bleach, Mozilla's HTML sanitizing
library.
Automatic safety testing through Travis is also utilized, running daily even if there are no new changes.
There's a way to opt-out of these safety precautions for those cases where you have a genuine need, this way it's obvious for you and your team that these places are to be considered with extra care.
import safemd
# Disable additional whitelist sanitizing through bleach
safemd.render(content, UNSAFE_NO_BLEACH=True)
# Disable cmark safety functions
safemd.render(content, UNSAFE_CMARK_XSS=True)
It's not uncommon for various markdown-renderers in production environments to be open for XSS-exploits, some more widespread than others. A list of common exploits have been assembled for your convenience, so you can test your current and future code.
[Just a link](javascript:alert("hi"))
[Normal link](data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGkiKTwvc2NyaXB0Pgo=)
[Nothing fishy here](data:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHBzOi8vZ2lzdGNkbi5naXRoYWNrLmNvbS9IdWx0bmVyL2JjMDIzOGJkOWIxZDI4M2JhMWM5NDczZjU0M2ZmZjc4L3Jhdy9kM2U5YWFkYTdlMGRlNzFkNmNlYTY1MDVmMTljZGE2NjE1MmE0MDFlL2hpLmpzIiBpbnRlZ3JpdHk9InNoYTM4NC0yaGZ6aFlkelB1SGd0S1E2Vk96UGlNbEN2Nzl3WDM1NzdxTDR3eWpmNWhMYkEvcW1BZHhCbXdxNGl6YXRwRy93IiBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj48L3NjcmlwdD4=)
Of course, this document wouldn't be complete without a list of markdown-based XSS-exploits found in the wild. Most of these are from 2018 and 2017.
- Valve, store.steampowered.com markdown XSS
- GitLab, Markdown XSS, internal
- PasteBin, markdown XSS (twice)
- Google Colaboratory, XSS + CSP Bypass
- Zendesk, Markdown based Stored XSS
- Streamlabs, account comromise XSS
- Commento
- Leanote
- Markdown's XSS Vulnerability (and how to mitigate it), showdownjs
- And the list goes on…
I am grateful for all suggestions, improvements and bugfixes. Feel free to send a PR or create a GitHub Issue for anything that isn't sensitive and urgent. Additional tests trying to break the security is especially appriciated.
I'm on keybase for encrypted communication. Send an email to security on my own domain hultner.se. Be aware, I discard any SPF, DMARC or DKIM-failing message, including SPF-Soft fail.
.
..: