Trust & Security

Security your whole school can rely on.

You are trusting us with sensitive records — including children’s data. We treat that as the foundation of the product rather than a feature of it. This page sets out exactly how it is protected, in enough detail to be checked.

Getting into an account

Most breaches start at the front door, so this is where we spend the most care: proving that the person signing in is who they claim to be, and making a stolen credential worth as little as possible.

Passwords are stored as one-way hashes

Passwords are hashed with Argon2id — the algorithm designed to be slow and memory-hard specifically to frustrate cracking — and only the hash is ever stored. There is no path back to the original, which is why we can help someone reset a password but can never tell them what it was.

  • Changing a password requires the current one, and reusing it is refused
  • A successful reset immediately revokes every existing session for that account

Repeated guessing locks the account

Failed sign-in attempts are counted in a rolling window; once the threshold is crossed, the account is locked for a cool-off period. The lock is stored with the account rather than held in memory, so it survives restarts and cannot be shaken off by hitting a different server.

  • The lockout covers every way in — email address and username sign-in alike
  • Responses are worded so that a failed attempt does not reveal whether an account exists

Sessions are short-lived and rotate

The token that authorises day-to-day requests lasts fifteen minutes. Renewing it uses a separate refresh token that is exchanged for a new one every single time, so a captured refresh token is already spent by the time anyone tries to use it.

  • Refresh tokens live in a cookie the browser will not let JavaScript read, marked Secure and SameSite=strict, and scoped to the sign-in endpoints alone
  • Every refresh token is tracked server-side and can be revoked; the reason is kept — rotated, signed out, expired, signature mismatch, account deactivated, password reset
  • Cross-site request forgery is blocked by that SameSite cookie plus a matching token the app must echo back in a header

Password reset links are single-use and expire

A reset link is stored only as a hash — the link in the email is the only copy — and is marked used the moment it is redeemed. It expires one hour after it is issued, whether it is used or not.

The service refuses to start up insecurely

Configuration mistakes are a quiet way to lose all of the above, so production start-up asserts the settings that matter and fails loudly rather than degrading. A deployment that lacks secure session cookies, signing keys or the data-encryption key does not come up at all.

What each person is allowed to reach

Authentication answers “who is this?”. Everything after it answers “and what may they touch?” — checked on every request, against the specific action being attempted, with refusal as the default.

Every request is authorised individually

Protected endpoints pass through a policy check that evaluates the caller’s roles against the exact action the request represents before any handler runs. There is no “logged in, therefore allowed” tier: if nothing grants the action, the request is refused, and the refusal is recorded with its reason.

Roles are yours to shape

Schools start from ready-made roles and can build their own with a visual role builder that reads as a sentence rather than a permission matrix. Permissions are granular — per module, per action — so access can follow a real job rather than the nearest approximate template.

  • Permissions for a module a school has not switched on cannot be granted at all
  • Changes to who holds what are themselves auditable events, and raise a security alert

The portal you are acting in is part of the decision

The same person may be a member of staff and a parent. Requests declare which portal they are acting as, and that is weighed in the authorisation decision, so a parent-portal session cannot reach staff-side data even when the human behind it also works at the school.

Staff see the pupils they are responsible for

Beyond module permissions, which pupils a member of staff can see is narrowed by declared criteria — class, stage, status, house and so on — that are applied inside the query itself. A subject or form teacher reaching a class they are not assigned to is turned away by a dedicated check.

Platform administration is a separate world

Our own super-administration surface is a distinct context with its own session, its own cookie scope and its own permission set. It is not a privileged mode of a school account, and it cannot be reached by escalating one.

Keeping schools apart

A multi-school platform has one catastrophic failure mode: showing one school another’s pupils. We treat that as a database problem rather than an application problem, because application code can forget and the database cannot.

Isolation is enforced beneath the application

Every tenant-owned table — close to two hundred of them — has row-level security enabled and forced, with a policy that compares the row’s school against the school context the request is running under. It applies to every query, including ones a developer writes by hand, because it is the database applying it rather than the code asking nicely.

No context means no rows

The policy is written so that a connection with no school context matches nothing at all. The failure mode of a bug in our code is therefore an empty result, not somebody else’s data — the safe answer is the default answer.

Cross-school work is explicit

Background jobs and platform-level operations that legitimately span schools have to opt in through an explicit system context. There is no ambient bypass that ordinary request handling could stumble into.

