Consent Docs

Security and data protection

What personal data PIEDAP Consent holds, how it is protected, who is responsible for what, and the gaps a reviewer should know about.

Whitepaper15 min readFor decision makers, DPOs and reviewers

For security reviewers and Data Protection Officers who are assessing PIEDAP Consent as a vendor, or before running it themselves.

Summary#

PIEDAP Consent is built to hold identifiers only as hashes. A readable copy is kept with a record only if a project asks for one, and then encrypted. Secrets are encrypted by field or stored as hashes. Projects are kept apart by the session, by the key and by database constraints. Consent evidence is sealed, signed and anchored, so a change after the fact can be detected.

The software does not secure a deployment by itself. TLS, network isolation, encryption at rest, backups and the protection of the deployment's keys belong to whoever runs it. Access inside a project is all or nothing. The audit log is not tamper-evident. Rights requests and the breach register are deleted only when a project is closed, and the audit log never is.

PIEDAP Consent holds no certification against SOC 2, ISO/IEC 27001 or any other framework. No independent penetration test has been carried out.

What this paper can and cannot tell you#

The paper describes the controls in the software as it ships. They are implemented in code. Where a control is covered by automated tests, the text says so.

It cannot give you an attestation. There is no audit report to hand to your assessor, and no test report from an outside firm. A reviewer has to judge the design from the description here and, on a self-hosted deployment, from the code.

Much of your real exposure depends on how the deployment is run, not on the software. This paper therefore separates three parties throughout: the software, the operator and you.

Who is responsible for what#

Party Role under the DPDP Act Responsible for
You, the organisation that asks for consent Data Fiduciary Purposes, wording, which identifiers are recorded, how long evidence is kept, who has access to the project
The operator of the deployment Data Processor, acting on your instructions Running the software, holding the keys, TLS, the network, backups, updates, logs
The person who answers the notice Data Principal Nothing. Their rights are served through you
Partners you name on a purpose Your own processors or other fiduciaries Nothing in this product. It never sends them data

If you run the deployment yourself, you are both the Data Fiduciary and the operator. There is then no processor for the consent records, and every operator duty in this paper is yours.

What personal data is held#

Data How it is stored Why
The person's identifier: a random browser id, or your choice of email, phone, user id, device id or token A keyed hash only, unless the project keeps an encrypted readable copy To attach a decision to a person and find it again
Decisions per purpose and partner, and the form version In clear, sealed Evidence of consent
Country and subdivision Short codes To choose the law that applies
IP address A salted hash. The address is not stored with the record Provenance of the record
Browser string and page address In clear, cut to 500 characters Provenance of the record
Extra metadata from your integration In clear, up to 8 KB Whatever you send
Parent or guardian verification Relationship, method, time, and a keyed hash of the reference Evidence of verifiable consent
A rights request The identifier as a keyed hash, the contact encrypted, the note and timeline in clear To track and answer the request
Cookie and storage names, third-party hosts In clear, without values The cookie list
How consent was taken in a branch, by phone or on paper Channel, method, your evidence reference and a document SHA-256, sealed. Staff and site references as keyed hashes Evidence of how consent was obtained
A notice campaign's people Identifiers as keyed hashes only. Your reference encrypted. Answers in clear, sealed daily Evidence that a notice was sent and answered
Processor tasks Partner, action, purposes and dates, with no identifier Accountability for passing on withdrawals and erasures
Actions by your team Audit log: user id, email, time, IP address, browser, action, and reads of personal data Accountability
Request log One line per request, with the client's IP address Operation

The product does not collect some things at all. The runtime sends no cookie values, form inputs or page content. Location stops at country and, where needed, subdivision. There is no contact list, because the product sends no email or SMS. Your team's accounts and sessions belong to the PIEDAP IAM service, not to this product.

How identifiers are protected#

An identifier is stored as an HMAC-SHA256 under a pepper. The pepper is a secret of the deployment, held outside the database. The hashes in a copy of the database alone cannot be matched against a list of known identifiers.

A keyed hash is not anonymous data. Whoever holds the pepper and a candidate identifier can test it. The protection is against a database leak, not against the operator.

You still need to know whom a record is about. Each project has a fingerprint key of its own. The subject fingerprint is a second HMAC of the same identifier under that key. You recompute it from your own identifier, so the service does not need to hold the identifier in readable form.

There are four places where a readable identifier can exist:

  1. The encrypted copy, when Store a reversible subject identifier is on. Webhooks then include the identifier, and the delivery log holds it for 30 days.
  2. A rights request filed on the hosted form. The identifier the person typed, and the reply address, go to you by webhook. They sit in the delivery log for 30 days.
  3. Whatever your integration puts in metadata.
  4. The reference you give each person in a notice campaign, encrypted, when you choose to send one. It also appears in campaign.withdrawn webhooks.

