Overview
At work, we tend to test our near-complete features internally as a team to capture any bugs before they are shipped to customers. We call this process a 'blitz' and it has successfully caught many bugs for us that can be addressed before shipping a feature.
In this case, I was testing a sanitization library we had used to sanitize some use provided input (as HTML) called sanitize-html-react. After testing some common payloads from the OWASP XSS Cheat Sheet I discovered the library was flawed.
This library was selected because it was already in use in other parts of the codebase. For this reason, we believed it to be safe as we assumed it has passed the required security checks and standards we have set in place. However, in this case, it wasn't.
sanitize-html-react library
The sanitize-html-react library is a fork at some point in time off the sanitize-html library.
However, it has no dependencies on sanitize-html and therefore it will not benefit from security updates.
The sanitize-html library is actively maintained where as sanitize-html-react library has not been updated for more than four years!
https://github.com/zacharystenger/sanitize-html-react
Proof of Concept
const userInputHtml = "<<u>u>underlined"
const sanitizedUserInput = sanitizeHtml(userProvidedHtml, {
allowedTags: [],
allowedAttributes: {},
});
...
expect(sanitizedUserInput).toEqual('underlined'); // fails
expect(sanitizedUserInput).not.toContain('<u>'); // fails
The discovered bug was found using the recursive XSS payload above.
The above config states we should discard all tags (as allowedTags
list is empty).
In the above example the inner <u> in <<u>u>underlined is stripped away which forms a new <u> element resulting in the final string <u>underlined.
The same concept can be used to render <script> tags (or any other tag).
External Usage
Many open-source public repositories are or have used this library, potentially opening themselves up to security vulnerabilities.
- https://github.com/influxdata/influxdb
- https://github.com/influxdata/platform
- https://github.com/influxdata/chronograf
- https://github.com/wwayne/react-tooltip
- https://github.com/man-group/dtale
- https://github.com/microsoft/pxt
- https://github.com/ghostery/ghostery-extension
- https://github.com/replicatedhq/ship
- https://github.com/plotly/falcon
Disclosure
An issue has been created on the Github repo here:
https://github.com/zacharystenger/sanitize-html-react/issues/3
Proposed Solutions
✅ sanitize-html
Replace package with sanitize-html (https://www.npmjs.com/package/sanitize-html)
This is a maintained, up to date, drop-in replacement.
✅ DOMPurify
Replace package with DOMPurify (https://www.npmjs.com/package/dompurify)
Popular and maintained XSS sanitizer.
✅ lodash.escape
Consider using Lodash escape (https://lodash.com/docs/4.17.15#escape) if you were only using sanitize-html-react in escape mode.