A build gate keeps the coverage honest

The list of protected tables is regenerated from the schema itself, and every pull request runs a check that fails the build if a tenant table has lost its protection. Adding a table and forgetting to protect it is caught before it can be deployed, not after.

Protecting the data itself

Access control assumes the attacker comes through the front door. Encryption is what protects the records if someone ever gets around it — on the wire, on disk, and, for the most sensitive writing a school does, field by field.

Encrypted in transit

All traffic runs over TLS. In production we send a strict transport security header with a six-month lifetime, covering subdomains and marked for preload, so browsers refuse to connect over plain HTTP even once.

Encrypted at rest — and again, at the field level

The database and file storage are encrypted at rest by default. On top of that, the most sensitive free text a school holds is separately encrypted with AES-256-GCM before it is ever written, so it is unreadable to anything reading the underlying storage — over a hundred columns across more than fifty tables.

  • Covered: safeguarding and discipline records, clinic and medical notes, hostel medical details, HR casework and grievances, recruitment notes, and guardian and pupil notes and addresses
  • Each value carries its own initialisation vector, and the column it belongs to is bound into the encryption itself, so ciphertext cannot be moved from one field to another
  • Keys can be rotated without downtime, with a resumable re-encryption pass; production will not start without the key

What is deliberately not encrypted, and why

Encrypted text cannot be searched, sorted or filtered. Rather than encrypt a field and silently break the feature that depends on it, a small set of columns is left in plain text on purpose — message bodies, and a couple of fields that HR search relies on — and each one is recorded, with its reason, in the same registry that drives the encryption. Where we could narrow a search instead, we did: several case narratives are now encrypted because their list views search the reference and category rather than the story.

The registry is checked by the compiler and the build

One registry is the single source of truth for which columns are encrypted; the code that encrypts, the backfill and the key rotation all derive from it, and a mismatch is a compile error. A build gate catches the three things types cannot see — an entry with no encryption attached, a column of the wrong type, and a column that is both encrypted and queried.

Files are encrypted, and links to them expire

Uploads are encrypted by the storage service on write, and the requirement is signed into the upload authorisation so a browser cannot opt out of it. Files are never public: each view is served through a signed link that expires — an hour for downloads, fifteen minutes for uploads.

What we accept from the outside

Anything a browser or a third party can send us is treated as hostile until it has been checked — request bodies, file uploads and payment callbacks alike.

Requests are validated, and unknown fields are dropped

Every request body is validated against a declared shape before a handler sees it, and any property that is not part of that shape is stripped rather than passed through. Database access goes through parameterised queries throughout, so values are never assembled into SQL.

Uploads are inspected before they are stored

File uploads are checked against the size and content types the endpoint declares, and executable and script file types are rejected outright regardless of what they claim to be.

Payment callbacks are verified cryptographically

Webhooks from our payment provider are verified against a signature computed over the raw request body and compared in constant time. An unsigned or altered callback is discarded, so no one can fabricate a payment by calling the endpoint.

The money ledger is signed

Credit and wallet entries carry integrity signatures and are chained to the entry before them, with separate signing domains for balances and entries so one can never be replayed as the other. A silently edited financial record does not verify.

The API surface is kept small in production

Interactive API documentation is served only outside production. Cross-origin access is restricted to the origins we configure, and a state-changing request arriving without an allowed origin is refused.

A record of what happened

Prevention is never complete, so the platform is built to answer the question that follows an incident: exactly who did what, when, and was anyone turned away. That record is designed to be tamper-evident rather than merely present.

The trail is written where the decision is made

Audit entries are emitted at the same point that authorises the request. That means the record includes attempts that were refused, and why they were refused — the entries an investigation usually needs most, and the ones a bolted-on audit log usually misses.

Entries are sealed and chained

Each entry is sealed with a keyed signature over a fixed set of its fields and bound to the entry before it. Editing a record, or removing one, breaks the chain from that point on — so tampering is detectable rather than invisible. The canonical form is versioned, so improving what we record never invalidates sealed history.

The chain is periodically notarised

On a schedule, a contiguous run of the chain is sealed into a signed digest and written to object storage as a write-once record. Even if the database itself were compromised, the digests provide an independent copy of what the history was.

Trails stay readable, and stay bounded

Who performed an action is captured as a snapshot at the time it happened, so a trail still makes sense after someone’s role or name changes. The table is partitioned by month and provisioned ahead of time, and retention is configurable, with the option to archive before anything is removed.

Schools can read their own trail

This is not an internal-only facility. The audit trail is a feature of the platform: schools can search their own history by person, action, resource and date, and verify the integrity of what they are looking at.

Noticing when something is wrong

A record nobody reads is an archive, not a control. Activity is continuously summarised, compared against thresholds, and turned into alerts when the shape of it changes.

Activity is rolled up into metrics

Audit activity is aggregated into time-series metrics broken down by school, service, action and resource type, which is what makes “unusual” a measurable statement rather than an impression.

Alarms fire on sustained changes, not single blips

Each alarm sums its metric over a trailing window and only changes state after a configured number of consecutive breaches, so a momentary spike does not cry wolf and a slow-burn pattern does not slip through.

Security-relevant events raise alerts

Suspicious sign-ins, brute-force patterns, permission and access changes, large data exports and unusual activity each raise a graded security alert, which is routed to the people who need to see it.

Credentials never reach the logs

Authorisation headers, cookies and CSRF tokens are redacted at the logger itself rather than at each call site, so a single unlucky log line cannot write a live session into our logs.

How the software is built

Most security regressions are ordinary mistakes, not attacks. So the checks that matter run automatically on every change, and the ones that protect data are wired to fail the build rather than file a warning.

Every change is scanned before it can merge

Pull requests run secret scanning across the repository, a review of newly introduced dependencies, and an audit that fails on high-severity vulnerabilities in anything we depend on.

The safeguards have their own gates

The continuous integration pipeline verifies that tenant isolation coverage has not regressed, that the encrypted-column registry and the code agree, that migrations provision a database from scratch, and that architectural boundaries hold — alongside type checking, unit, integration and end-to-end tests.

Secure defaults, reviewed changes

Changes are reviewed before they land, and the defaults are chosen to be safe when someone forgets: refusal rather than permission, validation rather than trust, and start-up assertions rather than silent downgrades.

Least privilege inside the company too

Internal access to production systems is limited to what a task requires, granted narrowly and removed as roles change. Nobody needs a standing key to a school’s data to do their job.

Where it runs, and who else is involved

A platform is only as trustworthy as the companies behind it, so we keep that list short, name every one of them publicly, and bind each by data-protection obligations.

Reputable, managed infrastructure

The platform runs on established cloud infrastructure with network isolation and tightly scoped internal access, using managed services for the database and file storage so that patching and hardening at that layer are handled by specialists rather than improvised by us.

Every sub-processor is named

The companies that can process personal data on a school’s behalf — infrastructure and storage, managed database hosting, payment processing, and email and SMS delivery — are listed in a public register alongside what each one handles and where. Adding one is a change to that register, not a quiet configuration change.

The browser is told to be strict

Our pages are served with a content security policy, strict transport security, a no-referrer policy and content-type protection, are refused permission to be framed by other sites, and give up browser capabilities they do not need — camera, microphone, geolocation, payment and USB are all denied outright.

Your data stays yours

Security controls decide who can reach the data. Governance decides what we are allowed to do with it — and the answer is: only what the school asks of us.

The school is in charge of its data

Your school is the data controller and we are the processor: we act on your instructions, for the purpose of providing the service, and for nothing else. Our data processing terms set that out in full, including what happens when an agreement ends.

We do not sell it, and we do not advertise against it

We do not sell personal information, and we never use children’s data for advertising or profiling. There is no advertising business here to feed, and there will not be one.

We ask for as little as we can

The platform collects what running a school actually requires, and no more. Where a field is optional, it stays optional, and the data a school no longer needs can be removed under its own retention decisions.

Responsible disclosure

We welcome reports from security researchers. If you believe you have found a vulnerability, please email us with details and steps to reproduce, and give us a reasonable opportunity to investigate and remediate before any public disclosure. We will not pursue action against good-faith research that respects our users’ privacy and data, and we will keep you updated while we work on a fix.

Something not covered here?

If you are assessing EaseAcademia for a school or a group and need detail this page does not carry — a specific control, a data-protection question, or a completed security questionnaire — write to us and a person who works on this will answer.

Run your whole school on one connected platform

Start with the base package, switch on the modules you need, and give every parent, student and staff member a single place to log in.