This is too long to post as a comment so I'm posting as an answer.
You correctly note that:
I think that eval may be a solution but I want a better solution as using eval is discouraged
Indeed eval is problematic in any language because it essentially allows the running program to treat a string as code. The big problem with this is that this open the door to code injection (in layman's term: hacking). But this advice is more of a theoretical advice instead of an advice of what functions to use.
So what does the advice mean by eval
?
Eval is any function or mechanism that allows strings to be interpreted as code. Different languages have different functions or features that allows eval
to happen. Some languages for example allows recursive string interpolation. Some languages have a function called interp()
that spawns a sub-interpreter. Some languages literally have a function called eval()
.
Javascript has four eval mechanisms:
Using the eval()
function.
- Any string passed to
eval()
is treated as code.
Setting the src
attribute of script tags
- The value of the
src
attribute is assumed to be a javascript file to be downloaded
- This works in both literal code (actual code in HTML itself) or dynamically generated script tag (creating script element in javascript and set it's src property)
The body of script tags
- Any text inside a script tag is treated as code
- This only works in literal code. As you found out, setting script body using
innerHTML
is banned (and has been since Netscape 4 - the first browser with javascript)
The javascript:
URI protocol
- Everything after the
:
is treated as code
- This only works if the user clicks the link
So, what you are trying to do is to perform an eval
. It does not matter weather you use the eval
function or not, you are still trying to do an eval
using innerHTML
(though it doesn't work)
So, is there a safe way to run javascript on the page if everything is an eval
?
There is - at least for modern browsers.
Modern browsers implement a feature called subresource integrity. Unfortunately, at the time this answer is written, it is not supported on Edge. Basically you can calculate the hash of the js file (such as sha1) and declare it in the script tag so the browser can confirm that the js file has not been tampered with. The following is an example:
<script src="https://cdn.example.com/script.js"
integrity="sha384-+/M6kredJcxdsqkczBUjMLvqyHb1K/JThDXWsBVxMEeZHEaMKEOEct339VItX1zB"></script>
Are you this paranoid?
It depends. The eval
issue might really be important to you. If so, loading javascript from an external source with subresource integrity set up is the ONLY safe way to execute javascript.
But not everybody is that paranoid. We have been generally doing OK so far without this feature. Here are a few rules of thumb that can mitigate evaling code regardless if you use the script tag or call eval()
:
Always make sure you ONLY execute static javascript code. Do not try to construct code from strings.
If you must construct code from strings (for example using a templating language like Handlebars or using a bundler like Webpack or Browserify) then never include any user generated content in the code. You can still load content by making ajax requests instead of including them in the code.
If you must have user generated content in your code than make sure you sanitise the content. There are several generally accepted strategies such as banning the use of special characters like <
and >
and "
or escaping special characters. There are even libraries that will do this for you.
Basically what we want to avoid is situations like users entering their name as John"; console.log("gotcha");"
and somehow being able to execute code.