- AES-256-GCM encryption for sensitive fields at rest.
- HMAC-SHA256 to sign critical documents and detect any later modification.
EncryptionService on the backend and
use the same master key (ENCRYPTION_KEY) derived via scrypt.
Encryption at rest
When encryption happens
Every field that contains personal information or credentials is encrypted before being persisted in MongoDB. In particular:- Client data in anonymous responses: email and phone are
stored encrypted (
form-submission). - Integration credentials and OAuth tokens (when a company connects Woku with Salesforce, Sperant, etc.).
- Webhook secrets that the company configures to receive events.
How it works
Each encryption uses a new IV, so encrypting the same value twice
produces two different ciphertexts (equality cannot be inferred by
inspection). The
authTag is validated on every decryption: if the
ciphertext was altered, decryption throws an error instead of
returning garbage.
Search over encrypted fields
To be able to search by email without decrypting every record, Woku stores a parallel SHA-256 hash (generateSearchHash) in lowercase.
The search compares hash against hash, not text against text.
Integrity signatures (HMAC-SHA256)
What they are for
When a company requires that its form responses cannot be modified after being stored (legal auditing, compliance in a regulated sector), Woku can sign each document with HMAC-SHA256 at write time and verify the signature when reading it. If someone modifies the document in the database directly, verification fails and the read returns an error.How it works
Scope = domain separation: each use case signs with a
different scope (e.g.:
form-submission, form-response). A signature
generated for one scope is not valid in another, so an attacker
cannot move signatures between document types.
Canonical JSON: Woku recursively sorts the keys of the
objects before signing, so two logically equal representations
of the same document produce the same signature (regardless of the
order in which the API received them or Mongo wrote them). Arrays are NOT
reordered, positional order is semantically significant.
Enable it for your company
There is a per-company flag:integrityHashEnabled. When it is enabled,
new writes of critical documents are signed; reads
verify and fail if the signature does not match.
Current status: the primitive (signDocument/verifySignature) is already available on the backend and the flag exists in the company model. Automatic integration withform-submissionandform-response(whenintegrityHashEnabledis active) arrives in a later release. Customers with strict compliance who need this sooner can coordinate it with support.
Key management
ENCRYPTION_KEYlives in SSM Parameter Store as aSecureStringunder/shared/prod/secrets/ENCRYPTION_KEYand is injected into the container at startup (see Secrets management).- Key rotation is manual today. It involves:
- Generating the new key.
- Re-encrypting all records (migration script) with the new
key before switching
ENCRYPTION_KEYin SSM, during a maintenance window. - Discarding the old one.
- There is no HSM in V1; the key lives in the process environment variables (in memory) and is never logged.
Guarantees
- Authenticated encryption: if an attacker modifies the ciphertext in
the database, the
authTagdoes not validate and decryption throws an error, so the altered record is not returned as if it were valid. - Constant-time signature:
verifySignatureuses comparison resistant to timing attacks. - Domain separation: the scope in HMAC prevents reuse of signatures between document types.
- Deterministic JSON: canonicalization removes ambiguity from key order.
Known limitations
- Rotating
ENCRYPTION_KEYrequires a maintenance window (there is no key versioning yet). - Signing of
form-submission/form-responseis not wired automatically; it arrives in the Stage 10 release. - Applying it to third-party integration credentials depends on those integrations being built (Stage 6).