31

We use the following CSP header:

default-src 'self' *.ourdomain.com; script-src 'self' *.ourdomain.com 'sha256-[...]' 'unsafe-eval'; 
connect-src 'self' *.ourdomain.com; 
style-src 'unsafe-inline' * 'self' data:; font-src *; 
img-src * 'self' data:

The recommendation by our security team is not use unsafe-eval.

My question is: as long as we are using sha256-[...] to restrict any script that we haven't deployed ourselves, what is the security risk of still keeping unsafe-eval in the CSP header? In what situation would this still expose us to cross-site attacks?

dopoto
  • 1,124
  • 1
  • 10
  • 20

2 Answers2

36

Because eval is literally unsafe. Eval in every language means "take this string and execute it code." Sure, you may be using eval in a semi-safe way, but as long as you allow it at all, you are saying "anyone is allowed to execute arbitrary code in my application given an entry point".

2022 edit: the section below has not stood the test of time and things like WASM require unsafe-eval. It's still true that many front end frameworks still require unsafe-eval too.

It is my opinion that there is no reason to use eval. Show me a case where eval is required in actual useful code and I'll bet that I can rewrite the code without using eval or declare it as impossibly secure code.

Disallowing Inline script is only half the battle, especially if you use jquery.

Quiz: does this code trigger an inline script violation or an eval violation?

$('body').html('<script>alert(1)</script>')

You may be surprised.

Spoiler:

it's eval (at the time this was written)

oreoshake
  • 4,712
  • 1
  • 31
  • 38
  • 10
    There is a reason: performance. It is nearly impossible to build an interpreter that is as fast as compiled code. Using `eval()` allows faster code in cases when the code depends on some runtime data. For a more specific use case, consider a site in which user supplies URL to data, and an arbitrary expression into that data. The expression would need to be evaluated millions of times. Doing it interpretively (e.g. expression "a.b.c" is converted into an array ['a','b','c'], and loop to access child of a child of a child object) is clearly slower than x=eval('(a) => a.b.c') and call x(a). – Yuri Astrakhan Jan 10 '18 at 23:27
  • 3
    @yurik yes, performance is absolutely a reason. I don’t have any data to back it up, but I feel the performance gain won’t be meaningful in many cases. People used to eval JSON instead of parsing it, but the trade off just wasn’t worth it to many. – oreoshake Jan 11 '18 at 03:18
  • I think eval(json) and JSON.parse() are comparable in performance, but if you have a large json, I'm sure doing a manual parsing (without the JSON.parse) would be much slower. – Yuri Astrakhan Jan 11 '18 at 03:45
  • I’m trying to avoid absolute statements, especially around something as complicated as csp, but it is still my opinion that the risk is not worth the gain. But I do want to reiterate that disabling eval is not performant for some. – oreoshake Jan 11 '18 at 07:30
  • 1
    Fair enough. Initial JS implementations were interpreters, but later they became compilers of JS to native CPU instructions. Did that make them safer? Probably not, but it clearly gained in performance. We all demand fast JS, even though internally browsers basically use "eval" on JS code to make it native. So they do a lot of careful checks, and sandboxing using CPU features, all to gain both the speed and security. – Yuri Astrakhan Jan 11 '18 at 16:28
  • Also, the `unsafe-eval` is needed to use the WASM, for now. – Inkeliz Nov 23 '18 at 14:10
  • 3
    Show me a case where eval is required and I'll bet that I can rewrite the code without using eval or declare it as impossibly secure code: function check() { try { eval("class = {}"); } catch (e) { return false; } return true; } – Julius Žaromskis Jan 21 '19 at 13:22
  • I believe there is no other way than using eval/Function for some feature detections, e.g. for generators and async/await. – Mattias W Apr 22 '20 at 12:06
  • 1
    No matter how much I beat on our scripts, jquery and other 3rd party libraries we use, still somehow keep tripping an `eval` that gets blocked by CSP. Without rewriting someone ELSES code (defeating the purpose of using said libraries)... we are just at a loss, and throwing our hands up and adding `unsafe-eval` to our definition. – IncredibleHat Apr 23 '20 at 23:58
  • Underscore uses `new Function` which is also blocked by CSP for same reason: https://github.com/jashkenas/underscore/blob/master/underscore.js#L915 – dinesh ygv Nov 09 '20 at 09:54
  • maybe this was the case when this was written, but that no longer evaluates as unsafe-eval, it is an unsafe-inline – XRBtoTheMOON Apr 23 '21 at 00:02
  • 1
    Thanks for mentioning that @XRBtoTheMOON, I've updated the spoiler. – oreoshake Nov 16 '21 at 23:56
  • _Show me a case where eval is required in actual useful code and I'll bet that I can rewrite the code without using eval_ @oreoshake How could I write this without `eval`? `var supportsEs6 = function (){ try {new Function("(x = 0) => a");return true}catch(e){ return false}}();` – AdrianRM Mar 30 '22 at 16:58
  • One more reason: Flutter. Flutter web canvaskit works based on it. :( – ostmond Jul 28 '22 at 12:52
5

The security risk is that it doesn't protect any of your own code that may be vulnerable because eval is used.

If you are using eval in your own code you should question why. Is there a safer alternative that can be employed instead?

See here for a (contrived) example of how code can be injected by an attacker. Of course whether this can be done to your site very much depends on your code.

The upshot is that there's almost always an alternative to using eval.

Community
  • 1
  • 1
SilverlightFox
  • 32,436
  • 11
  • 76
  • 145