You control all four. A person search in Records sends the identifier in the request body, which is not logged.

How secrets are held#

The deployment holds its secrets as environment variables, outside the database. The most important are the encryption key, the subject hash pepper, the evidence chain keys and the receipt signing key.

Inside the database, sensitive values are protected by field:

  • Readable identifiers, request contacts, identity secrets, webhook signing secrets and fingerprint keys are encrypted with AES-256-GCM.
  • Secret keys are stored only as a SHA-512 hash salted with a deployment secret. A lost key cannot be recovered.
  • Email confirmation tokens are stored hashed.

Two of the deployment secrets can never be changed. Every stored identifier hash depends on the pepper. There is no re-encryption tool for the encryption key. A reviewer should ask how these two are generated, stored and backed up, because losing either makes records unreadable or unfindable.

When no dedicated chain key or signing key is set, both are derived from another deployment secret, in the end the encryption key. A production deployment should set dedicated values and keep them in a secret manager. The API refuses to start in production without an encryption key.

How access is controlled#

Your team. The console has no accounts of its own: the PIEDAP IAM service signs people in. The API verifies the session cookie's signature and loads the session from Redis. It takes the organisation, account and project from that session, never from request parameters. State-changing requests need a header that a cross-site form cannot send.

Your sites and servers. A publishable key is public. It is bound to a list of allowed origins, and a live key answers only those. A secret key is sent from your server, and calls on a person as a whole need a scope you grant per key. A secret key sent from a browser is refused, and the console marks it Exposed.

A consent check needs its own scope, consent:check. Each identifier checked counts against the key's budget (600 a minute unless you change it), which limits how fast a copied key can test identifiers. Give the scope to a key used only for checks, and revoke any key you suspect was copied.

A notice campaign link lets whoever holds it answer for one person. Link tokens are stored as HMACs. An encrypted copy is kept for 7 days so the links can be downloaded, then cleared.

The operator. New keys are test keys. A key becomes live only when the operator approves it, through endpoints that take a separate platform key.

The database. Foreign keys include the project id. A row cannot point at another project's data, even if a query forgot to filter.

Roles. Every console route checks the permission it needs against the person's role: owner, privacy officer, grievance officer, content editor, developer or auditor. Until an owner turns roles on, everyone who can open the project has every permission. Erasure, exports and key reveals can also wait for a second person. The audit log shows who did what.

How evidence is protected#

Each change to a consent record is sealed with HMAC-SHA256 and linked to the state before it. Receipts and daily anchors are signed with Ed25519. Each day's events are combined into a Merkle root that is sent to your webhooks. Chain replay, signatures and inclusion proofs are covered by unit tests, including tampering cases.

The effect is that an edit made in the database, outside the product, shows up when the record is checked. A seal fails, or the chain is reported as not keyed. Whoever holds both the chain key and the signing key can still create new, validly sealed records. Anchors that you keep outside the deployment limit that to days not yet anchored. Proving consent sets out the argument in full.

Network, input and outbound calls#

In transit. The API speaks plain HTTP inside the deployment. The operator's load balancer or reverse proxy ends TLS. The API has no setting of its own for TLS on its PostgreSQL and Redis connections. Both must sit on a private network.

Input. Request bodies are validated against a schema, and unknown properties are dropped. Markup is stripped from plain-text fields. The console, the banner and the hosted pages write stored values as text, not as HTML.

Outbound. The API calls addresses that customers type in: webhook endpoints, scan targets and custom translation endpoints. Each is resolved before use and refused when it points at a private, loopback or link-local network. The cloud metadata range is always refused. For webhooks and translation endpoints, the address is checked again at connection time, and redirects are not followed.

Private networks. On a deployment you run, the operator can list networks in PRIVATE_NETWORK_ALLOWLIST. A webhook endpoint reaches an address there only when its own Private network switch is on; translation calls may reach the list without a switch. The list is empty by default, a range wider than /8 is refused, and the metadata range is never reachable.

Rate limits. Requests are limited per client address, per key and per person call. Counters are shared through Redis when it is connected. The client address is read from X-Forwarded-For only when the connecting proxy is trusted.

In the browser. Hosted pages are served with a content security policy of default-src 'none' and run no scripts. A pinned runtime build can be loaded with Subresource Integrity. The banner renders inside a shadow root, unless you place it inline.

Logging and accountability#

There are two records of activity, and they differ in strength.

The request log is one JSON line per request, on standard output. It holds the method, the address requested, the status, the time taken, the client's IP address and a request id. Request bodies are not logged. The values of token, key, identityProof, secret, subjectId and search parameters, and notice link tokens, are replaced before writing. The operator decides where the log is kept and for how long.

