Skip to content

Instantly share code, notes, and snippets.

@vjeux
Created March 31, 2015 22:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vjeux/eb6080f729920a2cb9b8 to your computer and use it in GitHub Desktop.
Save vjeux/eb6080f729920a2cb9b8 to your computer and use it in GitHub Desktop.
// Can't do that with inline styles, they don't have access to pseudo selectors
body::before { content: "Thanks Obama"; }
textarea.post-comment::before {
  content: "Please enter your password to post a comment...";
}
.container::before { content: url(http://example.com/processIntensiveEndpoint); }
.another::before { content: url(http://example.com/processIntensiveEndpoint?1); }
body::before { content: url(http://example.com/processIntensiveEndpoint?2); }

// Yes, this is a real threat but fortunately, this IE6+7 is mostly gone nowadays so the surface area is very small for an attacker
In old IE <8 you could use CSS expressions to run JavaScript in CSS. Known as "Dynamic Properties."

// React doesn't let you write this with inline style attribute
<style>
<!-- </style><script>alert('whoops')</script> -->
</style>

// React escapes the style attribute by default, so you CANNOT escape from the style attribute.
// However, React doesn't properly delimits the boundaries of styles, so you can write
// <div style={{'border: 1px solid red; color': 'red'}} /> and it'll happily 
// output <div style="border: 1px solid red; color: red;"></div> for the initial rendering.
// Sadly, if you want to properly escape you need to implement a fully featured CSS parser
// which we don't want. The other option is to not use innerHTML for the initial rendering,
// which we are probably going to do at some point since performance is better on modern
// browsers. But we'll still be affected on server-side rendering use cases
Here is an XSS Filter Evasion Cheat Sheet from OWASP:

// One of the design principle of React is that everything that's markup is encoded in jsx
// and not escaped, everything else is escaped by default. So you need to manually put yourself
// in unsafe mode (dangerouslySetInnerHTML) in order to avoid escaping.
"just ensure all user data is escaped and inline styles are safe."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment