Updated Recommendations for CSP and Avoiding 'unsafe-eval'
Hello Dynamsoft Team,
We are a team developing an Angular web app with PWA capabilities and are integrating the dynamsoft-barcode-reader-bundle (version 10.4.3100). Our application hosts the required Dynamsoft resources locally to ensure quick and seamless caching via our service worker. These resources include the following:
/assets/dynamsoft-capture-vision-std/std.js
/assets/dynamsoft-capture-vision-std/std.wasm
/assets/dynamsoft-image-processing/dip.wasm
/assets/dynamsoft-core/core.js
/assets/dynamsoft-core/core.worker.js
/assets/dynamsoft-core/core.wasm
/assets/dynamsoft-license/license.js
/assets/dynamsoft-license/dls.license.dialog.html
/assets/dynamsoft-license/license.wasm
/assets/dynamsoft-utility/utility.js
/assets/dynamsoft-barcode-reader/dbr.js
/assets/dynamsoft-barcode-reader/dbr.wasm
/assets/dynamsoft-barcode-reader/DBR-PresetTemplates.json
/assets/dynamsoft-capture-vision-router/cvr.js
/assets/dynamsoft-capture-vision-router/cvr.wasm
/assets/dynamsoft-capture-vision-router/cvr.worker.js
/assets/dynamsoft-camera-enhancer/dce.js
/assets/dynamsoft-camera-enhancer/dce.ui.html
Currently, we are concerned about having to use 'unsafe-eval' in our Content Security Policy (CSP). Our CSP configuration is defined in a web.config file and looks like this:
<add name="Content-Security-Policy"
value="default-src 'self' login.microsoftonline.com;
script-src 'self' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src http: https: data:;
connect-src 'self' login.microsoftonline.com mdls.dynamsoftonline.com sdls.dynamsoftonline.com data:;
frame-ancestors 'none';
font-src 'self' data:" />
This setup works but requires 'unsafe-eval' for the library to function, which we would like to avoid for security reasons. Ideally, we aim to:
- Use 'wasm-unsafe-eval' instead of 'unsafe-eval' (or a similarly scoped policy).
- Specify more restrictive CSP rules (e.g., per-directory or per-file rules) for Dynamsoft resources to minimize the risk.
We came across Issue #102 from October 2021, which provides minimum CSP requirements. However, that post is over three years old, and we were wondering if there have been any updates since then.
Additionally, we attempted to set CSP-specific rules for Dynamsoft files by using <location> nodes in web.config:
<location path="<path-to-dynamsoft-files>">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy"
value="...specific CSP rules here..." />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
Unfortunately, this approach resulted in an error when trying to load core.worker.js.
Questions:
- Are there updated recommendations for configuring CSP to avoid 'unsafe-eval' with Dynamsoft libraries?
- Is there a roadmap or consideration for removing the reliance on 'unsafe-eval' in the library?
- Are there best practices for hosting Dynamsoft resources locally while maintaining a secure CSP?
Thank you for your guidance and support!
Best regards, Rune
For resources self deployment:
The resources are located at the path
node_modules/<pkg>, without@<version>. You must copy “dynamsoft-xxx” packages elsewhere and add@<version>. The<version>can be obtained frompackage.jsonof each package. Another thing to do is to specify theengineResourcePathsso that the SDK can correctly locate the resources.
I guess this is why you failed to load core.worker.js.
===
As for CSP, I failed to remove 'unsafe-eval' in my test.
Webassembly needs to call js. It uses eval in std.js. I tried strict-dynamic but it does not work for webworker.
===
Your approach should be the most reasonable one, exposing only the scan barcode page to a looser CSP and ensuring strict security for other pages.
Also refer: https://stackoverflow.com/questions/9522348/add-custom-header-based-on-file-type
Thank you for taking a look at this.
We do set the engineResourcePaths
CoreModule.engineResourcePaths = {
std: 'assets/dynamsoft-capture-vision-std/',
dip: 'assets/dynamsoft-image-processing/',
core: 'assets/dynamsoft-core/',
license: 'assets/dynamsoft-license/',
cvr: 'assets/dynamsoft-capture-vision-router/',
dbr: 'assets/dynamsoft-barcode-reader/',
dce: 'assets/dynamsoft-camera-enhancer/',
utility: 'assets/dynamsoft-utility/',
}
I will try with looking more into custom headers.
CoreModule.engineResourcePaths = {std: dip: core: xxxxxx}
As it is overly complex, Some months ago, I suggested to remove this from the guide and only keep the usage of rootDirectory. 😂 Well, anyway, it also works.
==
If you still have problem loading core.worker.js. Maybe you miss blob: in worker-src.
You can check CSP in https://github.com/dynamsoft-rd-0/dbrjs-mass-samples/blob/master/CSP-for-dbrjs10.4.3100.html.
You can remove all https://cdn.jsdelivr.net/xxxx sha384- nonce-, in angular + self deployment.
Good news, @rujep
our colleagues have made a new discovery: if you deploy resources in the same domain of page, you don't need unsafe-eval.
🎉🎉🎉
e.g. page: https://a.b.c/somewhere/index.html resource: https://a.b.c/assets/
The dynamsoft demos online are not self-hosted, but that shouldn't really matter when it comes to whether unsafe-eval is required in the CSP, unless self-hosting changes the code that is actually being run. I can't imagine that being the case, but let me know if that's an invalid assumption.
Based on my testing, it seems unsafe-eval is still required whenever the page has a CSP (which I imagine is most production applications). Details below.
I applied the following CSP without unsafe-eval to the Common 1D + 2D demo on the dynamsoft website and the demo failed to render the camera view. I did this by overriding the response header of the HTML document response in Chrome dev tools.
default-src 'self' ;
script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' https://* ;
connect-src 'self' https://* ;
worker-src 'self' blob: ; style-src 'self' 'unsafe-inline' ;
img-src 'self' data: https://*;
frame-src 'self' https://*
(Note the https://* are for the various CDN domains that are hosting the frontend assets and wouldn't be necessary in production.)
When I added unsafe-eval to script-src, the camera view renders as expected.
default-src 'self' ;
script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' https://* ;
connect-src 'self' https://* ;
worker-src 'self' blob: ; style-src 'self' 'unsafe-inline' ;
img-src 'self' data: https://*;
frame-src 'self' https://*
Sorry for the lack of feedback the last few days.
I have tried different approaches as well, and always come to the same conclusion, that for now 'unsafe-eval' is required.
I created this issue because it wasn't easy to find anything on it in existing documentation and wanted to know if I was missing something obvious, as well as hopefully have the team looking into the possibility of only requiring 'wasm-unsafe-eval' or none of the above in the future.
@michael-yx-wu
I had the same idea as you at first. But I verified my colleague's discovery, and indeed, unsafe-eval is not needed when the resource and page are in the same domain. When they are in different domains, we fetch URL first, then load blob as worker (to solve the cross-domain problem). When they are in the same domain, the worker loads directly through the URL.
I guess wasm-unsafe-eval needs to be passed through the worker, which only works when the resources are in the same domain.
@rujep I'll try to give you a sample, with all resources self-host and nodejs server (any server is OK. file:// can't work)
Sorry for rely late because of my vacation.
The code generated by the wasm compiler (emsdk) naturally has eval. Although according to some issues, we can remove some of them. But some of these functions have no alternatives. I have a idea but I am not sure if they will work. It is very likely that these changes will bring huge performance and size sacrifices.
I guess this might be why wasm-unsafe-eval came into being.
https://drive.google.com/file/d/1LHM75cUZSoKH2I8d79dK4Wr0DHVynF6T/view?usp=sharing (share removed)
Please try this zip. Node.js as server.
I will continue to try to solve it perfectly after vacation (one week left).
It is expected to be time-consuming and require research compilation and detailed usage for emsdk.
I can't predict the time, please don't wait for me. ❤
@Keillion Thank you for your efforts. I will try the zip later today. And I (almost 😄) don't care about the time frame. I am just happy that you are looking into it.
I believe you have received a response from our technical support. For the completeness of this issue, I still put the zip package that solves the problem here.
Here I use nodejs as server. You can find CSP rules in app.js.
The relevant solution is expected to be included in the next major version.
Hello there,
Do you have any update for the CSP ?
For my case I have to add "'wasm-unsafe-eval'" and "'unsafe-eval'" to 'script-src'
Here is my my csp policy, I'm working with NestJs
contentSecurityPolicy: { useDefaults: true, reportOnly: false, directives: { ...defaultDirectives, 'connect-src': scannerIpAddress, 'script-src': ["'self'", '.openstreetmap.org', 'https://cdn.jsdelivr.net', "'wasm-unsafe-eval'", "'unsafe-eval'"], 'img-src': [ "'self'", 'data:', 'blob:', '.openstreetmap.org' ], 'style-src': ["'self'", "'unsafe-inline'", 'fonts.googleapis.com'], 'font-src': ["'self'", 'fonts.gstatic.com'], 'worker-src': ["'self'","blob:"] } },
@AntoineTat1
No eval required. Require wasm-unsafe-eval, not unsafe-eval.
- If you use public CDN, ensure
import { CoreModule } from "dynamsoft-core";
CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/[email protected]/"
- If you self host resources (downloads maybe slow for end users), use resources in the zip and set
CoreModule.engineResourcePaths.rootDirectoryaccordingly.
@Keillion
It still doesn't work
here my dynamsoft.config and csp config
import { CoreModule } from "dynamsoft-core";
import { LicenseManager } from "dynamsoft-license";
import "dynamsoft-barcode-reader";
CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/[email protected]/";
LicenseManager.initLicense("xxx", {
executeNow: true,
});
CoreModule.loadWasm(["DBR"]);
contentSecurityPolicy: { useDefaults: true, reportOnly: false, directives: { ...defaultDirectives, 'connect-src': scannerIpAddress, 'script-src': ["'self'", '*.openstreetmap.org', 'https://cdn.jsdelivr.net'], 'img-src': [ "'self'", 'data:', 'blob:', '*.openstreetmap.org' ], 'style-src': ["'self'", "'unsafe-inline'", 'fonts.googleapis.com'], 'font-src': ["'self'", 'fonts.gstatic.com'], 'worker-src': ["'self'","blob:"] } },
and a screenshot with errors
@AntoineTat1 I am not too familiar with nextjs. I found that I need to bypass the inline script generated by nextjs itself in order to continue my testing.
Do you have a sample that has been bypassed the nextjs's inline script?
<script>
self.__next_f.push
</script>
I'm keeping trying...
@AntoineTat1
OK, I know, just allow inline for this time 😂 .
https://github.com/dynamsoft-rd-0/dbrjs-mass-samples/blob/master/nextjs-csp/next.config.mjs
I use this CSP. wasm-unsafe-eval is required. It works well when npm run build + npm run start.
Hi @Keillion,
I’m actually not using Next.js in my project, but NestJS.
From what I understand, you’re trying to bypass CSP by allowing inline scripts (unsafe-inline). However, my goal is to keep the highest level of security and not add any extra parameters to the CSP policy.
If there’s an alternative way to make Dynamsoft work without relaxing CSP, I’d be happy to explore it. Let me know your thoughts !
In script-src,
I use unsafe-inline because of Next.js.
Our SDK only require wasm-unsafe-eval. As long as it is based on wasm technology, this is unavoidable.
In style-src, we can also bypass it, by using already in DOM elements instead of injected UI.
=== My mistake🐤, I'll try Nest.js.
I observed that Nest.js is a backend framework.
To bypass style unsafe-inline, depends on your front-end.
Please check this commit https://github.com/dynamsoft-rd-0/dbrjs-mass-samples/commit/fb8cfa562f1aed30d888fcb968884b7e4e88c0c3
Key code: await CameraView.createInstance(<htmlElement>)
Or you use esay-barcode-scanner: EasyBarcodeScanner.scan(uiElement: HTMLElement)
The dafault ui html is easy-barcode-scanner.ui.html
Some frameworks some settings generate style in JSX/TSX by js (element.style.xxx = 'xxx'). This is allowed by the most stringent CSP.
If not work in your case (the framework convert JSX/TSX to html directly), you need to define these styles in split css file, like what I do for @keyframes dce-rotate and @keyframes dce-scanlight.
@AntoineTat1
Concerning our frameworks : backend: NestJS, it is there where I set my CSP policy frontend: React, in this app I set my Dynamsoft configuration
If I have no choice but to use wasm-unsafe-eval, I'll implement it and ensure the SDK functionality works as expected. Everything works perfectly with wasm-unsafe-eval enabled but I have this CSP.
contentSecurityPolicy: { useDefaults: true, reportOnly: false, directives: { ...defaultDirectives, 'connect-src': scannerIpAddress, 'script-src': ["'self'", '*.openstreetmap.org', 'https://cdn.jsdelivr.net', "'wasm-unsafe-eval'"], 'img-src': [ "'self'", 'data:', 'blob:', '*.openstreetmap.org' ], 'style-src': ["'self'", "'unsafe-inline'", 'fonts.googleapis.com'], 'font-src': ["'self'", 'fonts.gstatic.com'], 'worker-src': ["'self'","blob:"] } },
Quick question about the wasm-unsafe-eval in your SDK. I'm a bit concerned about the security implications. Are you guys planning to reduce or phase out this directive in future versions? @Keillion
For algorithm-intensive applications, wasm is important, and the performance achieved with pure js will be unacceptable. So we have no plan to remove wasm-unsafe-eval.
Related Discussions: https://stackoverflow.com/a/37177219
And our wasm is executed in the web worker environment, wasm cannot directly manipulate the DOM of the main thread. We enhance wasm's security in this way.
====
Other topics, I suggest that use https://cdn.jsdelivr.net/npm/[email protected]/ instead of https://cdn.jsdelivr.net/. A smaller security perimeter results in a smaller attack surface.
@AntoineTat1