The audit log records every state-changing console request: who, when, from which address, on what, and the result. It records reads of personal data: opening a record or a rights request, a person search, a receipt and a download of the log. It records property exports, key reveals, and each call on a person made with a secret key, with the first 12 characters of the identifier's keyed hash.

The audit log is append-only in the database: a trigger refuses changes and deletions, though the table's owner can still turn it off. Each entry is sealed with HMAC-SHA256 to the one before it, so a changed, removed or inserted entry shows when the log is verified. It is not anchored daily as consent evidence is. The reply waits until its entry is written. A reply whose entry could not be stored is marked X-Audit-Log: not-stored, counted and kept on standard output.

Each entry is also one JSON line on standard output. A SIEM can pull the log with a key that holds audit:read, and you can download it. Your SIEM copy is the independent record.

Retention and deletion#

Data How long it is kept
Consent records and their events While the consent is in force, then for the project's Keep evidence for period: 1, 3, 7 or 10 years, 7 by default
Identifying details on an erased record Removed at once. For a record under the DPDP Act erased within a year of its last processing, removed when the year is up
Test records 30 days after their last change
Webhook deliveries and settled outbox events 30 days
Idempotency records 24 hours
Rights requests, the breach register, daily anchors, notice campaigns Until the project is closed
The audit log Not deleted by the product; the database refuses deletions
Processor tasks As long as their record
Request log Set by the operator
Everything else in a closed project Deleted 30 days after an owner schedules the closure, or after the operator's PROJECT_DELETION_DAYS. Legal holds postpone it
Backups Outside the product. Backups keep deleted data until they expire

Evidence past its period is deleted only when the operator has turned retention enforcement on. Otherwise records stay. The job never deletes a record less than a calendar year after its last processing.

A rights request keeps the contact the person gave, encrypted, for as long as its row stays. If your policy sets a limit for requests or the breach register, the operator removes old rows in the database. Old audit entries are archived and removed by the table's owner; see Download the audit log. At the end of a contract an owner exports the data and closes the project, which can be cancelled until its date; see Close a project.

Outside services and data location#

The software needs no outside service to record consent. It can make these calls:

Call What is sent
Your webhook endpoints The event. For consent events, the identifier type and fingerprint, and the identifier only if the project stores a readable copy
The translation service you chose The form or purpose wording. No personal data about visitors
The IAB vendor list Nothing. It is a download
The sites you ask it to scan Ordinary page requests from a headless browser
The PIEDAP IAM service An API token, to be checked

Location is worked out from a database inside the API, or from headers set by the operator's CDN. No outside geolocation service is called.

Everything the product stores is in one PostgreSQL database. Sessions and rate-limit counters are in Redis.

The country the data sits in is where the deployment runs. The operator states it in DEPLOYMENT_REGION, DEPLOYMENT_COUNTRY and DEPLOYMENT_PROVIDER. Every project sees it in Settings, and the DPDP Act check notes it against section 16. Whoever hosts the deployment is the operator's sub-processor.

Gaps a reviewer should weigh#

These are stated in the documentation, and listed here together.

Gap What it means for you
No certification and no independent penetration test You assess the design yourself
No TLS setting for the database and Redis connections The operator must keep both on a private network
The audit log is not anchored Each entry is sealed, but there is no daily anchor. Stream it to your SIEM for an independent copy
Retention enforcement is off by default Ask the operator whether it is on
One signing key at a time Save the public key set. A changed key breaks the online check of older receipts
The pepper and the encryption key cannot be rotated Their storage and backup matter more than usual
/api/docs is served without sign-in It lists endpoints and holds no data. The operator can block the path
No pipeline for dependency or image scanning in the repository Watching advisories is the operator's
Private networks can be opened for webhooks Only networks the operator lists, and only for endpoints with Private network on

Questions for the operator#

  • Are the region and country in Settings where the deployment runs? Where are the backups, and is any data moved outside India?
  • Is the database encrypted at rest, and is a restore tested on a schedule?
  • Are the pepper, the chain keys and the signing key dedicated values, held in a secret manager?
  • Who can reach the database, Redis, the API port and the metrics endpoint?
  • Who in IAM can open our project?
  • Is retention enforcement on, and how long are the request logs kept?
  • What is PROJECT_DELETION_DAYS, and how long do backups keep a closed project's data?
  • How do you learn about security advisories for the dependencies, and how fast do you apply updates?
  • Which sub-processors host the deployment?

Record the answers in your contract. Data processing gives you the facts to put in it.

Further reading#

↑↓ to move, Enter to open