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

Five failed sign-in attempts within fifteen minutes lock the account for fifteen minutes. 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
  • The account holder is told, in the app and by email, when their account is locked

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, 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, and the sign-in endpoints accept requests only from our own sites

A second factor is available, and a school can require it

Staff, administrators and parents can add an authenticator app to their sign-in: a six-digit code that changes every thirty seconds, backed by ten one-time recovery codes. There is deliberately no text-message or email factor, since both are weaker than the password they would back up, and the secret behind it is itself encrypted at rest.

  • A school can require it of every member of staff, meaning anyone with a role beyond pupil, graduate or parent, with a grace period of none, three, seven, fourteen or thirty days that runs from the join date for anyone who joins later
  • Once the deadline passes, the server refuses that account’s requests into the school, leaving open only its own account settings, signing out and setting up the second factor, and staff cannot switch it off while the school requires it
  • A school can reset a lost second factor only for an account used at that school alone, with a stated reason; only the owner can reset an administrator, and nobody in the school can reset the owner
  • Every reset is written to the audit trail and emailed to the account holder; pupils and graduates cannot hold a second factor, and for parents it stays optional

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.

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. So the school a request belongs to is never something a browser can choose, and no school query runs without one.

The school comes from the signed session

Every request runs under the school recorded in the person’s signed session token, not a value their browser supplies. A request that names a different school is refused before any handler runs.

Every school query is limited to that school

Data access goes through a shared repository layer — close to three hundred repositories are built on it — that adds the school to every query it constructs, and refuses outright to run a school query that names no school.

Cross-school work is explicit

Platform operations that legitimately span schools, such as scheduled jobs, use separately named methods to do it. Reaching across schools is a visible choice in the code, not something an ordinary query can do by accident.

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 — 167 columns across 79 tables.

  • Covered: safeguarding and discipline records, clinic and medical notes, hostel medical details, HR casework and grievances, recruitment notes, guardian and pupil notes and addresses, admissions and transfer papers, and exam questions and answers
  • 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 — thirteen in all, such as message bodies and the fields 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 most downloads (a day for message attachments, a week for profile photos and school logos), 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 limit and the content types each feature accepts. Message attachments, for example, are limited to images, PDFs and slide decks of up to 5 MB each.

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 — 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 summarised daily

Each day, the latest run of the chain is sealed into a signed digest and written to file storage, outside the database. If the database were ever tampered with, the digests give a separate record 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. 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 export it.

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 changes state after the number of breaching readings configured for it, so it can be tuned to ignore a momentary spike without missing a slow-burn pattern.

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 limited roles stay limited, 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, storage and 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.