Skip to content Skip to sidebar Skip to footer

Passing Vars From Ejs To Javascript (server To Client On Render) While Avoiding Xss Issues

It seems like the accepted way to pass variables to JavaScript using ejs is like so: But I've ran into

Solution 1:

You can use Base64 encoding after JSON.stringify. This would avoid all risks related to quotes or HTML tags since Base64 only contains "safe" characters to put in a quoted string.

The solution I use in my website:

EJS file:

<script>var foo = <%- passValue(foo) %>
</script>

which will render into something like (for example here foo = null):

<script>var page = JSON.parse(Base64.decode("bnVsbA=="))
</script>

Server-side NodeJS:

functionpassValue(value) {
  return'JSON.parse(Base64.decode("' + newBuffer(JSON.stringify(value)).toString('base64') + '"))'
}

Client-side JS (this is an implementation of Base64 decoding that works with Unicode, you can use another if you prefer but be careful if it supports Unicode):

varBase64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}elseif(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}elseif(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}elseif(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}

Solution 2:

As a simpler alternative to Base64 encoding you can also use the native JS functions encodeURI() and decodeURI(). They escape <script> tags and quotes properly and even support unicode characters.

Your script would then look like this:

<script>var foo = JSON.parse(decodeURI("<%- encodeURI(JSON.stringify(foo)); %>"));
</script>

and setting foo = "</script><script>alert('test')</script><script>" would lead to following HTML source generated by EJS, which is perfectly safe:

<script>var foo = JSON.parse(decodeURI("%22%3C/script%3E%3Cscript%3Ealert('test')%3C/script%3E%3Cscript%3E%22"));
</script>

Like Raphaƫl mentioned, you could also define a server side function to call from EJS to simplify this process if this is used more often.

functionpassValue(value) {
  return'JSON.parse(decodeURI("' + encodeURI(JSON.stringify(value)) + '"))';
}

Post a Comment for "Passing Vars From Ejs To Javascript (server To Client On Render) While Avoiding Xss Issues"