dotboris / vuejs-serverside-template-xss

Demo of a Vue.js app that mixes both clientside templates and serverside templates leading to an XSS vulnerability
MIT License
300 stars 22 forks source link

An alternative fix suggestion for legacy apps #5

Open dalevink opened 6 years ago

dalevink commented 6 years ago

I have a suggestion, for any existing app that consistently addresses existing XSS vulnerabilities (pre Vue).

For example, you (should?) have an existing "globally" used function, such as:

function htmlEscape($text) {
  return htmlspecialchars(strval($text), ENT_QUOTES, 'UTF-8');
}

Could this be simply altered to include the escaping of Vue template interpolation, eg:

function htmlEscape($text) {
  $text = str_replace("{", "{{ '{' }}", strval($text));
  return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
}

A possible one line fix?

Note: Updated code fix as per suggestion below – thanks to @apreiml

apreiml commented 6 years ago

@dalevink It would be better to escape even one '{' character. Otherwise XSS is still possible, if you concatenate output somewhere.

For example:

htmlEscape('{').htmlEscape('{ 5 + 5 }}')

helmut commented 6 years ago

It would be great if the htmlspecialchars function would convert the curly brackets {} into entities like it does with the greater than/less than symbols <>.

Haven't had a chance to test it but that would make sense without adding extra spans throughout your layout.

UPDATE: Just tested and vue still interprets the entities! Thats interesting...

dalevink commented 6 years ago

@helmut I found this and was surprised too that Vue interprets the entities!

Yeah, the extra spans aren't pretty, but they could be a temporary trade-off as you migrate to better methods.

Hold that thought… here’s a way to avoid ANY extra markup and simply use Vue itself to escape the string: https://jsfiddle.net/dalevink/p9gj54Lo/16/ … that's nicer – I've updated the code above too :)

apreiml commented 6 years ago

@dalevink This is also a nice solution, but then you should be careful if you use this escape method outside of the Vue app scope (e.g. in the header).