Skip to content

Security

Isolation first, then everything else

A laboratory should not have to trust that we remembered a WHERE clause. The strongest guarantee we can offer is structural, so that is the one we built the system on.

The isolation model

One database per laboratory. Not one table with a column.

Most multi-tenant systems put every customer in shared tables and separate them with a tenant identifier on each row. It works until one query is written without the filter — and then one laboratory sees another’s patients.

When a laboratory is onboarded, Clinexas provisions it a physically separate database and its own subdomain. The connection is resolved per request from the address you arrived on. There is no shared patients table, so there is no query that could cross the boundary — correct or careless.

A session token additionally carries the laboratory it was issued for, and is rejected if presented against a different one. Requests for an unknown or suspended laboratory are refused before authentication runs at all.

What a request passes through

  1. 1Resolve the laboratory — from the subdomain — unknown or suspended stops here
  2. 2Rate limit — before any credential is examined
  3. 3Authenticate — token checked, and bound to this laboratory
  4. 4Authorise — role, then any permissions the laboratory has granted
  5. 5Validate — strict allow-list on every field submitted
  6. 6Apply clinical rules — such as the self-approval bar
  7. 7Record — privileged actions written to the audit log

Controls

The rest of the defences

Isolation prevents the worst outcome. These handle the ordinary ones.

Authentication

  • Passwords are stored only as salted hashes. Nobody at Clinexas can read one.
  • The short-lived access token is held in memory, never in localStorage, so a cross-site scripting flaw cannot lift it out of storage.
  • The long-lived refresh token sits in an HttpOnly, SameSite=Strict cookie scoped to the auth path — the browser will not hand it to scripts or to another site.
  • Sign-in endpoints are rate limited.

Authorisation

  • A user's role, branch and active status are re-read from the database on every request, so revoking access takes effect immediately rather than when a token expires.
  • Roles gate every endpoint, and each laboratory can widen a role's permissions without us shipping code. Grants can only widen — never silently revoke a restriction.
  • Some rules a role check cannot express are enforced in the service layer, such as the bar on approving a result you entered yourself.

Accountability

  • Privileged actions write to an audit log with the acting user, so an amended result or a waived invoice always has an author.
  • Releasing a report is recorded per test — the method, the recipient, the staff member and the time.
  • Our staff do not browse laboratory data. Support access is requested, recorded and granted by the laboratory.

Minimum disclosure

  • The public report verification page confirms provenance only — laboratory, lab number, test and date. It shows no parameter values and no diagnosis.
  • An unknown code and a not-yet-approved code return the identical response, so the page cannot be used to test whether a record exists.
  • Request payloads are validated against a strict allow-list; unexpected fields are rejected rather than quietly stored.

Reporting a vulnerability

Report it privately to [email protected] before disclosing it publicly, and give us a reasonable window to fix it. We will keep you updated, and we do not pursue researchers who act in good faith and do not access data that is not theirs.

Please do not test against a live laboratory’s workspace — real patient data sits behind it. Ask us and we will arrange somewhere safe.

How we handle the data itself is covered in the Privacy Policy.