Using a Content Security Policy (CSP) with WebViewer

MDN says a Content Security Policy (CSP) is:
"an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks."

WebViewer requires certain CSP directives to be used. If you do not need to support embedded JavaScript then the current recommended policy is:

sh

1script-src 'self' 'wasm-unsafe-eval' blob:; font-src 'self' data: blob:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; connect-src https://www.pdftron.com/ https://pws-collect.pdftron.com/ https://proxy.pdftron.com/

If you use this policy and want to make sure the embedded JavaScript warnings/errors don't show up because of the CSP then you can disable embedded JavaScript in WebViewer.

Allowing embedded JavaScript

If you need to enable to embedded JavaScript then you'll currently need to enable unsafe-inline and unsafe-eval for script-src.

sh

1script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; font-src 'self' data:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; connect-src https://www.pdftron.com/ https://pws-collect.pdftron.com/ https://proxy.pdftron.com/

Why is connect-src https://www.pdftron.com/ necessary?

The https://www.pdftron.com/ domain hosts fonts that WebViewer will download when necessary for certain documents.

It is possible to self-serve these fonts on another domain by following the guide for self serving substitute fonts.

Why is connect-src https://pws-collect.pdftron.com/ necessary?

When WebViewer is provided a license key that transmits usage data back to Apryse, then this is needed to ensure that the API server hosted at https://pws-collect.pdftron.com/ can be reached.

If the license key used is not a usage-based license key, then adding this URL to the connect-src directive is not necessary.

Why is connect-src https://proxy.pdftron.com/ necessary?

The proxy server is used in cases where WebViewer needs to contact a Certificate Revocation List (CRL) server or Online Certificate Status Protocol (OCSP) server for a subset of Digital Signature Verification calls.

This Proxy server can be substituted for any other Proxy server via the API Core.PDFNet.VerificationOptions.setRevocationProxyPrefix.

Otherwise, if your instance of WebViewer is not utilizing Digital Signature Verification, then this can be removed.

Why is script-src 'wasm-unsafe-eval' necessary?

WebViewer leverages WebAssembly for several native modules, and therefore requires a CSP directive that allows WebAssembly to execute.

While the term "unsafe" is used, we are currently unaware of any security risks related to the wasm-unsafe-eval directive, as it is distinct from the more dangerous unsafe-eval directive. For avoidance of doubt, wasm-unsafe-eval does not allow for the JavaScript eval method to be invoked, whereas unsafe-eval does allow for the eval method to be invoked.

Furthermore, as of this writing, the official WebAssembly documentation recommends this directive when WebAssembly compilation and instantiation is required.

Why is style-src 'unsafe-inline' necessary?

WebViewer requires the following Content Security Policy (CSP) setting for the style-src directive: style-src 'self' 'unsafe-inline'

The unsafe-inline source is required because the built and minified WebViewer code contains HTML elements that use the style attribute.

When the unsafe-inline source is used strictly with the style-src directive, only inline changes related to styling on a web page are possible.

Any risks beyond styling changes are mitigated by the self source, meaning that any attempts to use style attribute logic to make network requests outside of the source web page where WebViewer is hosted is not possible, given that the self source will use the same CSP rule to prevent communication outside of the hosting domain.

What if the web server I want to deploy WebViewer on does not allow the script-src blob: directive?

There is a WebViewer constructor option disableObjectURLBlobs that will remove the need for script-src blob:, that can be called like so:

sh

1WebViewer({
2 // ...
3 disableObjectURLBlobs: true,
4 // ...
5}, document.getElementById('viewer')).then(instance => {
6 // ...
7});

For additional context, WebViewer by default loads various compressed JavaScript worker files that are compressed as the G-Zip (.gz) or Brotli (.br) compression types. In order for WebViewer to uncompress these file extensions, they need to be opened with the browser's URL.createObjectURL method, which returns a Blob that is then loaded as a blob: URL.

If the CSP directive script-src blob: is not available, then this operation is not permitted.

Please note that the downside of this is that WebViewer then needs to load uncompressed versions of these worker files, which may have a minor impact on performance, depending on the client's network speed, though it is worth noting that the size difference between the compressed and uncompressed assets are not major.

What if the web server I want to deploy WebViewer on does not allow the script-src 'wasm-unsafe-eval' directive?

WebViewer can operate in a non-WebAssembly context with disableObjectURLBlobs, like so:

sh

1WebViewer({
2 // ...
3 disableObjectURLBlobs: true,
4 backendType: WebViewer.BackendTypes.ASM,
5 // ...
6}, document.getElementById('viewer')).then(instance => {
7 // ...
8});

However, this does require uncompressing the non-WASM PDF & Office worker files manually, as Apryse does not ship the uncompressed versions of these files due to the file size of the uncompressed files.

The files to uncompress

PDF Workers

WebViewer has a fullAPI option as part of the constructor, which would look like this extending from the code example above:

sh

1WebViewer({
2 // ...
3 disableObjectURLBlobs: true,
4 backendType: WebViewer.BackendTypes.ASM,
5 fullAPI: true,
6 // ...
7}, document.getElementById('viewer')).then(instance => {
8 // ...
9});

If fullAPI: true is being used, then the following commands should be run in lib/core/pdf/full, otherwise if fullAPI is not being used, perform the commands in lib/core/pdf/lean.

sh

1# Copy and rename the new JavaScript worker file to end with .gz
2cp PDFNetC.gz.js.mem PDFNetC.js.gz
3# Uncompress the newly copied G-Zip compressed file
4gzip -d PDFNetC.js.gz
5# Copy and rename the Binary worker file to end with .gz and move it up one level, as this is where PDFNetC.js expects to load the uncompressed binary mem worker
6cp PDFNetC.gz.mem ../PDFNetC.js.mem.gz
7# Uncompress the newly copied G-Zip compressed file
8gzip -d ../PDFNetC.js.mem.gz
Office Workers

For modern Office files (.docx, .xlsx and .pptx), perform the following commands in lib/core/office:

sh

1# Copy and rename the new JavaScript worker file to end with .gz
2cp WebOfficeWorker.gz.js.mem WebOfficeWorker.js.gz
3# Uncompress the newly copied G-Zip compressed file
4gzip -d WebOfficeWorker.js.gz
5# Copy and rename the Binary worker file to end with .gz
6cp WebOfficeWorker.gz.mem WebOfficeWorker.js.mem.gz
7# Uncompress the newly copied G-Zip compressed file
8gzip -d WebOfficeWorker.js.mem.gz

Please note that the instructions for the PDF workers also need to be completed for the Office workers to operate correctly.

Legacy Office Workers

For legacy Office files (.doc, .xls and .ppt), perform the following commands in lib/core/legacyOffice:

sh

1# Copy and rename the new JavaScript worker file to end with .gz
2cp WebB2XOfficeWorker.gz.js.mem WebB2XOfficeWorker.js.gz
3# Uncompress the newly copied G-Zip compressed file
4gzip -d WebB2XOfficeWorker.js.gz
5# Copy and rename the Binary worker file to end with .gz
6cp WebB2XOfficeWorker.gz.mem WebB2XOfficeWorker.js.mem.gz
7# Uncompress the newly copied G-Zip compressed file
8gzip -d WebB2XOfficeWorker.js.mem.gz

Please note that the instructions for the PDF workers also need to be completed for the Office workers to operate correctly.

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales