VerifyKitv0.13.3

Security

This guide covers the VerifyKit SDK's security model, data handling, trust architecture, and recommendations for secure deployment.


Trust Model

VerifyKit is a client-side PDF signature verification library. All cryptographic operations run entirely in the browser (or in Node.js), with no data sent to external servers by default.

  • No server-side processing. PDF bytes are never uploaded to a remote server for verification. All parsing, hash computation, and certificate chain validation happen locally.
  • WASM sandbox. The core verification engine is compiled to WebAssembly and runs inside the browser's WASM sandbox, which provides memory isolation from the host page.
  • Deterministic results. Given the same PDF and trust store, verification results are identical across environments.

Trust Boundary (client-side verification)

VerifyKit computes its verdict on the client, for the client — like Adobe Reader validating a signature locally. It tells the person viewing the document whether the signature is sound; it is not a server-side attestation.

Because verification runs in the user's browser, a malicious user controls that environment and can tamper with the result (patch the WASM, alter the JavaScript, or simply report a different outcome). Therefore:

  • Do not trust a browser-computed verdict for server-side security decisions. If your backend must act on whether a signature is valid (accept a filing, release a payment, etc.), run verification on your server with @trexolab/verifykit-core in Node.js and use that result — never a value reported by the client.
  • The client-side result is authoritative only for the user viewing it, and only as far as the delivered code (WASM + JS) is untampered. Serve over HTTPS and use SRI on the bundle so the delivered code itself is trustworthy.

Data Handling

PDF Bytes Stay Local

When a user loads a PDF into VerifyKit (via useVerification().load(), verifier.verify(), or the viewer UI), the file bytes remain in browser memory. They are:

  • Read into an ArrayBuffer or Uint8Array
  • Passed to the WASM engine for signature extraction and validation
  • Optionally passed to PDF.js for rendering

At no point are the PDF bytes transmitted over the network. When the viewer is destroyed or the page is closed, the memory is released.

No Telemetry or Analytics

The SDK does not collect usage data, telemetry, or analytics. There are no outbound network requests initiated by the SDK itself, with the following exceptions:

  • AIA (Authority Information Access): When enableAIA is true (the default), the verifier may fetch missing intermediate certificates from URLs embedded in the certificate chain. Disable this with enableAIA: false.
  • Revocation checking: When the @trexolab/verifykit-plugin-revocation plugin is installed, OCSP and CRL requests are made via a server-side proxy to check certificate revocation status.
  • PDF.js worker: The worker script is loaded from the URL you provide via workerUrl.

WASM Security

The verification engine is compiled from Rust to WebAssembly. This provides several security properties:

  • Memory isolation. WASM modules operate in a linear memory sandbox. They cannot access the host page's JavaScript heap, DOM, or other browser APIs directly.
  • No file system access. The WASM module can only process data explicitly passed to it via the JavaScript API.
  • No network access. The WASM module cannot make network requests. All network operations (AIA, revocation) are handled by the JavaScript layer.
  • Type safety. The Rust source code provides memory safety guarantees that carry through to the compiled WASM output.

WASM Loading

The WASM binary ships as a .wasm file inside the package and is located automatically — your bundler emits it as a hashed asset, and the Node build reads it from disk. Between v0.3.1 and v0.11.0 it was base64-embedded in the JavaScript instead; nothing about its contents or its integrity guarantees changed with the move, only where the bytes travel.

If you point wasmUrl at a copy of your own, serve it over HTTPS from an origin you control, and as application/wasm — browsers refuse to stream-compile any other content type. Treat that URL the way you would a script source: whatever it returns is the verification engine.


Certificate Trust Store

VerifyKit ships with a built-in trust store of 134 root CA certificates: 118 from the Adobe Approved Trust List (AATL) and the 16-certificate Indian CCA hierarchy. These cover the most widely used certificate authorities for document signing.

Trust Store Modes

ModeBehavior
'merge'Your custom certificates are added alongside the built-in roots
'replace'Only your custom certificates are trusted; built-in roots are ignored
typescript
const verifier = await createVerifier({
  trustStore: {
    certificates: [pemString],
    mode: 'merge',
  },
})

When to Use a Custom Trust Store

  • Enterprise CAs: If your organization uses an internal CA for document signing, add the root certificate via mode: 'merge'.
  • Restricted environments: If you want to trust only specific CAs (e.g., a national eID CA), use mode: 'replace' with just those root certificates.

Algorithm Policy

VerifyKit classifies signature algorithms into three tiers:

TierAlgorithmsBehavior
ForbiddenMD5, MD2, MD4Always Invalid. Not configurable — these algorithms are cryptographically broken.
DeprecatedSHA-1 family (SHA-1, SHA-1 with RSA, SHA-1 with ECDSA, DSA with SHA-1)Valid by default (Adobe Reader parity) with the algorithm name surfaced in the check result. Can be promoted to Warning via config.
StrongSHA-256 / SHA-384 / SHA-512 (RSA, RSA-PSS, ECDSA), Ed25519Valid.

Why the Adobe-parity default?

Adobe Reader treats SHA-1 document signatures as valid and displays the algorithm in the signature properties. VerifyKit matches this so a SHA-1 signature on an otherwise-sound document reads as "Valid" to end users, with the algorithm name available via signatureCheck.algorithmName for UI disclosure (the built-in React UI shows a "Signed with SHA-1" line in the signature summary).

Opting into stricter policy

For deployments that require a harder stance on SHA-1, pass an algorithmPolicy to createVerifier or call setAlgorithmPolicy before verifyPdf:

ts
import { createVerifier, setAlgorithmPolicy } from '@trexolab/verifykit-core'
 
// Option A — per-verifier
const verifier = await createVerifier({
  algorithmPolicy: { sha1: 'warn' },
})
 
// Option B — global
await setAlgorithmPolicy({ sha1: 'warn' })
// …verification calls now surface SHA-1 as a warning
await resetAlgorithmPolicy() // restore Adobe-parity default

With { sha1: 'warn' }, a signature whose only issue is the SHA-1 algorithm transitions from overallStatus: 'valid' to overallStatus: 'warning'. MD5/MD2/MD4 continue to produce overallStatus: 'invalid' regardless of policy.

Reading the algorithm from results

Every algorithmCheck result includes an algorithmName field (optional). Use it to drive your own disclosures:

ts
const algo = signature.algorithmCheck.algorithmName
if (algo && /SHA-?1\b/i.test(algo)) {
  // show a "Signed with SHA-1" badge
}

Revocation Proxy Security

The @trexolab/verifykit-plugin-revocation plugin performs online certificate revocation checking (OCSP and CRL). Because browsers block direct OCSP/CRL requests due to CORS, the plugin routes these requests through a server-side proxy.

SSRF Protection

If you implement a revocation proxy endpoint, protect it against Server-Side Request Forgery (SSRF):

  1. Allowlist target hosts. Only forward requests to known OCSP responder and CRL distribution point hostnames. Do not allow arbitrary URLs.
  2. Validate URL schemes. Only permit http:// and https:// schemes. Reject file://, ftp://, and other protocols.
  3. Block internal networks. Reject requests targeting private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, ::1).
  4. Set timeouts. Apply reasonable request timeouts (e.g., 10 seconds) to prevent resource exhaustion.
  5. Rate limit. Apply rate limiting to the proxy endpoint to prevent abuse.

Content Security Policy

If your application enforces a strict Content Security Policy, add the following directives:

Content-Security-Policy:
  script-src 'self' 'wasm-unsafe-eval';
  worker-src 'self' https://unpkg.com;
DirectivePurpose
script-src 'wasm-unsafe-eval'Required for WebAssembly compilation
worker-src 'self'Required for the PDF.js web worker
worker-src https://unpkg.comRequired only if loading the worker from the CDN

If you self-host the worker file, worker-src 'self' is sufficient.

Note: The 'wasm-unsafe-eval' directive is specifically scoped to WASM compilation. It does not permit arbitrary eval() calls.


Compliance References

VerifyKit implements signature verification algorithms consistent with the following standards and regulations. These references are provided for informational purposes and do not constitute legal advice or certification claims.

  • PAdES (ETSI EN 319 142): The SDK validates PAdES signatures at levels B-B, B-T, B-LT, and B-LTA. It parses CMS/PKCS#7 signature containers embedded in PDF documents per the PAdES specification.
  • eIDAS (EU Regulation 910/2014): The trust store and certificate chain validation support verification of qualified electronic signatures issued under the eIDAS framework, provided the appropriate trust anchors are configured.
  • ESIGN Act (US): VerifyKit can verify digital signatures that comply with the US Electronic Signatures in Global and National Commerce Act. The SDK does not impose jurisdiction-specific rules; it validates the cryptographic properties of the signature.
  • PDF 2.0 (ISO 32000-2): Signature parsing follows the PDF specification for digital signature dictionaries, byte range coverage, and incremental update detection.

Note: VerifyKit is a technical verification tool. It validates cryptographic integrity, certificate chains, and timestamp validity. Determining the legal standing of a signature in a specific jurisdiction requires legal counsel.


Recommendations

Keep Dependencies Updated

  • Update @trexolab/verifykit-core and @trexolab/verifykit-react regularly to receive trust store updates and security patches.
  • Pin pdfjs-dist to the version specified by VerifyKit to avoid compatibility issues.

Use HTTPS

  • Serve your application over HTTPS to prevent man-in-the-middle tampering with the WASM module or PDF.js worker.
  • If self-hosting the worker, serve it from the same origin over HTTPS.

Validate Proxy Endpoints

  • If using the revocation plugin, ensure your proxy endpoint validates and sanitizes all input URLs.
  • Apply authentication to the proxy endpoint if it is exposed on a public network.

Restrict Trust Stores in Sensitive Environments

  • For high-assurance use cases, use mode: 'replace' with a curated set of trusted root certificates rather than the full built-in set.

Handle Verification Results Carefully

  • Do not rely solely on overallStatus for security decisions. Inspect individual check results (integrityCheck, certificateChainCheck, revocationCheck, etc.) to understand the full picture.
  • Log verification results for audit trails when required by your compliance framework.