← SOC 2 · ISO and SOC Certifications · All Tools

SOC 2 Evidence Helper

An auditor's evidence request list arrives written in audit language and lands on engineers who have to turn each line into a file. This decodes the common requests: what is actually being tested, what to produce, where to get it, and why it gets sent back.

Paste a line from your request list, or browse by area.

All (50) Access controlBackup and recoveryCapacity and availabilityChange managementCommunicationData handlingEncryption and data protectionIncident managementInfrastructureMonitoring and loggingPeoplePhysical and environmentalVendor managementVulnerability management
Six rules that apply to every item on this page

Give the population, not your sample. When a request says “listing” or “all”, export the complete set and let the auditor choose what to test. Supplying a hand-picked subset is treated as a control weakness in itself, because it means you decided what was examined.

Every screenshot needs four things. The system it came from (URL or hostname visible), the date and time, the account that took it, and the full unfiltered view. A cropped screenshot with no timestamp is the single most common reason evidence is returned.

Prefer an export to a screenshot. Auditors ask for screenshots because they work everywhere, not because they are best. A CSV or JSON export from the console or API is reproducible, complete, and harder to dispute. Provide the export and a screenshot of the screen it came from.

Date it inside the observation period. Evidence produced after the period ended does not test the period. For a Type 2, evidence has to fall within the window, and for recurring controls it has to appear at the stated frequency across the whole window.

Redact secrets, not context. Mask passwords, tokens and keys. Do not mask usernames, timestamps, hostnames or role names -- those are the parts being tested, and over-redaction makes the evidence unverifiable.

Name the file so it can be found again. Something like 2026-03-14_okta_mfa-policy_prod.png. Auditors work through hundreds of files and unnamed screenshots generate follow-up requests.

Access control

Password or authentication settings for in-scope production applications (include screenshots of MFA in use if applicable)

#

What they are actually testing

The auditor is not collecting your password policy for its own sake. They are testing that access to production is protected by an authentication mechanism that is actually enforced, and that its configuration matches what your own policy claims. Two separate assertions: the setting exists, and it applies to everyone in scope. The parenthetical about MFA is the part that carries most of the weight — password rules are weak evidence on their own, and a configured MFA policy with no exceptions is strong evidence.

Typically maps to: Security (common criteria) — logical access; authentication of users before granting access
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The authentication policy configuration for each in-scope production application and for the identity provider in front of them.
  • Evidence that MFA is enforced — the policy, and the population it applies to.
  • A user listing for each in-scope application showing MFA status per user, so the auditor can see there are no uncovered accounts.
  • The list of any exceptions (break-glass, service accounts) with their compensating controls.
  • Your written password/authentication policy, so the configuration can be compared against what you committed to.

Where to get it

Identity provider (Okta, Entra ID, Google Workspace, JumpCloud) — This is the strongest evidence and where to start. Screenshot the authentication or sign-on policy showing the factor requirement and who it applies to, then export the user list with MFA enrolment status. In Entra ID that is the Conditional Access policy plus an authentication-methods report; in Okta, the global session policy and app sign-on policy plus a user report; in Google Workspace, the 2-Step Verification enforcement setting plus the user list with the 2SV column. Show the policy is ON and in 'enforced' state, not 'report only' — a Conditional Access policy in report-only mode enforces nothing, and auditors look for this specifically.

Application-level settings (where the app has its own) — For each in-scope application, screenshot its security/authentication settings page: minimum length, complexity, lockout threshold, session timeout, and whether SSO is mandatory. If the app allows both SSO and local passwords, show that local login is disabled — otherwise the identity provider's MFA is bypassable and the whole control fails.

Cloud consoles (AWS, Azure, GCP) — AWS: the IAM credential report is the single best artefact — `aws iam generate-credential-report` then `aws iam get-credential-report --query Content --output text | base64 -d > credential-report.csv`. It lists every user, whether MFA is active, password age and key age, in one file. Also capture the account password policy: `aws iam get-account-password-policy`. Azure: the Conditional Access policy plus `Get-MgReportAuthenticationMethod...` or the authentication methods activity report. GCP: the org policy and the Cloud Identity 2SV enforcement setting.

Oracle Cloud Infrastructure (OCI) — First establish which IAM you are on, because the evidence differs: newer tenancies use identity domains, older ones the legacy IAM. With identity domains, capture the password policy and the MFA settings under the domain's security settings, plus the sign-on policies — these are OCI's equivalent of Conditional Access and carry the same question of who they actually apply to, so show the group scope rather than just that a policy exists. Export the user list from the domain with the MFA enrolment attribute. On legacy IAM, `oci iam user list --all` returns `is-mfa-activated` per user, which is the single cleanest artefact — the equivalent of the AWS credential report. Two OCI-specific traps worth pre-empting, because an auditor who knows the platform will ask: federation, where the tenancy federates to an external identity provider and the real MFA control lives there — produce the IdP evidence too, and show that local IAM users cannot bypass it; and API signing keys and auth tokens, which authenticate without any interactive factor at all, so list who holds them and how they are governed. Also capture the membership of the tenancy Administrators group, which is the first thing looked at.

Linux servers with direct access — Show that password authentication is off, so the authentication control is the key rather than the password policy: `sshd -T | grep -Ei 'passwordauthentication|permitrootlogin|pubkeyauthentication'` captures the effective running configuration rather than what the file says, which is the distinction auditors care about. Add `grep -E '^(PASS_MAX_DAYS|PASS_MIN_LEN)' /etc/login.defs` and the PAM password quality configuration if local accounts exist at all.

Databases — Frequently missed and frequently the weakest point, because a database often allows local authentication that bypasses SSO entirely. Show the authentication method configuration (for PostgreSQL, `pg_hba.conf` and `SHOW password_encryption;`) and the account listing. If humans connect directly to production databases, the auditor will follow that path.

The 'in-scope' part — Before producing anything, confirm which applications are in scope with the practitioner and produce the same evidence for every one of them. A complete set for four applications when the system description names six is an immediate follow-up request.

Why it gets sent back

  • A policy document with no configuration screenshot — that shows intent, not enforcement.
  • A screenshot of MFA on the auditor's own test account, or on yours. The request is about the population, not one user.
  • A Conditional Access or sign-on policy left in report-only mode.
  • A user list where some accounts show no MFA and the exceptions are not explained.
  • Evidence for the identity provider only, when applications also accept local logins that bypass it.
  • Undated screenshots, or screenshots dated after the observation period closed.

Related

/security-settings · /user-management-permissions

Access control

Account lockout settings for in-scope production applications

#

What they are actually testing

A companion to the authentication-settings request, and usually sent alongside it. The auditor is testing that repeated failed logins are throttled or stopped rather than allowed to continue indefinitely, and that the configured values match what your own policy states. As always, you are measured against your own commitment, which is a good reason for the policy to describe what you actually do. One nuance worth understanding before you answer, because it changes what good evidence looks like: where you have disabled password authentication entirely, lockout thresholds are close to irrelevant — there is no password to guess. Saying that plainly, and showing the configuration that proves it, is a stronger answer than producing a lockout number for a system nobody can attempt a password against. Say it explicitly though; leaving the field blank reads as an omission.

Typically maps to: Security (common criteria) — logical access; protection against repeated unauthorised authentication attempts
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The lockout configuration per in-scope production application and for the identity provider: threshold, lockout duration, and reset or observation window.
  • Your written policy, so the configuration can be compared against it.
  • Evidence of what happens on lockout — automatic unlock after a period, or a verified manual unlock process.
  • Where lockout does not apply because password authentication is disabled, the configuration demonstrating that.
  • Any systems deliberately excluded, with the reason and what protects them instead.

Where to get it

Identity provider — Where SSO is in place this is the primary artefact and often the only one that matters, because the applications never see a password. Screenshot the lockout or sign-on policy showing threshold, duration and reset window, and show which users or groups it applies to. Entra ID's smart lockout and Okta's authentication policy both expose these as explicit numbers.

Applications with their own authentication — Per in-scope application, the security settings page showing the threshold and duration. If an application has no lockout capability at all, do not hide it — state it, and describe the compensating control, which is typically a WAF or gateway rate limit in front of it. An acknowledged limitation with a compensating control is a normal finding; a silent one is not.

Cloud consoles — AWS: the account password policy has no lockout setting, which surprises people — `aws iam get-account-password-policy` will not answer this request. The honest answer is that console access is protected by MFA and by federation, and that is what you evidence. Azure: the Entra ID smart lockout threshold and duration. GCP and OCI: the policy in the identity domain or Cloud Identity settings.

Linux servers — Only relevant if password authentication is possible at all, so lead with whether it is: `sshd -T | grep -i passwordauthentication`. If it returns `no`, that is your evidence, and it is better evidence than a lockout number. Where local passwords do exist, show the PAM faillock or tally configuration (`grep -r faillock /etc/pam.d/ /etc/security/faillock.conf`) with deny count and unlock time, plus `faillock --user <name>` to show it is actually recording. If you run fail2ban or similar at the network layer, include its jail configuration — it is a different control but it answers the same underlying risk and auditors accept it when described as such.

Databases — Often the weakest point, because databases frequently allow local authentication with no lockout whatsoever and sit behind nothing. Show the account policy where the engine supports one, and where it does not, show what restricts reachability instead — network policy, private subnet, no route from outside.

Proving it works, if asked — Some auditors ask for demonstrated behaviour rather than configuration. A short screen recording or a screenshot sequence of a test account being locked after the threshold, timestamped, settles it. Use a dedicated test account, never a real user's.

Why it gets sent back

  • A policy document stating a threshold, with no configuration screenshot.
  • Configured values that do not match the stated policy — either is defensible, the mismatch is not.
  • Evidence for the identity provider only, while an application also accepts local logins with no lockout.
  • A blank or omitted answer for a system where password login is disabled, instead of saying so and showing the configuration.
  • Lockout with no unlock path, or an unlock process with no identity verification — an unlock that anyone can request by email is its own weakness.
  • Undated screenshots, or ones taken after the observation period ended.

Related

/security-settings · /user-management-permissions

Access control

Complete user listing for in-scope systems, including role or permission level

#

What they are actually testing

The auditor needs the population before they can sample anything else. This listing is also what the joiner/leaver and access-review tests are checked against, so an incomplete one undermines several later tests rather than just this one.

Typically maps to: Security — logical access; least privilege
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • One export per in-scope system: username, full name, role or group, status, creation date, last login.
  • A mapping of service and shared accounts to an owner and a purpose.
  • The date the export was taken, and by whom.

Where to get it

Identity provider — Export all users with their group memberships and application assignments. This is the master list the others are reconciled against.

AWS — `aws iam list-users`, `aws iam list-roles`, and per user `aws iam list-attached-user-policies` / `list-groups-for-user`. The credential report also covers most of this in one file.

Oracle Cloud Infrastructure (OCI) — `oci iam user list --all` for the population and `oci iam group list` with `oci iam group list-users --group-id <id>` for membership; policies come from `oci iam policy list --compartment-id <ocid>` and are where the permissions actually live, since OCI grants to groups through policy statements rather than attaching rights to users. With identity domains, export from the domain instead and say which you are on. Two things a plain user list leaves out. The tenancy Administrators group, which is the shortest and most consequential row in the export. And API signing keys: `oci iam user api-key list --user-id <id>` per user, because a key is a working credential that never passes through a login screen — a listing of who can log in is not a listing of who has access.

Linux — `getent passwd` for accounts and `getent group sudo wheel` (or your elevation group) for who can escalate — the second is the one that gets tested.

Databases — The role and grant listing; for PostgreSQL `\du` or a query against `pg_roles`, including which roles carry SUPERUSER.

SaaS applications — Each admin console has a user export. Include the role column — a list of names without permission levels does not answer the request.

Why it gets sent back

  • Active users only, when the request said complete — disabled accounts that were never removed are exactly what is being looked for.
  • No role or permission column.
  • Service accounts with no named owner.

Related

/user-management-permissions

Access control

Evidence of periodic user access reviews performed during the period

#

What they are actually testing

Testing that somebody with authority looked at who has access, decided it was still appropriate, and acted where it was not. The review happening is half of it; the other half is that the removals it identified were actually carried out. Auditors routinely find reviews completed with findings and no follow-up.

Typically maps to: Security — logical access is periodically reviewed and modified as needed
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The review record for each occurrence in the period, at your stated frequency.
  • Who performed it, who approved it, and the date — approval by someone other than the person being reviewed.
  • The listing the review was performed against.
  • The resulting actions, and evidence each one was completed with its date.

Where to get it

If you run reviews in a ticket or GRC system — Export the ticket with its full history: the assignment, the reviewer's comments, the sign-off, and linked remediation tickets. This is the cleanest form.

If you run reviews in a spreadsheet — Keep the file for each cycle with reviewer name and date inside it, and keep the removal tickets it generated. Store them somewhere with reliable timestamps — a file whose only date is 'last modified' is weak.

Closing the loop — For each access the review said to remove, produce the removal evidence: the ticket, and a later user listing that no longer contains the account. That before-and-after pair is the strongest version of this evidence.

Why it gets sent back

  • One review when your policy says quarterly and the period was twelve months.
  • A review with findings and no evidence they were remediated.
  • Self-review — the system owner reviewing their own access.
  • A signed summary with no underlying listing attached.

Related

/user-management-permissions

Access control

For a sample of terminated employees, evidence that access was revoked timely

#

What they are actually testing

Comparing two dates: when the person left, and when their access ended. The control is your stated timescale, so the test is against your own commitment rather than an external standard — which makes an unrealistic policy actively harmful.

Typically maps to: Security — access is removed when no longer required
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The complete list of leavers in the period, with termination dates — the auditor samples from it.
  • Per sampled leaver: the offboarding ticket, the deactivation record from each system, and the timestamp of each.
  • Evidence the account is gone now, from a current listing.

Where to get it

The leaver list — From HR, not from IT. Reconciling the HR list against your identity provider is the test, so the populations have to come from different sources.

Identity provider — The system log entry for the deactivation, with its timestamp. Okta's system log, Entra ID's audit log, Workspace's admin audit log all export this.

Anything outside SSO — The accounts most often missed: VPN, database logins, cloud console IAM users, source control, CI/CD, monitoring, on-call paging, shared password vault. Produce evidence per system, not just for the IdP.

Why it gets sent back

  • An HR list and an IdP list that disagree.
  • A revocation date later than your policy allows, with no explanation.
  • Evidence for SSO only, while a git or database account for the same person is still active.

Related

/user-management-permissions

Change management

For a sample of changes, evidence of request, approval, testing and deployment

#

What they are actually testing

Following individual changes end to end. The specific thing being tested is that approval happened *before* deployment and by someone other than the author — so timestamps matter as much as the existence of each artefact.

Typically maps to: Security — changes are authorised, designed, tested and approved before implementation
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The complete population of production changes in the period, so the auditor can sample.
  • Per sampled change: the ticket, the pull request with reviewer and approval timestamp, the test or pipeline result, and the deployment record with its timestamp.

Where to get it

The population — The hardest part, because changes arrive through several paths. Deployment logs from the pipeline are usually the best single source. If changes can also be made outside the pipeline, that population has to be included too — and the auditor will ask how you know it is complete.

Git and code review — The PR page shows author, reviewer, approval time and merge time in one view. `git log --merges --since=... --pretty=format:'%h %ad %an %s'` gives the population; the PR link gives the detail.

Pipeline — The run record: what was built, which tests ran and passed, who triggered it, when it deployed, and to which environment.

Emergency changes — Handle these explicitly rather than hoping they are not sampled. Show the retrospective approval and that it followed your documented emergency process. An emergency path is acceptable; an undocumented one is not.

Why it gets sent back

  • Approval timestamped after deployment.
  • The author approving their own change.
  • A change in production with no corresponding ticket or PR.
  • A population that omits hotfixes or manual changes.

Related

/cicd-setup

Change management

Evidence that access to deploy to production is restricted to authorised personnel

#

What they are actually testing

Testing that the change-management control cannot be bypassed. If anyone can push directly to production, every other change control is advisory.

Typically maps to: Security — segregation of duties; restricted deployment capability
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Branch protection settings on production branches.
  • The list of who can approve and merge, and who can trigger a production deployment.
  • Cloud IAM showing who holds write access to production infrastructure.
  • Evidence that the pipeline's own credentials are not held by individuals.

Where to get it

Source control — Screenshot branch protection: required reviews, dismiss stale approvals, restrict who can push, and — the one that gets checked — whether administrators can override.

Pipeline — The environment protection rules and the list of approvers.

Cloud — `aws iam list-entities-for-policy` for the production deployment role, or the equivalent role assignment listing, showing who can assume it.

Standing access — If engineers hold permanent production credentials, expect scrutiny. Time-bound elevation with an approval trail is far easier to evidence than explaining why it is not needed.

Why it gets sent back

  • Branch protection with an admin override that several people hold.
  • A shared deployment account whose credentials are in a wiki.
  • A list of authorised deployers that does not match the actual permissions.

Related

/cicd-setup · /user-management-permissions

Change management

Evidence that prior code is maintained in a code repository for rollback capabilities and/or evidence of version control

#

What they are actually testing

This reads like the easiest request on the list and it contains two separate claims that people answer as one. Version control is the easy half: a repository exists, history is retained, changes are attributable. Screenshot it and move on. Rollback capability is the other half, and showing a git repository does not evidence it. Git proves the source is versioned. It says nothing about whether you can put the previous version of the running system back, which needs four things to be true at once: the prior build artefact still exists, the deployment mechanism can select it, its configuration and secrets still work, and the database will accept it. That last one is why rollbacks fail in practice. Code reverts in seconds. A migration that dropped a column does not, and a rollback procedure that does not mention the database is not a rollback procedure. If you address nothing else from this page, address that — an auditor with engineering background asks about it, and it is also simply true.

Typically maps to: Availability — changes can be reversed; Security — changes are tracked and the record of them is protected
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The repositories in scope, with history retained for the full period and beyond.
  • Branch protection showing history cannot be rewritten or deleted.
  • Artefact retention: evidence that prior builds, images or packages still exist for as far back as you would want to roll back.
  • The deployment mechanism's ability to select a previous version, demonstrated.
  • A documented rollback procedure that includes the database.
  • Evidence of an actual rollback performed during the period, if one occurred.
  • Infrastructure and configuration under version control too, not only application code.

Where to get it

Version control — the straightforward half — The repository list with history, the tagging or release convention (`git tag -l` showing releases across the period), and who can write. Add branch protection, screenshotted from the saved settings, and look specifically at two toggles: force push and branch deletion. If force push is permitted on the main branch, history can be rewritten, and every claim resting on that history weakens — including the audit trail for the change-management tests.

Do the prior artefacts still exist? — The question that quietly fails. Container registries and package repositories usually have lifecycle rules that delete old versions, and the defaults are often thirty days or the most recent handful. If you claim you can roll back six months and the registry keeps ten images, you cannot. Produce the retention policy — `aws ecr describe-lifecycle-policy --repository-name <name>` or the equivalent — alongside a listing showing what is actually still there, and make sure the two agree with your stated rollback window.

Show the rollback mechanism, do not assert it — Kubernetes: `kubectl rollout history deployment/<name>` lists the retained revisions and `kubectl rollout undo` is the mechanism — include the revision history limit, because that is the real depth. Argo CD: `argocd app history` and the rollback action. ECS: the task definition revisions. Traditional deployments: the previous release directory or package still present on the host. Whichever it is, the artefact is the history listing showing more than one version available.

The database, properly — State which approach you use, because they are genuinely different controls. Reversible migrations, where the tool has a down step and it is tested rather than merely written. Expand and contract, where schema changes are made backward compatible so the previous code keeps working and the cleanup happens in a later release — this is the approach that actually makes rollback safe, and worth saying so if you use it. Or point-in-time recovery as the fallback, in which case be honest that rollback means restoring and therefore means data loss between the restore point and now. Any of the three is a defensible answer. Silence is not.

Configuration and secrets — A previous build with the current configuration is not the previous version. If configuration is versioned alongside the code, say so and show it. If secrets rotated since, note whether the old build can still start — a rollback that fails on an expired credential is one nobody has tried.

Infrastructure, not only application code — Increasingly asked, and often the gap. Terraform, CloudFormation or configuration management in a repository is versioned infrastructure; resources created by clicking in a console are not, and cannot be rolled back at all. Where state is held remotely, show that state versioning is enabled. See /infrastructure-as-code.

A real rollback beats any amount of configuration — If you rolled back during the period, that single event is the strongest evidence available: the incident or change ticket, the deployment record showing the earlier version going out, and the timeline. It proves the capability instead of describing it. If you never have, consider rehearsing one in a non-production environment and keeping the record — the same reasoning as restore testing, and for the same reason.

Who guards the history — Two smaller points that come up. Who can delete a repository or rewrite history, which should be a short list. And whether the repository itself is backed up, if the hosting provider disappearing is within your risk appetite — some auditors ask, and for a code base that is also your disaster recovery position it is a fair question. See /backup-and-recovery.

Why it gets sent back

  • A screenshot of a repository, with nothing addressing production rollback.
  • Artefact retention shorter than the rollback window being claimed.
  • Force push or branch deletion permitted on the main branch.
  • A rollback procedure that does not mention the database.
  • A claimed capability with no evidence it has ever been exercised or tested.
  • Application code versioned while infrastructure is created by hand in a console.
  • Revision history limits set so low that only the current version is retained.

Related

/cicd-setup · /infrastructure-as-code · /backup-and-recovery · /incident-handling

Change management

For an example change made to a system as a result of a capacity constraint/issue, evidence the change was documented, authorized, tested, and approved

#

What they are actually testing

This is two tests wearing one request, and the category was not chosen at random. The change-management half is the familiar one: documented, authorised, tested, approved. The availability half is that capacity monitoring actually produces action — that somebody watched a number, saw it heading somewhere bad, and did something before it arrived. The reason the auditor picked a capacity change specifically is that these happen under time pressure. The disk is filling, the queue is backing up, the database is at its connection limit. That is exactly when process gets skipped, so it is a good place to test whether your process survives contact with urgency. In practice most organisations can produce the change and fail on the front of the chain: they resized something because it was slow, and there is no metric, no threshold, no alert and no ticket saying capacity. Keep the thread intact from the number to the change.

Typically maps to: Availability — capacity is monitored and acted on; Security — changes are authorised, tested and approved
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The capacity evidence that identified the constraint: the metric, the threshold or trend, and the alert or review that raised it, with its date.
  • The record documenting the issue and the proposed change.
  • Authorisation — permission to make the change.
  • Testing evidence, scaled to the kind of change, with what was verified.
  • Approval to release, and by whom — someone other than the implementer.
  • The implementation record with its timestamp.
  • Post-change verification that the constraint actually cleared: the same metric, after.

Where to get it

Finding a good example — Search your tickets for the vocabulary — disk, capacity, scale, resize, throttle, connection limit, out of memory — and cross-check against cloud resize or scaling events over the period. The output of a /capacity-trending review is the ideal starting point, because a trend review that produced a dated action gives you the front of the chain already written. Pick one where the whole chain exists rather than the most dramatic incident.

The capacity evidence itself — This is the half most often missing, so lead with it: the monitoring graph over a window wide enough to show the trend, with the threshold visible and the breach marked; the alert that fired, with its timestamp; or the trend projection that said when the resource would run out. A graph alone is weak — pair it with the alert or the review record, so there is evidence somebody was told rather than that the data existed.

Authorised and approved are two things — The request lists them separately and they are frequently conflated. Authorisation is permission to make the change at all; approval is sign-off that the change as built is fit to release. Many processes merge them into one step, which is fine — say so explicitly rather than presenting one artefact twice and hoping. What is not fine is the implementer being the only signature on either.

Testing a capacity change, which is genuinely awkward — How do you test adding disk? The honest answer is that testing scales to the change, and auditors accept that as long as you show something deliberate. The usual forms: the change applied in a non-production environment first; validation criteria defined before the change and checked after; a documented rollback plan where the change cannot be rehearsed. For a resize that requires downtime, the rehearsal and the timing measurement are the test. For a configuration change such as a connection pool size, a load test is. State which form you used and why — an unexplained blank in the testing column is the second most common failure here.

Cloud resize and scaling events — The implementation evidence is in the provider's audit trail: the modify or resize API call with its timestamp and the identity that made it. AWS CloudTrail, Azure activity log, GCP audit logs, OCI audit. This is stronger than a console screenshot because it records who, and it lets the auditor tie the change to the authorised person.

If the change was automatic, say so — it is a different answer — Autoscaling responding to load is not a change going through change control, and presenting one as your example invites an awkward conversation. The change subject to control is the scaling policy, so point at the change record for the policy, and use the scaling events as evidence that the policy works. Either answer is fine; conflating them is not. If autoscaling handles most of your capacity responses, pick a manual example for this request and mention the autoscaling separately.

Emergencies — If the example was urgent enough to bypass the normal route, do not hide it. Show that your documented emergency change process was followed, and produce the retrospective authorisation and approval with their dates. An emergency path used as designed is a working control. An undocumented one is the finding.

Closing the loop — Post-change verification is the step that gets left off, and it is cheap: the same metric after the change, showing the constraint cleared. It also answers a question the auditor may ask next — whether the change was sized correctly or merely delayed the problem by a month.

Why it gets sent back

  • A change record with no capacity evidence behind it — the change is proved, the trigger is not.
  • A capacity alert with no resulting change, presented as the example.
  • The testing field blank, with no explanation of how testing was scaled to the change.
  • The implementer as the sole authoriser or approver.
  • Approval timestamped after the change was implemented, with no emergency process behind it.
  • No post-change verification that the constraint cleared.
  • An autoscaling event offered as a controlled change, with no policy change record behind it.

Related

/capacity-trending · /performance-tuning-capacity-planning · /resource-monitoring · /workload-requirements

Change management

Configuration evidence of change detection mechanism (e.g. File integrity monitoring (FIM) or Code Repository configurations, including the notification settings and an example alert)

#

What they are actually testing

Read alongside the deployment alert request at /soc2-evidence/#deployment-alert, because they are two halves of the same idea and it is worth being clear which you are answering. That one detects changes that went out through your process. This one detects changes that did not — a modified binary, an edited configuration file, something dropped into a web root, a workflow quietly altered. Expected changes versus unexpected ones. The or in the request is offering two mechanisms for two architectures, and choosing the right one is most of the answer. File integrity monitoring suits long-lived servers where files persist and can be tampered with. Repository and pipeline controls suit immutable infrastructure, where nothing is edited in place and the meaningful change happens in source. Say which applies to you and why, rather than producing whichever tool you happen to own. One thing to be ready for, because auditors who have seen FIM before ask it: how many alerts did it produce, and what happened to them. FIM on a busy server is extremely noisy, and the two common outcomes are tuning it into silence or ignoring the output. Zero alerts across a year invites the first suspicion; fifty thousand untriaged alerts confirms the second.

Typically maps to: Security — unauthorised or unexpected changes to system components are detected
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Which mechanism you use and the architectural reason for it.
  • The configuration: what is monitored, how the baseline is established, and whether checking is scheduled or continuous.
  • The notification settings: what triggers an alert, who receives it, through which channel.
  • An example alert from inside the period, with what was done about it.
  • Alert volume over the period, and the triage evidence — the answer to the question above.
  • Exclusions and tuning, with the justification for each.
  • Coverage: which in-scope systems are monitored, and any that are not, with the reason.

Where to get it

File integrity monitoring — the configuration — For AIDE, the rule set in `/etc/aide.conf` plus the timer or cron entry that runs `aide --check`, and the report destination. For Wazuh or OSSEC, the syscheck block showing the monitored directories, whether real-time is enabled, and the alert level. Commercial and cloud-native options have equivalent configuration screens. Whichever it is, the evidence is the saved configuration and a run from inside the period — a rule set with no check history is the same failure as any other configured-but-never-ran control.

What should be monitored — Auditors look at the path list, so make it defensible: system binaries and libraries, configuration directories, web document roots, scheduled task directories, service unit files, and authentication material such as authorised keys files. These are the locations an intruder modifies. A path list that monitors an application directory and nothing else is thin.

The baseline, and who can move it — The overlooked control. FIM compares against a baseline, so whoever can re-baseline can silence it — make a change, re-initialise, and the difference disappears. Show how the baseline is updated: that it happens after an approved change, that it is logged, and that the ability is restricted. Without this, the whole mechanism is advisory.

Noise, and the honest answer to it — Log files, temporary directories and package-manager activity generate constant changes and legitimately need excluding. Produce the exclusion list with a reason per entry, and make sure it has not grown until the monitored set is trivial — excluding an entire configuration directory because updates were noisy is a finding. The better pattern, where the tool supports it, is to correlate: suppress changes that coincide with an approved package update or a deployment, and alert on everything else. If you do that, say so, because it directly answers the volume question.

Immutable infrastructure — the stronger answer — Where containers are replaced rather than patched, FIM is largely the wrong tool and there is a better claim available. A read-only root filesystem makes the change impossible rather than detectable; a runtime security tool alerting on writes to binary paths detects what remains; and a GitOps controller reporting a deployment as out of sync with its manifest is change detection at the level that matters. If this is your architecture, lead with it and explain why FIM would add little — that reads far better than a token FIM agent installed to satisfy a checklist.

Code repository configurations — The alternative the request names, and it means more than protected branches. Alerts worth configuring and evidencing: direct pushes to a protected branch, changes to the branch protection rules themselves, new deploy keys or access tokens, and changes to CI workflow files. That last one is the highest value in the list, because a modified pipeline definition is a change to the mechanism every other change control depends on, and it is not code anyone reviews with the same care. Commit signing, where you require it, belongs here too.

Infrastructure and cloud drift — Configuration change detection at the infrastructure layer: cloud configuration rules alerting on a resource moving out of compliance, or a scheduled `terraform plan -detailed-exitcode` reporting drift from the committed state. The output of either is good evidence, and the scheduled plan has the advantage of producing a dated history. See /infrastructure-as-code.

The example alert — From the period, with the triage attached: what fired, who looked at it, what it turned out to be, and how it was closed. A benign example is fine — most are — provided the record shows somebody determined that rather than assuming it. An alert produced by deliberately touching a file the week before the audit is visibly that, and it answers the configuration question while undermining the volume one.

Why it gets sent back

  • A configured tool with no check history and no alerts across the period.
  • A large alert volume with no evidence any of it was triaged.
  • Exclusions broad enough that little of consequence is monitored.
  • An uncontrolled baseline, where anyone able to make a change can also re-baseline.
  • Monitoring on some in-scope systems, with the remainder unmentioned.
  • Immutable infrastructure claimed while long-lived servers exist unmonitored.
  • The repository route offered while force push is permitted, or with no alert on changes to the protection rules themselves.
  • An example alert visibly manufactured shortly before the audit.

Related

/log-aggregation · /os-installation-and-hardening · /infrastructure-as-code · /resource-monitoring

Change management

Screenshot of the change control software settings showing that an alert is triggered when a change is implemented (i.e. pushed or promoted) to the production environment (with an example alert)

#

What they are actually testing

Every other change-management request tests a preventive control: approvals, reviews, restricted deployment rights. This one tests the detective control sitting behind them. The auditor is asking what happens if the preventive controls are bypassed — would anyone find out? An alert on every production change is the answer, because an unauthorised deployment then announces itself. Which leads to the question the request does not spell out but the auditor will ask next: who receives the alert, and would they notice an unexpected one? A notification landing in a channel that fires forty times a day is not a detective control, because nobody is going to spot the one that should not be there. Be ready to answer that, and if the honest answer is that the channel is noisy, say what compensates — a separate alert for deployments outside the pipeline is the usual and much stronger answer. The two halves have to match. A configuration screenshot with no example proves nothing fired; an example with no configuration proves nothing is systematic. Its counterpart, covering detection of changes that did not go through the process at all, is at /soc2-evidence/#change-detection.

Typically maps to: Security — changes to the system are detected and monitored
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • A screenshot of the notification configuration itself — the rule, trigger and destination — in whatever tool implements it.
  • Who receives it: the channel, distribution list or on-call route, and how many people that is.
  • A real example alert from inside the observation period, with its timestamp visible.
  • The change record or deployment that the example alert corresponds to, so the two can be tied together.
  • Coverage evidence: that every route to production raises an alert, not only the pipeline.
  • What happens when an unexpected alert appears — the process, and ideally an instance of it being followed.

Where to get it

Identify every path to production first — Before screenshotting anything, list the ways a change can reach production: the pipeline, a manual deployment, a direct database change, an infrastructure apply run from a laptop, a configuration flag flipped in a console, a container image repointed. The control is only as good as its coverage, and an alert that fires reliably for the pipeline while a manual path stays silent has a hole exactly where the risk is. If some paths cannot alert, close them instead — see /soc2-evidence/#prod-access-restriction.

CI/CD pipelines — The notification step or integration settings: in GitHub Actions the workflow step or the environment configuration, in GitLab the integration settings, in Jenkins the post-build notifier, in Azure DevOps the service hook. Screenshot the configuration as it is saved, not the YAML in an editor — the saved state is what runs. Include the environment protection rules in the same shot where the tool shows them together, since it links this control to the approval one.

GitOps controllers — Argo CD and Flux both have notification controllers with sync and health triggers. The configuration object is the evidence, and it has the advantage of being in version control already — show the manifest and the resulting message. Worth noting that a GitOps controller also detects drift, which is a stronger claim than deployment alerting alone and is worth mentioning if you have it.

Change management software — Where the request says change control software it often means exactly that: ServiceNow, Jira Service Management or similar. Screenshot the notification rule on the change record reaching implemented or closed, with its recipients. If the tool is the system of record but the deployment happens elsewhere, show both, and show how they are linked — a deployment that never updates the change record is a gap the auditor will find by sampling.

The backstop that catches the bypass — The strongest version of this control does not watch the pipeline at all. It watches the target: an alert on any write to production infrastructure whose identity is not the pipeline. Cloud audit trail rules, Kubernetes audit log alerting, or a database trigger on schema change. This fires precisely when somebody goes around the process, which is the scenario the whole request exists for. If you have it, lead with it — it answers the coverage question before it is asked. See /log-aggregation.

Capturing the example alert — Take it from the period, not from a test you ran this morning for the auditor — they can see the date, and a freshly manufactured example suggests there were no real ones. The screenshot needs the channel or recipient visible, the timestamp, and enough of the content to identify what was deployed. Then attach the matching change ticket and pipeline run so the chain is complete.

Redaction — Chat screenshots leak more than people expect: unrelated messages above and below, internal hostnames, ticket titles, other people's names. Crop to the alert and one line of context, mask what is genuinely sensitive, and leave the timestamp, channel name and deployment identifier visible — those are the parts being tested.

Why it gets sent back

  • A configuration screenshot with no example alert, or an example with no configuration.
  • An example alert dated after the observation period, or visibly generated for the audit.
  • An alert that covers pipeline deployments while manual or out-of-band paths exist and stay silent.
  • A destination nobody monitors — an unused channel, or a mailbox with no owner.
  • An example alert that cannot be tied to a change record or a deployment run.
  • A chat screenshot with the timestamp or channel cropped out.
  • No answer to who would notice an unexpected alert among the routine ones.

Related

/cicd-setup · /log-aggregation · /resource-monitoring

Vulnerability management

Vulnerability scan results and evidence of remediation within defined timeframes

#

What they are actually testing

Two things: that scanning happens at your stated frequency across the whole estate, and that findings are closed within your own stated SLA. As with offboarding, you are measured against your policy — a policy promising 7 days for criticals when you achieve 30 creates exceptions that a 30-day policy would not.

Typically maps to: Security — vulnerabilities are identified, evaluated and remediated
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Scan reports across the period at the stated frequency, showing coverage.
  • The findings register with severity, discovery date, and closure date.
  • Remediation tickets for a sample.
  • Documented, approved risk acceptances for anything open past its SLA.

Where to get it

Scanners — Export the scan history showing dates and target counts — coverage is tested as well as findings. A scan of 40 hosts when the estate has 60 is a gap.

Container and dependency scanning — Pipeline scan output per build, plus how the gate behaves on a finding.

Cloud posture — Security Hub, Defender for Cloud or SCC findings with their timeline.

Proving the timeline — The strongest artefact is a report with discovery date, severity and closure date in one row per finding — it demonstrates the SLA directly rather than requiring the auditor to cross-reference.

Why it gets sent back

  • A single recent scan for a twelve-month period.
  • Scans covering part of the estate.
  • Open criticals past SLA with no risk acceptance.
  • A clean report with no evidence of what was scanned.

Related

/security-patch-management

Vulnerability management

Evidence that systems are patched in line with policy

#

What they are actually testing

That patching is a running process rather than an occasional effort, and that it covers the whole in-scope estate including anything outside the automation.

Typically maps to: Security — system components are maintained and updated
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Patch status per host across the period.
  • Configuration showing automatic security updates enabled, where claimed.
  • The reboot record for kernel updates.
  • Exceptions with their justification.

Where to get it

Linux — `grep -E 'Unattended-Upgrade|APT::Periodic' -r /etc/apt/apt.conf.d/` or `systemctl is-enabled dnf-automatic.timer`, plus `/var/log/dpkg.log` or `dnf history` as proof updates actually applied. `uptime` alongside it — a kernel patched and never booted is not applied.

Fleet-wide — Patch manager or configuration management compliance report, which covers population and status in one artefact.

Containers — Base image rebuild cadence and the registry scan history.

Why it gets sent back

  • Configuration showing automation is enabled, with no evidence it ran.
  • Hosts absent from the report that appear in the asset list.
  • Long uptimes on hosts whose kernel was patched.

Related

/security-patch-management · /scheduled-maintenance

Vulnerability management

Patch update configurations and evidence of an example system patching run

#

What they are actually testing

Two requests joined by an “and”, testing different things, and the join is the point. The configuration half tests that patching is systematic — that a mechanism exists, is enabled, and is configured the same way across a class of systems rather than depending on someone remembering. The example-run half is a walkthrough: one system, one patch cycle, followed from the patch becoming available through to installed and verified. What the auditor is really checking is that the two agree. The classic failure is producing an unattended-upgrades configuration and then illustrating it with a screenshot of somebody connecting over SSH and running an upgrade by hand. That pair does not evidence the control — it demonstrates that the documented mechanism is not the one in use, which is a worse outcome than either artefact on its own. Pick an example that the configuration actually produced.

Typically maps to: Security — system components are updated; changes are authorised and applied
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The patch policy: what gets patched, how quickly by severity, and who decides. You are measured against this, so it should describe what you do.
  • The patch configuration for each system class in scope — not just the Linux one.
  • Where patches come from: the repositories or update sources, and whether signatures are verified.
  • For the example run: the full chain, from the patch becoming available to the post-patch verification, with timestamps at each step.
  • Which class the example represents, and how the other classes are patched — so one example is not mistaken for full coverage.

Where to get it

Pick the example deliberately, and honestly — A representative in-scope production system, not the cleanest lab box and not something out of scope. If the auditor picks instead, any host should survive it, which is the real test. Where classes patch differently — Linux automatically, Windows through an update service, network devices by hand, containers by rebuild — one example covers one class. Offer one per class rather than waiting to be asked for the rest.

What a complete example chain looks like — Seven links, and the last two are the ones usually missing: (1) the advisory or scan finding that identified the patch, with the date it became available; (2) the ticket or the automated policy that covers it; (3) the change record, if your process requires one; (4) the installation log with its timestamp; (5) the reboot, if the patch needed one, with its timestamp; (6) post-patch verification — the version now installed, the service healthy, the finding gone from the next scan; (7) the elapsed time from availability to installed, set against your stated SLA. Without (1) there is no clock, and without (7) nobody can tell whether you met your own commitment.

Linux — configuration — Debian and Ubuntu: `cat /etc/apt/apt.conf.d/20auto-upgrades` for whether it is on, and `/etc/apt/apt.conf.d/50unattended-upgrades` for what it will install — show that it covers security updates specifically and note whether it is set to reboot. RHEL family: `systemctl is-enabled --now dnf-automatic.timer` and `/etc/dnf/automatic.conf`, where the `apply_updates` setting is the one that matters, since the default only downloads.

Linux — the example run — On RHEL family, `dnf history` then `dnf history info <id>` is close to an ideal single artefact: it gives the transaction date, the command that ran, the user, and every package with its before and after version. On Debian family, `/var/log/unattended-upgrades/unattended-upgrades.log` for the automated run plus `/var/log/dpkg.log` for the package detail. Add the reboot evidence: `uptime` alongside `needs-restarting -r` on RHEL, or whether `/var/run/reboot-required` is present on Debian — a kernel patched and never booted is not applied, and an auditor who knows the platform checks this.

Windows — The update service configuration — rings or groups, deferral periods, deadline settings — and the deployment report showing what went to which group and when. For the single machine, its update history and `Get-HotFix` output, plus the restart record.

Containers and images — Patching a container means rebuilding it, and this is where answers most often fall apart. The configuration is your base image update and rebuild cadence; the example run is one cycle: the base image changed, the pipeline rebuilt, the registry digest changed, the deployment rolled out, and the image scan is now clean. A long-running container that has been patched in place is a finding of its own, because the next deployment silently reverts it.

Network devices — Firmware version against the vendor's current advisory, the change ticket, the maintenance window record and the post-change version check. Usually manual, which is fine — it just has to be evidenced as a process rather than as an event.

Managed cloud services — The provider patches the underlying platform, which does not remove it from the answer. Evidence the maintenance window configuration, the engine version history, and your own process for deciding when to take a major version. State the responsibility split explicitly rather than leaving the service out of the list.

Where the patches come from — A question that gets asked more often than it used to. Which repositories or update sources are configured, whether you mirror them, and that package signatures are verified. If you pull from a mirror you control, show how it is kept current — a stale internal mirror is a patching failure that every configuration screenshot will look fine through.

Why it gets sent back

  • A configuration showing automation is enabled, with no log proving it ever ran.
  • An example of a manual run while the documented control is automated — the two contradicting each other is worse than either alone.
  • An example from a development or out-of-scope system.
  • No post-patch verification: no version check, no confirmation the finding cleared.
  • A kernel patch with no reboot evidence, or a long uptime on a host whose kernel was updated.
  • No date for when the patch became available, so the SLA cannot be assessed.
  • One Linux example presented as covering an estate that also has Windows, network devices and containers.

Related

/security-patch-management · /scheduled-maintenance · /os-installation-and-hardening

Backup and recovery

Evidence that backups are performed and monitored, including failure handling

#

What they are actually testing

Not that backups are configured — that they ran, that somebody would know if one did not, and that failures were acted on. A period with zero recorded failures often means nobody is watching rather than that nothing failed, and auditors read it that way.

Typically maps to: Availability — data is backed up and recoverable
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Backup job history across the period, showing successes and failures.
  • The alerting configuration for failures.
  • For a sampled failure, the ticket and its resolution.
  • The retention configuration, against your stated policy.

Where to get it

Job history — Export from the backup tool or cloud backup service covering the full period, not a screenshot of this week.

Home-grown scripts — The log plus the cron or timer definition, plus how a failure raises an alert. A script that fails silently is the finding, not the evidence.

Snapshots — The snapshot listing with timestamps and the lifecycle policy.

Failure handling — If nothing failed, say so and show the alerting would have fired — a test alert, or a failure from outside the period.

Why it gets sent back

  • A screenshot of a backup dashboard showing 'last run: success'.
  • No failures and no evidence that failures are detectable.
  • A failure with no ticket.

Related

/backup-and-recovery · /scheduled-maintenance

Backup and recovery

Backup notifications or backup log showing backup completions for the defined frequency

#

What they are actually testing

Narrower than the general backup request, and the difference is the last four words. This one is a completeness test: your policy states a frequency, and the auditor wants a record covering every expected run across the whole period. They are looking for gaps — the fortnight in March with nothing — not for proof that backups are possible. The word “or” in the request hides the most important decision. Notifications and a log answer different questions, and one of them has a hole in it. A notification proves somebody was told that a run finished. A log proves the run happened. If the job never starts at all — the timer was disabled, the host was down, the credential expired — no notification is sent, and the absence of a notification looks exactly like the absence of a problem. So lead with the job history, which is a positive record of each run, and use notifications as supporting evidence. Then show the thing that closes the hole: an alert that fires when an expected run does not appear.

Typically maps to: Availability — backups are performed at the defined frequency
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The job history for the full period, one entry per scheduled run, with job name, target, timestamp and outcome.
  • The schedule definition and the policy stating the frequency, so expected and actual can be compared.
  • A reconciliation of expected runs against actual runs, with any missing dates explained. This is the artefact that actually answers the request.
  • The notification configuration: who is told, and on success, failure or both.
  • Evidence that a run which never started would be detected — the part that notifications alone cannot give you.

Where to get it

Build the reconciliation, do not hand over raw logs — The request is about frequency, so present it that way: a table or calendar with one row per expected run over the period, and the actual outcome beside it. Twelve months of daily backups is 365 rows, which sounds like a lot and is a two-line script against the job history. Handing an auditor a raw log and letting them count is how you get a follow-up asking about a gap you could have explained up front.

Backup products — Export the job history rather than screenshotting a dashboard — dashboards show the latest state, and the request is about the whole period. Most products have a job report covering a date range; take the full period in one export so the date boundaries are visible.

Repository-based tools (restic, Borg, Kopia) — The snapshot list is the log, and it is better evidence than any notification because it is a property of the repository rather than of the job that wrote to it: `restic snapshots --json` or `borg list <repo> --json`. Each snapshot carries its own timestamp and host, so the gaps are visible directly.

Cloud backup services — AWS: `aws backup list-backup-jobs --by-created-after <date>` returns the job history with state per job, and paginates — collect all pages or the export silently truncates. Azure Backup and Google Cloud have equivalent job listings. For snapshot lifecycle policies, list the snapshots themselves with their creation timestamps, which is the same reasoning as the repository tools.

Databases — pgBackRest: `pgbackrest info --output=json` gives every backup with its type and timestamps, and is the cleanest single artefact for PostgreSQL. For plain dumps on a schedule, the file listing with timestamps and sizes — include sizes, because a dump that succeeded at 40 bytes is a failure the exit code did not report. Where you use continuous archiving, show the WAL archive is unbroken as well as that base backups ran.

Home-grown scripts on cron or timers — The common case, and the one that needs the most care. For systemd: `systemctl list-timers backup*` shows the schedule with last and next run, and `journalctl -u <unit> --since '<period start>'` gives the history — but check the journal retention first, because a default configuration may not hold twelve months and you will discover that while assembling evidence. For cron, the script's own log file, provided it writes one line per run including failures. A script that only logs on success cannot demonstrate frequency.

Kubernetes — `velero backup get` for the schedule and history, or the equivalent for whatever operator you run. Include the schedule object itself, not just the backups.

Detecting a run that never happened — The part most submissions miss. Options that satisfy it: a monitoring check on the age of the most recent backup, alerting when it exceeds the interval; a dead-man's-switch service the job pings on completion, which alerts on silence; or an alert on the absence of the expected log line. Show the configuration and, if you have one, an instance of it having fired — that single artefact answers the whole objection.

Why it gets sent back

  • A dashboard screenshot showing “last backup: success”.
  • A month of history for a twelve-month period.
  • Missing dates with no explanation — an explained gap is a minor point, an unexplained one is a finding.
  • Notification emails only, with no way to show a run that never started would be noticed.
  • A log that does not identify which job or which target, so coverage cannot be assessed.
  • Journal or log retention shorter than the period, discovered at evidence time.
  • An unbroken run of successes over the whole period with no evidence that a failure would have been visible — it usually means failures do not notify.

Related

/backup-and-recovery · /scheduled-maintenance · /resource-monitoring

Backup and recovery

Provide an example backup log for each frequency (monthly and incremental)

#

What they are actually testing

Read literally this is easy: two log files, one of each kind, and it is answered. Read as the auditor means it, there is a second question underneath, and it is the one worth preparing for.

A monthly full and a set of incrementals are not two independent backups. They are one chain. An incremental is worthless without the full it was built on and every incremental between the two, so what actually has to hold is that the chain is unbroken — and a single missing link silently invalidates everything after it until the next full. Producing one log of each kind demonstrates that both schedules run. It does not demonstrate that anything can be restored.

So answer the question asked, then answer the one behind it: show the chain, and show that a restore from the incremental end of it has been done.

Typically maps to: Availability — backups are performed at each defined frequency and are recoverable
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • An example log for each frequency, from inside the period, identifying the job and what it covered.
  • The schedule definition, so the examples can be checked against the stated frequencies rather than taken on trust.
  • The chain for one full cycle: the full, then every incremental that depends on it, in order, with no gaps.
  • Retention settings for each tier, and evidence a full is never pruned before the incrementals that need it.
  • A restore performed from the incremental end of a chain, not from the full.
  • Where your tool does not work in fulls and incrementals, an explanation of what it does instead.

Where to get it

Show the chain, not two files — The strongest single artefact is a listing of one complete cycle: the monthly full with its date, then each incremental beneath it in order, with sizes. A reader can see at a glance that nothing is missing. Two unrelated log excerpts prove both jobs exist and nothing about whether they compose.

Retention is where this usually breaks — If the full is retained for 30 days and the incrementals for 90, then after day 31 you hold 60 days of incrementals that can restore nothing, because their base is gone. This is a common misconfiguration, it looks healthy in every dashboard, and it is exactly what a chain listing exposes. Produce both retention settings side by side and show the dependency is respected.

Classic backup products — Export the job history with the backup type column included — full, incremental, differential — so the pattern is visible rather than asserted. Most products can also show the restore points with their dependencies, which is the chain artefact in its clearest form.

PostgreSQL — The equivalent tiers are a base backup and continuous WAL archiving. `pgbackrest info --output=json` gives every backup with its type and, crucially, its parent — the dependency made explicit. Show the WAL archive is unbroken as well, since a gap there is the same failure as a missing incremental.

MySQL and MariaDB — A periodic full plus binary logs. Evidence the binlogs are retained far enough back to reach the oldest full you claim to be able to restore from, and that `log_bin` has not been off for any stretch of the period.

Snapshots — Cloud volume snapshots are incremental in storage but each one restores standalone, so the chain problem does not arise in the same way — say so, rather than inventing a full and an incremental to fit the question. Produce the snapshot listing with timestamps and the lifecycle policy.

Deduplicating tools (restic, Borg, Kopia) — These have no fulls and incrementals either: every snapshot is logically complete and physically deduplicated against what came before. The honest answer explains that and shows the snapshot list plus the repository integrity check — `restic check` or `borg check` — which is the closest equivalent to proving the chain holds. An auditor who understands the tool accepts this readily; one working from a template may need the sentence of explanation, so write it.

ZFS and filesystem replication — A full send followed by incremental sends between snapshots. The receiving side's snapshot list is the chain, and a missing intermediate snapshot makes every later incremental unreceivable — which is at least a loud failure rather than a quiet one.

The restore that settles it — Restore from the most recent incremental in a chain, not from the full. That one exercise proves the full is intact, every incremental in between applied, and the tooling can walk the dependency. Record the date, what was restored, how it was validated and how long it took — see /soc2-evidence/#restore-test.

Why it gets sent back

  • One log of each kind, with nothing showing how they relate.
  • Incrementals whose base full has already been pruned.
  • Retention periods that make the longer-kept tier unrestorable.
  • Frequencies in the logs that do not match the stated schedule.
  • A gap in the chain with no explanation — the point at which everything after it stops being restorable.
  • Only ever restoring from fulls, so the incremental path has never been proven.
  • Forcing a deduplicating or snapshot-based tool into full-and-incremental language instead of explaining what it actually does.

Related

/backup-and-recovery · /scheduled-maintenance · /disaster-recovery-planning

Backup and recovery

Backup replication configurations and an example backup replication completion log and/or invoices or receipts from offsite backup vendor showing proof of backup rotations for the defined frequency

#

What they are actually testing

Everything else about backups tests whether they run and whether they restore. This one tests whether a copy ever leaves. A backup on the same host, the same array or the same building as the primary shares its failure: the fire, the flood, the array controller, the compromised hypervisor. Replication is what turns a copy into a backup.

Note what the and/or is offering. A replication log is your own system telling you it worked. A vendor invoice is a third party, with a commercial interest in being accurate, confirming a pickup happened on a date. The second is independent evidence and auditors weigh it accordingly, so if you have physical rotation, produce the invoices even though the internal records exist.

The trap is subtler than the usual ones. Replication that works perfectly will faithfully copy a deletion, and faithfully copy files a ransomware process has just encrypted. A replica is not a second chance at a mistake unless something on the far end keeps versions you cannot overwrite. Its sibling, covering replication of the live data rather than of the backups, is at /soc2-evidence/#offsite-data-replication.

Typically maps to: Availability — backup copies are maintained in a separate location at the defined frequency
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The replication configuration: source, target, what is included, and the schedule.
  • Where the target is, stated as a failure domain rather than a hostname — another region, another building, another provider.
  • Completion logs across the period, showing the replication finished rather than started.
  • Current replication lag, set against the recovery point objective it is supposed to meet.
  • Immutability or versioning on the target, and who can switch it off.
  • For physical rotation: the vendor's invoices or pickup receipts for the period, with dates that match the stated frequency.

Where to get it

Say where the copy is, in failure-domain terms — A target named only by hostname answers nothing. Another availability zone in the same region survives a rack and not a region; another building on the same power feed survives a fire and not a grid failure; another provider survives an account compromise that the same provider would not. State which separations you have, and be straightforward about the ones you do not — an acknowledged limit reads far better than a target described vaguely enough to seem further away than it is.

Completed, not started — Export the replication job history with the completion status and the bytes transferred, over the full period. A job that begins nightly and times out at 40% every night looks busy in a dashboard and has replicated nothing usable. The byte count is what distinguishes the two.

Measure the lag, do not assume it — If the recovery point objective is 24 hours and the replica is four days behind, the configuration does not meet the objective regardless of what the schedule says. Produce the timestamp of the newest restore point at the target alongside the objective. This is where an under-provisioned link shows itself: replication that cannot keep up does not fail, it falls behind quietly.

Bandwidth is a real constraint, and worth stating as one — On our own infrastructure the secondary host's uplink sustains about 0.4 MB/s, which decided what is replicated rather than the other way round: application state crosses nightly, container images are rebuilt at the far end from source instead of shipped, and one 436 MB archive was seeded directly on the first pass rather than pushed through a nightly window. Documenting that reasoning is better evidence than a configuration implying everything is replicated when the link could not carry it.

Cloud replication — Cross-region bucket replication, geo-redundant storage, or a provider's cross-region backup copy. Produce the replication rule or policy, the replication metrics showing objects actually replicated, and the destination region. For object storage, show the destination's versioning and object-lock settings in the same breath — that is the half that protects you from the deletion being replicated too.

Backup product copy jobs — The secondary copy job configuration and its history. Most products distinguish a backup job from a copy job, and it is the copy job this request is about; producing the primary job's history by mistake is a common slip.

Scripted replication — rsync to a remote repository, `restic copy`, `borg` to a second repo. Show the unit or cron entry, the log with completion and transfer sizes, and how a failure raises an alert. Also show that the remote repository is checked periodically for integrity — replication writing into a corrupt repository is replication producing nothing.

Do not offer database replication as backup — A streaming replica or a cluster member is high availability, not a backup. It applies a DROP TABLE within the second. Where you have both, say which is which — presenting a hot standby as the offsite backup copy is an error an auditor who knows databases will pick up immediately.

Physical media and the vendor's paperwork — Request the pickup or rotation records for the period from the vendor, dated, identifying your account. Count them against your stated frequency: four pickups where the policy says weekly is a finding regardless of what the internal log says. Where chain-of-custody receipts exist, include them — they evidence the media got where it was going, not only that it left.

Why it gets sent back

  • Configuration with no completion history.
  • Jobs recorded as run with no bytes transferred.
  • Replication lag exceeding the recovery point objective it is meant to satisfy.
  • A target in the same failure domain as the source, or described vaguely enough to hide that it is.
  • No versioning or immutability at the target, so a deletion or an encryption replicates faithfully.
  • Vendor invoices showing fewer rotations than the stated frequency, or none for part of the period.
  • Database replication offered as the offsite backup.

Related

/backup-and-recovery · /disaster-recovery-planning · /data-gravity

Backup and recovery

Offsite data replication configuration settings

#

What they are actually testing

Read this next to the backup replication request at /soc2-evidence/#backup-replication, because they sound alike and are not. That one is about copies of backups reaching somewhere else. This one is about the live data itself being continuously mirrored, so that a second site can take over.

The difference matters when you answer, because replication protects against exactly one thing: losing a site. It does nothing about a mistake. A deletion replicates in seconds; so does a bad migration, so does an encryption. Offering this as the backup story is the single most common error in this area, and an auditor who notices will ask the backup question again, with more interest. Keep the two answers separate and say which is which.

Note also that the request asks only for configuration. That is a narrower question than the others here, and the temptation is to answer it narrowly. The settings alone cannot show that the far end works — so produce them, and then produce the one thing that proves they are more than intent: a failover that was actually performed.

Typically maps to: Availability — data is replicated to a secondary location to meet recovery objectives
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • What is in scope and what is not: which datasets, databases and volumes replicate, and which deliberately do not.
  • The target, as a failure domain — another region, another provider, another building.
  • Synchronous or asynchronous, and the recovery point that mode implies.
  • Measured lag, against the stated recovery point objective.
  • Encryption in transit and at the destination.
  • Who can change or disable the replication, and whether that change is logged.
  • Evidence of a failover to the replica — the artefact that distinguishes a configured replica from a working one.

Where to get it

Synchronous or asynchronous is the first thing to state — Synchronous means a write is not acknowledged until the far end has it: zero data loss, at the cost of every write paying the round trip, and a stalled secondary can stall the primary. Asynchronous means bounded loss equal to the lag. Neither is wrong; answering without saying which is, because the recovery point objective follows directly from the choice.

Say what is NOT replicated, and why — This reads as a stronger answer than a list of what is, because it shows the scope was decided rather than assumed. On our own infrastructure the secondary host's uplink sustains about 0.4 MB/s, so application state crosses nightly while 16 GB of container images do not — they are rebuilt at the far end from source instead. The recorded reasoning is better evidence than a configuration implying everything replicates over a link that could not carry it. See /data-gravity.

Databases — Streaming replication, log shipping or a managed cross-region replica. Produce the configuration and the current lag from the replica's own view — for PostgreSQL, the replay lag rather than the connection state, because a connected replica can still be far behind. State explicitly that this is availability and not backup.

Object and block storage — Cross-region replication rules, geo-redundant settings, or volume replication. Include the destination region, whether replication is of new objects only or also existing ones, and the metrics showing objects actually arriving rather than merely being eligible.

File and application data — Scheduled rsync, a storage appliance's own replication, or a filesystem-level send. Show the schedule, the completion history and the alerting on failure. A replication that stops silently is the usual failure, and the evidence that it would be noticed is as important as the configuration.

The far end needs more than the bytes — Data at a second site with nothing able to serve it is not a recovery position. Show that the secondary has the software, the configuration and the credentials to actually use the data — or, where it is rebuilt on demand, how and from what. This is routinely missing from otherwise careful replication answers.

How traffic moves, and what stops both sites serving at once — Describe the mechanism and its fencing. Ours hands a single WireGuard AllowedIPs entry from the primary to the standby: because an address belongs to exactly one peer, handing it over takes it away in the same operation, and the old primary is left holding an address nothing routes to. Two hosts both believing they are authoritative is the failure a reviewer will probe for. See /disaster-recovery-planning.

The failover that proves it — Configuration shows intent. A performed failover shows capability. Produce the record: the date, who ran it, how long the switch took, what was verified at the far end, and what was learned. A replica never promoted is a hypothesis, and this is the one artefact here that cannot be produced from settings.

Why it gets sent back

  • Replication offered as the answer to the backup question.
  • Synchronous or asynchronous not stated, so the recovery point cannot be derived.
  • Lag unmeasured, or exceeding the objective it is meant to meet.
  • A target in the same failure domain as the source.
  • Data replicated with nothing at the far end able to serve it.
  • No evidence a failover has ever been performed or rehearsed.
  • No answer to what prevents both sites serving at once.

Related

/disaster-recovery-planning · /data-gravity · /scalability-path · /backup-and-recovery

Backup and recovery

System generated listing of users with access to backup media and/or vendor provided listing of users with the ability to recall backup media from third party storage facility

#

What they are actually testing

The reason this is asked separately from the general access listing is worth understanding, because it changes how carefully you answer. Production access controls protect the live system. A backup is the same data with none of those controls in front of it — no application permissions, no row-level checks, no audit trail on reads. Whoever can reach the backups has a complete copy of everything, and can usually take it away quietly. So the auditor wants this population examined on its own. Two phrases carry weight. “System generated” means the system's own export, not a spreadsheet somebody maintains — a maintained list is a claim about access, an export is evidence of it. “Vendor provided” means the list has to come from the storage vendor's own records, not from your copy of what you think they hold. And the “and/or” means answer both halves, or state plainly which one does not apply to you.

Typically maps to: Security — logical and physical access to backup media is restricted; Confidentiality
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • A system-generated export of everyone with access to each backup system: the backup application, the repository or storage holding the backups, and the tape library if you have one.
  • The vendor's own authorisation list — commonly called an authorised caller list — of who may request media back, obtained from the vendor and dated inside the period.
  • Who holds the backup encryption keys, since a key holder plus the media is the same exposure as direct access.
  • A reconciliation of every list above against current employees, with any leaver explained.
  • Evidence both lists were reviewed during the period, at your stated frequency.
  • An explicit statement where a half does not apply — for example that no physical media leaves the environment.

Where to get it

Backup application — Export the users and their roles from the backup product's own console rather than describing them. Include the role, not only the name: read access and the ability to delete backups or shorten retention are different exposures, and the second is the one that matters during a ransomware incident.

Backups in cloud object storage — The population is whoever can reach the bucket or container, which is usually wider than whoever uses the backup tool. Export the IAM policies, bucket policy and any cross-account access, and resolve them to actual identities rather than handing over the policy documents — an auditor asked for a list of users, not a list of rules. Cover three verbs separately: read, delete, and the ability to change lifecycle or retention.

Immutability, and who can override it — If you use object lock, legal hold or an immutable repository, show who can release it. That is a small and very privileged population, and if the answer is “the same administrators who manage the backups” then the immutability is weaker than it looks. Worth stating honestly with whatever compensates — an auditor who knows the subject will ask.

Repository tools (restic, Borg, Kopia) — Access is the repository credential or passphrase, so the real question is who holds it and where. This is frequently a single shared secret in a vault, which is defensible only if you can show the vault's access list and its audit log. Produce both — the vault listing is the system-generated export for this case.

Tape libraries and on-site media — Two populations, not one: who can operate the library or eject media, and who can physically reach it. The second comes from the badge or door access system as an export, and from the safe or cabinet key register if one exists. Do not forget cleaners, contractors and anyone on a permanent all-doors badge — that is exactly who this test finds.

The offsite storage vendor — Ask your account manager or the vendor portal for the current authorisation list in writing, and make sure the document is dated and identifies your account. Your own copy of it satisfies nothing, because the whole point is what the vendor will act on when somebody rings them. Ask at the same time for the recall history over the period, which several auditors want alongside the list: it shows who actually requested media, not just who could.

The list that rots — The authorised caller list is the single most commonly stale artefact in this whole area. Nobody tells the storage vendor when an employee leaves, so people remain authorised to collect your data years afterwards. Reconcile it against HR before you send it, and if you find leavers, remove them and show both the finding and the correction — that is a far better outcome than the auditor finding them. Then add vendor authorisation lists to your offboarding checklist, because this recurs otherwise.

If you have no physical media at all — Common now, and a perfectly good answer. State it explicitly: backups are held in cloud storage in named regions, no media leaves the environment, and there is no third-party storage facility. Then answer the first half properly. A blank where the second half should be reads as an omission rather than as a negative.

Why it gets sent back

  • A maintained spreadsheet where a system-generated export was asked for.
  • Your own copy of the vendor authorisation list instead of the vendor's, or an undated one.
  • Former employees present on the backup system list or the vendor list.
  • Read access covered, while delete, retention change and immutability override are not.
  • Key holders not considered — the media is only as restricted as the key.
  • Physical access to the library or media safe left out where tapes exist.
  • Silence on the half that does not apply, instead of a sentence saying so.

Related

/backup-and-recovery · /user-management-permissions · /security-compliance-needs

Backup and recovery

Evidence of backup restoration testing performed during the period

#

What they are actually testing

The control is recoverability, and the only evidence of recoverability is a restore. This is the request organisations most often cannot satisfy, because backups are monitored and restores are not tested.

Typically maps to: Availability — recovery procedures are tested
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The restore test record: date, what was restored, to where, by whom.
  • How the restored data was validated — this is the part usually missing.
  • The elapsed time, compared against your stated recovery objective.
  • Any issues found and what was done about them.

Where to get it

Automated tests — The job log per run, including the validation step — a row count, a checksum, a query returning an expected value. 'Restore completed' is not validation.

Manual tests — A short written record is enough, provided it names the date, the performer, what was restored, how it was checked and how long it took. Make it a template so each test produces the same record.

DR exercises — The exercise plan, the outcome, and the actions arising — which also serves ISO 22301 if you hold it.

Why it gets sent back

  • No restore test in the period.
  • A restore with no validation of the restored data.
  • A test of a non-production system presented as covering production.

Related

/backup-and-recovery · /disaster-recovery-planning

Capacity and availability

Annual future processing capacity demand forecast report or evidence that processing capacity is automatically balanced in real-time

#

What they are actually testing

The word or is offering two architectures, not two levels of effort. If your capacity is fixed, you have to predict demand, because adding capacity takes weeks. If it is elastic, the scaling mechanism answers demand as it arrives and substitutes for the prediction. Both are legitimate. The trap is in the second one, and it catches a lot of submissions: autoscaling has a ceiling. A maximum instance count, an account quota, a node group limit, a licence count, a database that does not scale at all. Answering we autoscale without stating where it stops is not evidence that capacity is managed — it is evidence that capacity is managed up to a number nobody has checked against expected demand. An auditor who knows infrastructure will ask what the maximum is and what happens at it. The second thing worth understanding before you answer: this is not a per-organisation choice, it is per resource. Autoscaling handles compute. It does nothing for storage, which grows monotonically; nothing for the backup window, which is fixed while the data grows into it; nothing for per-core or per-user licences; and nothing for a single-writer database. Most organisations therefore owe both answers, for different resources.

Typically maps to: Availability — current processing capacity is evaluated and future capacity is managed to meet objectives
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • A statement of which answer applies to which resource, so the mixed case is explicit rather than looking like an incomplete submission.
  • Forecast route: the report itself, dated inside the period, with its inputs, its author, and evidence somebody in management received it.
  • Autoscaling route: the scaling policies, the configured minimum and maximum, and the account or service quotas sitting above them.
  • Scaling activity from the period, showing the mechanism actually operated rather than merely existing.
  • The ceiling, as a number, compared against forecast demand — the step that joins the two halves together.
  • The resources that neither route covers, and how they are managed.

Where to get it

The forecast, and where its numbers come from — A capacity forecast that is an opinion reads as one. Build it from measured history: growth rates per resource taken from your monitoring retention, multiplied out over the horizon, and set against the business inputs — expected users, transactions, new customers. State the assumptions on the page, because an assumption you wrote down and got wrong is a working process, while an unstated one is a guess. Our /growth-projections page covers the arithmetic, including the way compounding is routinely underestimated.

Present it per resource, not as a narrative — A table with one row per resource — current usage, projected at twelve months, the hard limit, and the resulting headroom expressed as a multiple or a date — answers the request on one page. It also surfaces the finding that matters, which is the shortest row rather than the largest number.

Compare last year's forecast against what happened — The single artefact that most improves this submission and the one almost nobody includes. A forecast is a claim; a forecast next to the actual outcome is a process. Being wrong is fine and expected — what it demonstrates is that the forecast is produced, kept, and revisited, which is exactly what the criterion asks. See /capacity-trending.

Autoscaling — the configuration — AWS: `aws autoscaling describe-auto-scaling-groups` gives the minimum, maximum and desired counts, and Application Auto Scaling covers the managed services. Kubernetes: `kubectl get hpa -A` for the workload policies, plus the cluster autoscaler configuration and the node group maximum, which is the real ceiling. Azure and GCP have direct equivalents. Capture the maximum in every case — it is the number the request is really about.

Autoscaling — evidence it operated — `aws autoscaling describe-scaling-activities` for the history, or `kubectl describe hpa` for the scaling events, taken across the period. A policy that has never triggered in twelve months is worth a sentence of explanation: either load never warranted it, which is fine and should be said, or the policy does not work and nobody noticed.

The quotas above your settings — Frequently overlooked, and the reason a scaling event fails at three in the morning. Your autoscaling group may permit forty instances while the account quota permits twenty. `aws service-quotas list-service-quotas --service-code ec2` and the equivalents elsewhere. Include the current quota values and show they sit above your configured maxima.

The database, which usually does not scale — Worth a paragraph of its own in the answer, because it is the most common real ceiling. Read replicas help reads and are usually added by hand; writes go to one primary. Serverless database tiers scale within bounds that are themselves a limit. State the write capacity of the primary as a number and how far current load sits from it — see /scalability-path.

The resources autoscaling never touches — Storage, which only grows; the backup window, which is fixed while the data expands into it; per-core or per-user licence counts, which have a commercial ceiling rather than a technical one; and any third-party service with a rate limit. These need the forecast route regardless of how elastic the compute is, and naming them is what turns a partial answer into a complete one.

Management oversight — The criterion is about capacity being evaluated, which implies somebody evaluated it. Whichever route you take, show the output reaching a person with authority — a review meeting, minutes, an approved plan, a dated sign-off. A forecast produced and filed is weaker evidence than a shorter one that was discussed.

Why it gets sent back

  • A forecast with no date, or dated outside the observation period.
  • We use autoscaling, with no maximum stated and no comparison against expected demand.
  • Autoscaling evidence for compute, while storage, licences, the backup window and the database are not addressed.
  • Configured scaling policies with no activity history and no explanation of why none fired.
  • Account or service quotas below the configured autoscaling maximum.
  • A forecast that is purely a narrative, with no numbers per resource and no limits.
  • No evidence the forecast or the capacity position reached management.

Related

/capacity-trending · /growth-projections · /scalability-path · /performance-tuning-capacity-planning

Monitoring and logging

Evidence of security monitoring and alerting, including alert response

#

What they are actually testing

That monitoring exists, that alerts reach a human, and that the human did something. The third is where evidence is usually thin — a dashboard is not a control, and an alert nobody acted on is worse than no alert.

Typically maps to: Security — the system is monitored and anomalies are evaluated
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The alert rules or detection configuration in scope.
  • The routing: who is notified, by what channel, and the escalation if unanswered.
  • A sample of alerts from the period with the response recorded.
  • Evidence that log collection itself is monitored.

Where to get it

Alert configuration — Export the rule definitions rather than screenshotting a dashboard — rules are the control, dashboards are a view.

Routing — The on-call schedule and escalation policy from the paging tool, covering the period.

Response — For sampled alerts, the acknowledgement time and the resulting ticket or note. This closes the loop that the auditor is looking for.

Log integrity — That logs ship off the host and that the destination is append-only or access-controlled.

Why it gets sent back

  • Dashboard screenshots with no alert rules.
  • Alerts with no evidence anyone responded.
  • Alerting configured to an unmonitored mailbox.

Related

/log-aggregation · /resource-monitoring

Incident management

Listing of security incidents during the period and, for a sample, evidence of handling

#

What they are actually testing

That the incident process is used, not that no incidents occurred. Declaring zero incidents over twelve months invites more scrutiny than declaring several handled well.

Typically maps to: Security — incidents are identified, responded to and resolved
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The incident register for the period, with severity and dates.
  • Per sampled incident: detection, triage, actions taken, resolution, and follow-up.
  • Evidence of customer or regulator notification where your policy required it.
  • The incident response procedure itself.

Where to get it

The register — Export from the ticketing or incident tool with timestamps intact.

Per incident — The timeline is the evidence: detected at, acknowledged at, mitigated at, resolved at, with who did what.

Follow-up — The post-incident review and the resulting actions with owners — see /post-incident-follow-up. An incident closed with no action is a reasonable outcome only if the review says why.

If there were none — Say so explicitly, and show the process is live: a tabletop exercise, or operational incidents handled through the same process.

Why it gets sent back

  • Zero incidents with no explanation of how you would know.
  • Incidents with no timeline.
  • A procedure document with no incidents run through it.

Related

/incident-handling · /post-incident-follow-up

Data handling

ONLY IF CONFIDENTIALITY OR PRIVACY CATEGORIES ARE IN SCOPE — Evidence of approval from appropriate data owners / management for any instances of the use of production, confidential and/or personal information in nonproduction environments

#

What they are actually testing

Start with the prefix, because it is doing real work. Confidentiality and Privacy are optional categories, so the first thing to establish is whether they are actually in your scope. If they are not, the correct answer is a sentence saying so, not a scramble for evidence you do not owe. Check the engagement scope before you spend a week on this. If they are in scope: non-production environments almost always have weaker controls than production. Broader access, developers with full database rights, authentication that is not enforced the same way, no encryption on the copies, data that is never deleted. Production data in such an environment is the same data with most of its protections removed, which is the same reasoning behind the backup media request. So the auditor is testing that any such use is a decided exception with a named person behind it, rather than something that happened because a developer needed realistic data on a Tuesday. The hard part is not the approvals. It is knowing where the copies are — and an auditor sampling unrelated evidence may find one before you do. Its companion request, asking for evidence that production data is not used in development at all, is at /soc2-evidence/#no-prod-data-in-dev — answer the two together so they cannot contradict each other.

Typically maps to: Confidentiality and/or Privacy — confidential and personal information is protected wherever it is held
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Confirmation of whether Confidentiality or Privacy are in scope. If not, that sentence is the whole answer.
  • The policy governing use of production data outside production.
  • The register of instances: what data, which environment, why, for how long.
  • Per instance, the approval — from the data owner, and from management too where your policy requires both.
  • The controls applied to each instance, and evidence the copy was disposed of at the end.
  • Where you use masked or synthetic data instead, evidence that the masking actually works.
  • How an unapproved instance would be detected — without this, the register only lists what people chose to declare.

Where to get it

The stronger answer is usually that you do not do it — If production data never leaves production, say so and evidence the mechanism rather than the exceptions: the refresh pipeline that masks on the way out, the absence of any route for a developer to take a dump, the synthetic data generator the test suite uses. A prohibition with a working control behind it is a better answer than a tidy approval register, because it removes the risk rather than governing it. A policy that says do not, with nothing enforcing or detecting it, is the weakest of all three positions.

Where to actually look for instances — This is the work. Non-production database refreshes taken from a production snapshot; seed data and test fixtures committed to a repository years ago; support and debugging tools that pull live records into a staging console; attachments and screenshots in tickets, which is where auditors stumble on real customer names while sampling something else entirely; analytics and BI sandboxes; spreadsheets on laptops; and environments handed to contractors or vendors. Search rather than ask — asking produces the instances people remember.

Machine learning training data — Increasingly the largest instance and the one least likely to be in anybody's register, because it does not feel like a copy of the database. If production records were used to train, fine-tune or evaluate a model, that is production data in a non-production environment and it needs the same approval. It also has a property the others do not: the data may not be straightforwardly removable from the model afterwards, which makes the disposal question harder to answer. Raise it before an auditor does. See /iso-42001 if AI governance is also in scope.

Masking, and proving it worked — Claiming the data is masked is not evidence that it is. Show the masking step in the refresh pipeline as code, and then show the result: a query against the non-production database looking for the patterns that should no longer be there — real email domains, plausible national identifiers, card-shaped numbers — returning nothing. That second artefact is what turns a claim into evidence, and it is a ten-line script worth keeping as a scheduled check. Be careful with the word anonymised: data that can be re-identified by joining it to something else is pseudonymised, not anonymous, and may still be personal data. Where that distinction carries legal weight for you, it is a question for your data protection adviser rather than for the engineering team.

Approvals that hold up — The approver has to be the data owner, not the person who wanted the data. That sounds obvious and is the most common defect here, because the request usually originates from the team that needs it and gets signed off inside the same team. Each approval needs a date before the copy was made, a stated purpose, a scope — which fields, which records — and an expiry.

Disposal — An approval with no end date is an open-ended one, and the register will fill up with instances nobody ever closed. Each entry needs a disposal date and evidence the copy is gone: the environment destroyed, the database dropped, the file deleted. Include the backups of the non-production environment, which quietly keep the data long after the environment itself is gone.

Detection, so the register means something — A register lists what people declared. To show it is complete you need something that would catch an undeclared instance: periodic scanning of non-production data stores for personal-data patterns, database activity monitoring on production exports, data loss prevention on egress, or simply removing the ability to take a production dump from everyone who does not need it — prevention being easier to evidence than detection.

If Confidentiality and Privacy are not in scope — Answer the conditional explicitly: state that neither category is within the scope of the examination and that this request therefore does not apply. Do not leave it blank, and do not volunteer a partial answer — evidence produced for a category that is out of scope can raise questions the engagement was never meant to cover.

Why it gets sent back

  • A policy prohibiting the practice, with no detection and no register.
  • Approval granted by the person or team that requested the data.
  • Masked data claimed, with no evidence the masking removed anything.
  • Instances the auditor finds that are not in the register — typically a screenshot with real customer data attached to a ticket.
  • Approvals with no expiry, and entries with no disposal evidence.
  • Non-production backups still holding the data after the environment was removed.
  • Silence on the conditional, so it is unclear whether the request applies at all.

Related

/security-compliance-needs · /iso-27701 · /user-management-permissions · /soc-2

Data handling

ONLY IF CONFIDENTIALITY OR PRIVACY CATEGORIES ARE IN SCOPE — Evidence showing that confidential and/or personal data are not used in development activities (e.g. data masking software, fictitious data, etc.)

#

What they are actually testing

The mirror image of the approval request, and auditors frequently send both. That one governs the exceptions: where production data does reach a non-production environment, who approved it. This one asks for the mechanism that makes exceptions unnecessary in the first place. Between them they are meant to be exhaustive — either the data does not go there, or it does and somebody owned the decision. Which produces the trap worth knowing about before you answer: if you claim here that production data is never used in development, and the approval register for the other request has entries in it, you have contradicted yourself in the same submission. Answer them together, and let the two agree: the mechanism covers the estate, and the register covers the named exceptions to it. Note also that the request offers examples rather than requirements. Masking software and fictitious data are two ways of satisfying it; so is having no data path at all. Evidence whichever you actually use.

Typically maps to: Confidentiality and/or Privacy — confidential and personal information is not disclosed to environments that do not require it
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Confirmation that Confidentiality or Privacy are in scope — otherwise the request does not apply and one sentence answers it.
  • The mechanism, named: masking on refresh, synthetic generation, subsetting, or no data flow from production whatsoever.
  • Evidence the mechanism operates, not only that it is configured — a pipeline run from inside the period.
  • A verification result: a search of non-production data stores for real data, returning nothing.
  • Coverage across every non-production environment, including the ones nobody thinks of.
  • The preventive control: who can extract production data at all.
  • Any exceptions, pointing at the approval register rather than omitting them.

Where to get it

Name your mechanism, then prove it ran — Whether it is a commercial masking product, an anonymisation step written in SQL, a faker-based fixture generator or a subsetting tool, the evidence has two halves. The configuration or code, which shows what it does; and a run from inside the observation period, which shows it happened. A masking product with an impressive configuration screen and no job history is the same failure as a backup tool with no job history.

The verification query is the artefact that convinces — Everything above is a claim about what should be in the data. This is a measurement of what is: run a search against each non-production data store for the patterns that would indicate real records — your live email domains, nationally formatted identifiers, card-shaped numbers, phone prefixes — and show it returning nothing. Keep it as a scheduled job rather than a one-off, because then you also have a run history, which answers the period question too. It is perhaps twenty lines of SQL and it is the single most persuasive thing you can hand over here.

Canary records, if you want the strong version — Seed a handful of uniquely identifiable records into production that exist nowhere else and correspond to no real person. If one of them ever turns up in a non-production environment, a log, a ticket or a support export, you know a copy was taken and roughly when. It converts a claim that data does not leak into a detective control that would notice if it did, and it costs almost nothing to set up.

Cover the environments people forget — A claim about development activities has to hold across all of them, and the main staging environment is usually the only one that gets masked properly. Also in scope: continuous integration test databases, ephemeral preview environments spun up per branch, developers' local machines, analytics and BI sandboxes, and anything a contractor was given. The honest position for each is either that it receives no production data, or that the same mechanism applies to it.

Local development machines, the hard one — The claim is hardest to defend here because you do not control the laptop. What works is making the sanctioned path the easy one: a seeded fixture set or a containerised dataset developers can pull in a single command, so nobody needs to ask for a production dump. Pair it with the preventive side — if the ability to export from production is limited to a small named group, the claim about laptops largely follows from that, and the access listing evidences it.

Continuous integration — Test databases should be built from migrations plus fixtures, never restored from a production snapshot. This is worth checking rather than assuming: a CI pipeline restoring a nightly production dump to run integration tests is a common arrangement that contradicts everything else in the submission, and it is visible in the pipeline definition.

Logs, exports and support tooling — Data reaches non-production through channels that are not database copies. A debug log captured during a support session, a bug report with a request body attached, an export a developer took to reproduce something. These belong in the answer: show that logs are redacted at source — see /log-aggregation — and that support tooling does not copy live records outward.

Be careful with anonymised — Masked and anonymous are not the same claim. Data that can be re-identified by joining it against something else is pseudonymised, and in several jurisdictions remains personal data with all the obligations attached. Describe what your mechanism actually does — replaced, hashed, shuffled, generated — rather than reaching for the stronger word. Whether the result is legally anonymous is a question for your data protection adviser, not for the submission.

Why it gets sent back

  • Masking configuration with no run history from within the period.
  • Masking applied to staging while CI, preview environments or laptops are not covered, or not mentioned.
  • No verification that the masked data contains nothing real — a claim without a measurement.
  • A CI pipeline restoring a production snapshot, contradicting the rest of the answer.
  • This request answered with never, while the approval register for the companion request contains entries.
  • Logs, exports and support tooling treated as outside the question.
  • The word anonymised used for data that is merely pseudonymised.

Related

/security-compliance-needs · /iso-27701 · /log-aggregation · /cicd-setup

Vendor management

Listing of third-party vendors and evidence of due diligence performed

#

What they are actually testing

That you know who processes your data, that somebody assessed the risk, and that the assessment is refreshed. Obtaining a vendor's SOC 2 report is not the control — reading it is, including its exceptions and the complementary controls it expects of you.

Typically maps to: Security — vendors and business partners are assessed and monitored
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The vendor inventory with what each one does and what data they touch.
  • Risk classification per vendor.
  • Assurance evidence for the significant ones, with a record that it was reviewed.
  • Contracts with the relevant security and data protection terms.

Where to get it

The inventory — Reconcile the list against what is actually paid for — finance's records find vendors that IT's list does not.

Review record — A short note per vendor: who read the report, when, what the exceptions were, and whether the complementary user entity controls apply to you. A stored PDF with no note is a filed document, not a review.

Ongoing — Evidence that reports are refreshed as they expire, and that a vendor with no current report was escalated.

Why it gets sent back

  • A vendor list with no risk assessment.
  • Stored SOC 2 reports with no evidence anyone read them.
  • An expired report accepted without comment.

Related

/dependency-mapping

Infrastructure

System configuration standard documents or “system hardening procedure documentation” for all in-scope system types (include router, firewall, servers — web, database, application, POS servers, etc.)

#

What they are actually testing

This request is about the documents, not about any individual machine. The auditor is testing two things. First coverage: that a configuration standard exists for every system type in scope, which they will check by laying your list of standards against your asset inventory and system description — a type that appears in the inventory with no corresponding standard is the finding, and the enumeration in the request (router, firewall, web, database, application, POS) is a prompt to make you go looking rather than an exhaustive list. Second, that each standard is a controlled document: owned, approved, versioned, dated and reviewed, rather than a wiki page somebody wrote once. Note the wording. “System configuration standards” and the mention of POS servers is PCI DSS vocabulary rather than SOC 2 vocabulary, so a request phrased this way is often a checklist being reused across frameworks, or a combined assessment. It is worth asking which framework the request belongs to before you answer it, because PCI expectations around configuration standards are more prescriptive than SOC 2's — and if you have no POS estate at all, say so explicitly rather than leaving a silent gap in the coverage table.

Typically maps to: Security (common criteria) — baseline configurations are defined and maintained for system components
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The list of in-scope system types, taken from the asset inventory and the system description — this is what coverage is measured against, so it comes first.
  • One configuration or hardening standard per type, as a document you can hand over.
  • A coverage table mapping each system type, and each asset, to the standard that governs it. This single artefact answers most of the request.
  • Approval evidence per standard: author, approver, version, effective date.
  • Evidence each standard was reviewed within your stated cycle during the period.
  • The recognised source each one derives from, and where you departed from it.
  • Documented, approved deviations — the exceptions that exist in reality.

Where to get it

Start from the inventory, not from the documents — Produce the list of in-scope system types first and get agreement on it. Writing standards before the scope is settled is how you end up with five polished documents and a sixth type nobody covered. The /infrastructure-audit output is usually the right source, and the same reconciled asset register serves both purposes.

If the hardening lives in code and not in a document — This is the normal situation for an engineering team, and it is a good position rather than a gap. Your configuration management role, image build definition or Terraform module is the de facto standard — so generate the document from it: what the role enforces, why, and which deviations are parameterised. Do not write an aspirational document separately from the code. An auditor may sample a running host and compare it against the standard, and a document that disagrees with reality is worse evidence than no document, because it turns a coverage gap into a control failure.

Derive from a recognised source and say which — CIS Benchmarks, vendor hardening guides, or DISA STIGs. Name the source and version in the document. Two things auditors look for: that you tailored it rather than attaching the PDF unchanged, and that the tailoring decisions were made deliberately — a recorded 'not applicable, we do not run this service' is a perfectly good answer.

Routers and switches — Management plane access and authentication, disabled unused services and ports, SNMP community and version, logging destination, NTP, firmware baseline, and config backup. Evidence the running configuration matches: a diff of the live config against the intended one is strong, and easy to automate.

Firewalls — The default-deny posture, rule review cadence and last review date, admin access and MFA, logging, and firmware. See /network-and-firewall-configuration — the rule set itself belongs to a different request, this one is about the standard the rule set is built to.

Servers, by role — Web, application, database and any others each need their own standard, because what is hardened differs: a web server standard covers TLS configuration, headers and disabled modules; a database standard covers authentication method, network binding, encryption and privileged roles. A single 'server hardening' document covering all roles is the most common way this request fails. The shared base — accounts, logging, time, patching, minimal install — can sit in one common standard with role-specific ones layered on it, which is both less work and easier to defend. See /os-installation-and-hardening.

POS and payment terminals, if you have them — Usually vendor-supplied and vendor-maintained, which does not remove them from the coverage table. Document the vendor's hardening guidance you follow, your deployment configuration, how updates reach them, and the split of responsibilities with the vendor. If you have no POS estate, put a line in the table saying so.

Cloud, virtual and container images — Frequently missed because they do not feel like 'system types'. Base images, machine images, Kubernetes node configuration and the cloud account baseline each need a standard. For images, the build definition plus the scan gate is the document.

Making them controlled documents — Whatever you use — a document system, or Markdown in a repository. A repo is often the better answer: the commit history is the version control and the review evidence, pull request approvals are the approval record, and it sits next to the code that implements it. Either way each document needs an owner, a version, an effective date and a recorded review inside the period.

Why it gets sent back

  • One 'server hardening' document when the inventory distinguishes web, database and application servers.
  • Standards covering servers but not network devices, or the reverse.
  • A CIS Benchmark PDF attached unchanged, with no adoption or tailoring decision.
  • Documents with no version, no approver or no date.
  • No review recorded during the period when the policy says annual.
  • A standard that does not match the running configuration of a sampled host.
  • Deviations that exist on systems but appear nowhere in the documents.
  • A system type in the asset inventory that no standard covers, and no statement that it was consciously excluded.

Related

/system-configuration-standards · /os-installation-and-hardening · /network-and-firewall-configuration · /infrastructure-audit · /infrastructure-as-code

Infrastructure

Evidence of secure configuration baselines for in-scope infrastructure

#

What they are actually testing

That systems are built to a defined standard rather than individually, and that drift from it is detected. Configuration management makes this straightforward; hand-built servers make it painful.

Typically maps to: Security — infrastructure is configured to prevent unauthorised access
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The baseline itself — the configuration code or hardening standard.
  • Evidence it is applied across the in-scope estate.
  • Drift or compliance reporting across the period.
  • Firewall and network rules for in-scope environments.

Where to get it

Configuration management — A check-mode or dry-run report across the fleet is the ideal artefact: it shows both the standard and the conformance in one output.

Cloud — AWS, Azure, GCP — Config rules, policy compliance dashboards, or the equivalent posture report, exported with dates.

Oracle Cloud Infrastructure (OCI) — Cloud Guard is the nearest equivalent to a posture report: export its problems and the detector recipes behind them, with dates, and include the ones you chose to accept as well as the ones you closed. Security Zones are stronger evidence where you use them, because a zone refuses a non-conformant resource at creation rather than reporting it afterwards — that is prevention rather than detection, and worth saying so explicitly. For the network baseline: `oci network vcn list`, `oci network security-list get --security-list-id <ocid>` and the NSG rules. For storage: that buckets are not public, which `oci os bucket get --name <bucket>` reports, and that block volumes are encrypted. Where infrastructure is built from a Resource Manager stack, the Terraform in it is both the baseline and the evidence it was applied.

Network — Security group and firewall rule exports, plus evidence they are reviewed — see /network-and-firewall-configuration.

Encryption — That storage encryption and TLS are enforced: the setting, and a listing showing no unencrypted resources.

Why it gets sent back

  • A hardening document with no evidence of application.
  • Compliance reporting covering some hosts.
  • Firewall rules with no review record.

Related

/os-installation-and-hardening · /network-and-firewall-configuration

Communication

Example communication (i.e. newsletter, email, or announcement) made to both internal and external users regarding a change made to the system within the review period

#

What they are actually testing

The word both is the whole test. Nearly every organisation can produce one half — usually the internal one, because engineering teams announce deployments as a matter of habit — and then discovers that telling customers about changes happens informally, through account managers, or not at all. There is a second distinction inside the internal half that is easy to miss. Internal users means the people inside your company who use or depend on the system: support, operations, sales, finance. It does not mean the engineers who built it. A message in a deployment channel saying the release went out is engineering chatter, not communication to internal users, and an auditor who reads the channel will see the difference immediately. The request asks for one example, which is easy to read as a low bar. The stronger answer states what your criteria are for which changes get communicated and to whom, and then produces an example of those criteria being applied.

Typically maps to: Common criteria — the entity communicates relevant information, including system changes, to internal and external users
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Your criteria for what gets communicated, to which audience, and with how much notice — a short documented practice is enough.
  • One system change from inside the review period, ideally communicated to both audiences, so the two artefacts describe the same thing.
  • The internal communication: content, date, audience and channel.
  • The external communication: content, date, audience and channel.
  • Evidence of delivery where the channel gives it — a send record, a published page, an archive link.
  • Where a contract or a policy sets a notice period, evidence it was met.

Where to get it

Pick one change and cover it twice — Two artefacts about the same change is a much stronger submission than two unrelated ones. An auditor reading a customer email about a maintenance window and an internal briefing about the same window can see a process; two disconnected examples look like whatever was easiest to find. Maintenance windows and planned downtime are usually the easiest change to evidence this way, because both audiences genuinely have to be told.

External — the public artefacts — A release notes or changelog page is close to ideal: dated, continuous, publicly verifiable, and it proves the practice rather than one instance. A status page covering maintenance notices is equally good and often better for changes that affect availability. Both have the advantage that the auditor can check them without you sending anything.

External — the sent ones — For an email or newsletter, the platform's send record is the evidence, not the draft: the send date, the audience segment and the recipient count. In-app banners and notifications count too — screenshot the configuration with its display window alongside the message itself.

External — enterprise customers — Where changes are communicated through account managers rather than broadcast, that is legitimate but harder to evidence. Produce the template or briefing the account team was given plus a sample of the messages actually sent. A verbal process with nothing written is the weak case here.

Internal — to users, not to engineers — The artefacts that work: a notice to the support team ahead of a change, with what to expect and what to tell customers; an intranet or wiki announcement; an all-hands slide; an updated runbook circulated to operations. If your only internal evidence is a deployment channel, add one of these — it is a small habit to start and it closes the most common gap in this request.

APIs and developer-facing changes — If you publish an API, your external users are developers and the expected communication is a developer changelog, version deprecation notices and sunset timelines. Where a change is breaking, show the notice period given and that it matched whatever you commit to publicly.

Timing, where it matters — For maintenance, downtime or a breaking change, when you told people is part of the control. Show the communication date against the change date, and against any notice period in your contracts or terms. Notice given after the fact for something that required notice beforehand is a finding even though the communication exists.

If nothing qualifying happened — Possible, for a quiet period or a system with no user-visible changes. Say so explicitly and evidence the mechanism instead — the changelog page, the status page, the notification process — so it is clear the absence is a fact about the period rather than about the control. A blank answer reads as the latter.

Why it gets sent back

  • One audience covered and the other not mentioned.
  • A deployment channel message offered as communication to internal users.
  • Marketing content that is not about a change to the system.
  • Two unrelated examples with nothing linking them to the same change.
  • A draft or a template instead of evidence that it was sent or published.
  • Undated material, or material from outside the review period.
  • Notice given after a change that required notice beforehand.
  • Silence where no qualifying change occurred, rather than a sentence saying so.

Related

/incident-handling · /scheduled-maintenance · /responsive-support

People

Evidence that personnel completed security awareness training during the period

#

What they are actually testing

Simple to satisfy and frequently failed on completeness. The test is that everyone in scope completed it, including joiners during the period and contractors if your policy covers them.

Typically maps to: Security — personnel are made aware of their responsibilities
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The completion report with names and dates.
  • The employee population for the period, to reconcile against.
  • The training content or its outline.
  • Follow-up evidence for non-completers.

Where to get it

Platform export — Completion report per person per course, with dates.

Reconciliation — Against the HR list including leavers and joiners — the reconciliation is the evidence, not the raw report.

Joiners — That training happened within the window your policy states after start date.

Why it gets sent back

  • A completion percentage with no names.
  • Gaps with no follow-up.
  • Joiners late in the period with no training and no explanation.
Monitoring and logging

Audit log settings with a recent log extract for in-scope production servers

#

What they are actually testing

Two things are being tested, and the request wording hides the second one. The setting proves logging is turned on and configured to capture the right events; the extract proves it is actually running today and producing records. An auditor who gets only the configuration cannot tell whether the daemon has been dead for a month. An auditor who gets only the extract cannot tell what is being captured and what is silently excluded.

“Audit log” on a server means more than syslog. The events that matter are authentication (who logged in, from where, how), privilege use (sudo, su, root shells), and changes to the files that govern access — accounts, sudoers, the SSH daemon's configuration, the firewall. Application chatter in the same file does not count against you, but it does not count for you either.

Typically maps to: Security — system activity is logged; logs are retained and protected
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The audit configuration per server class: what is enabled, which events or files are watched, where the log goes, how long it is kept and who can read or alter it.
  • A recent extract — a few dozen lines are enough — that shows a login, a privileged command and a watched-file change, each with a timestamp, the account and the host.
  • The population of in-scope servers, so the auditor can see the setting applies to all of them and not to the one you exported from.
  • Evidence of log protection: shipping off the host, append-only storage or restricted read access, and that the retention period is at least what your policy states.

Where to get it

Linux with auditd — `auditctl -s` (daemon status, lost-event counter) and `auditctl -l` (the loaded rules) are the settings; `/etc/audit/auditd.conf` gives retention and rotation. For the extract use `ausearch -i --start today -m USER_LOGIN,USER_CMD` and `ausearch -i -k <your-key>` for watched files — the `-i` flag turns UIDs and timestamps into readable values, which is what the reviewer needs.

Linux without auditd — journald and rsyslog still give you authentication and sudo: `journalctl -u sshd`, `journalctl _COMM=sudo`, `/var/log/secure` or `auth.log`. Show retention with `journalctl --disk-usage` and the `SystemMaxUse`/`MaxRetentionSec` lines, plus the logrotate stanza. Be honest in the write-up that this covers access but not file-level change auditing.

Windows — The Advanced Audit Policy (`auditpol /get /category:*`) is the setting; the Security event log filtered to 4624/4625 (logon), 4672 (privileged logon) and 4719 (audit policy changed) is the extract. Export from Event Viewer with the hostname and time zone visible.

Cloud instances — Instance-level logging is still the host's own; the cloud adds the control plane — AWS CloudTrail, Azure Activity Log, GCP Cloud Audit Logs, OCI Audit — which records who started, stopped or reconfigured the servers. Provide both layers.

Containers — The host is the audit boundary, not the container. Container stdout/stderr through the Docker log driver (`/etc/docker/daemon.json` with a `json-file` size cap, or a shipper) is application logging; host auditd still sees `docker exec` and changes to the daemon's configuration — include a watch on those.

Protection and retention — Where the logs go after the host: the shipper configuration, the destination's access list, and the retention setting there. If logs stay on the host, say so and show the file permissions and rotation.

Why it gets sent back

  • A logging configuration with no extract, or an extract with no configuration.
  • An extract that shows only application noise and not a single authentication or privilege event.
  • Logs older than the observation period, or an extract with no timestamps or host.
  • One server's settings offered for a fleet with no evidence the rest match.
  • auditd “active” with zero rules loaded — the daemon alone proves nothing.
  • Retention shorter than the policy, or no answer on who can delete the logs.

Related

/access-logging · /log-aggregation · /os-installation-and-hardening

Monitoring and logging

Audit log settings with a recent log extract for in-scope production databases

#

What they are actually testing

The database holds the data the whole examination is about, so the auditor wants to know whether access to it leaves a trace. The minimum that satisfies this is connection logging — who connected, from where, when, and failed attempts — plus logging of schema and permission changes. Full statement logging is not expected and is usually impractical; what gets evidence returned is a database that logs only checkpoints and slow queries, because that is performance tuning, not audit.

Managed cloud databases shift part of this to the provider: the engine's parameter group is your setting, and the provider's log export is your extract.

Typically maps to: Security — system activity is logged; logical access to data is monitored
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The logging parameters of each in-scope database engine, showing connection and DDL (or equivalent) logging enabled, the log destination and its retention.
  • A recent extract containing at least one connection, one disconnection or failed login, and one privilege or schema change, each with user, database, client address and timestamp.
  • The role listing, so the auditor can match the accounts in the extract to real people or services.
  • For databases with no native audit facility (SQLite, embedded stores), the application-level access log that stands in for it, and a sentence saying why.

Where to get it

PostgreSQL — `SELECT name, setting, source FROM pg_settings WHERE name IN ('log_connections','log_disconnections','log_statement','log_line_prefix','log_destination','logging_collector')` is the setting — `log_connections = on`, `log_disconnections = on`, `log_statement = 'ddl'` and a prefix carrying `%u@%d %h` are the usual bar. Enable with `ALTER SYSTEM SET … ; SELECT pg_reload_conf();` (one statement at a time, no restart). The extract is the server log (or `docker logs` for a containerised instance) filtered on `connection` and `statement`.

MySQL / MariaDB — `general_log` is too noisy for production; use the audit plugin (MariaDB `server_audit`, Percona `audit_log`, MySQL Enterprise Audit) with `connect` and `query_ddl` event classes. `SHOW VARIABLES LIKE 'server_audit%'` is the setting; the audit file is the extract.

SQL Server — Server-level audit specification with `FAILED_LOGIN_GROUP`, `SUCCESSFUL_LOGIN_GROUP`, `SCHEMA_OBJECT_CHANGE_GROUP` and `SERVER_ROLE_MEMBER_CHANGE_GROUP`; `sys.server_audits` and `sys.server_audit_specification_details` are the setting, `sys.fn_get_audit_file` the extract.

Managed cloud databases — AWS RDS/Aurora: the parameter group's log settings and the CloudWatch log export; Azure SQL/Database for PostgreSQL: auditing or diagnostic settings to a Log Analytics workspace; GCP Cloud SQL: database flags plus Cloud Logging; OCI Autonomous Database: the Unified Audit trail (`UNIFIED_AUDIT_TRAIL`) or Data Safe's activity auditing report.

Oracle Database — `SELECT * FROM audit_unified_enabled_policies` for the setting (`ORA_LOGON_FAILURES` and `ORA_SECURECONFIG` at minimum), `UNIFIED_AUDIT_TRAIL` ordered by `event_timestamp` for the extract.

SQLite and embedded stores — There is no audit facility. Show the file's permissions and owner, the fact that only one application process opens it, and that application's own access log with the client address — that is the compensating control, and it is accepted when stated plainly.

Why it gets sent back

  • `log_statement = none` and `log_connections = off` with a log full of checkpoints.
  • A slow-query log offered as an audit log.
  • An extract without the client address or the user, so nobody can tell who connected.
  • Settings from a development or staging instance.
  • Logs that live only inside the database host with no retention stated.
  • The provider's generic documentation instead of your instance's parameters.

Related

/access-logging · /log-aggregation · /backup-and-recovery

Monitoring and logging

Audit log settings with a recent log extract for in-scope production applications

#

What they are actually testing

At the application layer the auditor wants attributability: that an action in the system can be traced to an account, a time and an origin. Debug logging that prints stack traces is not that. The events that carry weight are authentication (success and failure), administrative actions (user created, role changed, setting changed), and access to the records the application exists to protect.

Many applications never had an audit log designed; what they have is a web server access log plus whatever the framework prints. That can still pass if the access log carries the real client address and the authenticated user, and if the administrative actions are logged somewhere you can show.

Typically maps to: Security — system activity is logged; user actions are attributable
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The logging configuration per application: which events are logged, the format (so the auditor can see user and source fields are present), the destination and its retention.
  • A recent extract per application with a login, a failed login, and one administrative or data-access action, each with user, timestamp and source address.
  • For applications behind a reverse proxy or load balancer, evidence that the real client address is forwarded and recorded (`X-Forwarded-For` or equivalent), not the proxy's own.
  • The in-scope application list, with the logging mechanism named for each — including the ones that only have an access log, said plainly.

Where to get it

SaaS applications — The admin console's audit log (Google Workspace, Microsoft 365, Okta, GitHub organisation audit log, Atlassian audit log): a screenshot of the retention setting and an export of the last few days filtered to login and admin events.

Your own services — Show the logging call for the events that matter — a grep of `logger.info` or `audit(` around login and admin handlers is acceptable — plus the configured level, handler and format. If the framework provides it (Django `LogEntry`, Rails `audited`, Spring Security events), show that it is enabled.

Web servers and proxies — nginx or Apache access logs with a format containing `$remote_addr`, `$http_x_forwarded_for`, `$remote_user` and the status; for gunicorn `--access-logfile -` with an `--access-logformat` that includes the forwarded address. Include the logrotate or log driver settings.

Containers — `docker inspect <container> --format '{{.HostConfig.LogConfig}}'` shows the driver and size caps; `/etc/docker/daemon.json` sets the default. `docker logs --since 24h <app>` is the extract. If logs are shipped (Loki, Fluent Bit, CloudWatch agent), show the shipper's configuration and the destination's retention.

Admin consoles of infrastructure tools — Reverse-proxy managers, CI servers and monitoring tools usually keep their own audit table (who changed which proxy host, pipeline or alert). Export it — it is often the strongest application-level evidence you have.

Why it gets sent back

  • Debug or error logs with no user or source fields.
  • An access log where every request appears to come from the load balancer's address.
  • Settings alone, with no extract from the period.
  • An extract that cannot be tied to a named in-scope application and environment.
  • Logging that stops at the container with no retention beyond the next restart.

Related

/access-logging · /log-aggregation · /monitoring-logging-solutions

Monitoring and logging

Audit log settings with a recent log extract for in-scope VPNs

#

What they are actually testing

The VPN is the door into the private network, so the auditor wants to see who came through it and when. For a classic remote-access VPN that is a session log: user, source address, assigned address, connect and disconnect time, and failed authentications. For WireGuard and other key-based tunnels, which have no sessions and no built-in logging, the auditor still expects the same questions answered — you have to produce the trail yourself from the handshake state, and explain that the “user” is a device key mapped to a person.

Typically maps to: Security — remote access is logged and monitored
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The VPN's logging configuration: what is logged, where it goes, retention, and the peer or user population it covers.
  • A recent extract with at least one successful connection, one disconnection or stale peer, and a failed or rejected attempt if the technology produces one.
  • The mapping from VPN identity (username, certificate, public key) to a named person or device, so the extract is attributable.
  • Evidence that changes to the VPN configuration itself (peers added, routes changed) are logged or audited.

Where to get it

OpenVPN — `verb 3` or higher, `status` file and `log-append` in the server config are the settings; the status file and the connect/disconnect lines in the log are the extract. `client-connect` scripts, if used, show what is recorded per session.

WireGuard — There is no session log by design. Produce one: a cron job that reads `wg show <iface> dump` every few minutes and appends endpoint and handshake changes per peer to a file, with a periodic heartbeat line — that is your setting (the script and its cron line) and your extract (the file). Map public keys to hosts or people in a separate, non-secret file. Add an auditd watch on the configuration directory so peer changes are recorded, and include the SSH `Accepted` lines from the private subnet as the “who did what after connecting” layer.

Cloud VPN / managed — AWS Client VPN connection logs to CloudWatch, Azure VPN Gateway diagnostics (`GatewayDiagnosticLog`, `P2SDiagnosticLog`), GCP Cloud VPN logs in Cloud Logging, OCI Site-to-Site VPN logs via the Logging service: the diagnostic setting is the configuration, the log query is the extract.

Appliances (Fortinet, Palo Alto, Cisco) — The syslog/forwarding setting for VPN events and the SSL-VPN or IPsec event log filtered to the period; include the RADIUS or IdP authentication log if the VPN delegates login.

Zero-trust access (Tailscale, Cloudflare Access, Teleport) — The admin console's audit or session log, exported, plus the retention plan tier that governs it.

Why it gets sent back

  • “The VPN does not log” with no compensating trail.
  • A peer list or `wg show` snapshot offered as history.
  • Session records with no mapping to people or devices.
  • Logs kept only on the VPN gateway with no retention or protection stated.
  • Configuration changes to the VPN that appear nowhere.

Related

/network-and-firewall-configuration · /access-logging

Encryption and data protection

Encryption configurations for data at rest (DES, AES, RC, DH, ECC, RSA), including backups and removable media

#

What they are actually testing

The list of algorithms in brackets is a template line, not a wish list: the auditor wants to know which algorithm and key length protect each store, and will flag DES, RC4 and short RSA keys if they turn up. The real test is coverage — every place the in-scope data sits, including the copies people forget: backups, snapshots, exported dumps, the laptop that holds a standby copy, the SD card or USB disk used as off-site media.

Cloud block storage is encrypted by default on every major provider, which makes the production volumes the easy part. Evidence usually comes back over the backup chain and the removable media, because those are where encryption is a decision someone had to make rather than a default.

Typically maps to: Security / Confidentiality — data at rest is protected by encryption
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • An inventory of data stores in scope — volumes, databases, object storage, backup targets, removable media, standby hosts — each with: encrypted yes/no, algorithm and key length, and who manages the key.
  • The configuration or console view proving encryption for each store, not a statement that it is enabled.
  • For backups: the encryption step in the backup job, and a check of a recent backup file showing it is ciphertext (format header or `file`/`gpg --list-packets`).
  • For removable media: the policy, the encryption on the device, and where the key or passphrase lives — it must not sit on the media.
  • Key management: where keys are stored, who can read them, rotation if any.

Where to get it

Cloud block and object storage — AWS: EBS volume `Encrypted: true` and the KMS key, S3 default encryption (SSE-S3/SSE-KMS), RDS `StorageEncrypted`; Azure: SSE with platform or customer-managed keys on the storage account and managed disks; GCP: default encryption plus CMEK where used; OCI: every Block Volume and boot volume is AES-256 with Oracle-managed keys unless a Vault key is assigned — show the volume's “Encryption key” field in the console or `oci bv volume get`.

Linux hosts and laptops — `lsblk -f` shows `crypto_LUKS` under the physical device when full-disk encryption is in place; `cryptsetup luksDump <dev>` gives the cipher (`aes-xts-plain64`) and key size. A standby or backup host with plain `ext4` on bare metal is a finding to disclose, not hide.

Databases — Transparent Data Encryption settings (SQL Server `sys.dm_database_encryption_keys`, Oracle `V$ENCRYPTED_TABLESPACES`), or the statement that the engine relies on volume encryption underneath — acceptable if the volume evidence is there.

Application-level — Where the application encrypts fields or secrets itself (a password vault with Fernet/AES-GCM, envelope encryption), show the library and mode in the code and where the key file lives relative to the data.

Backups — The backup script or job definition at the encryption step (`gpg --symmetric --cipher-algo AES256`, `restic`/`borg` repository keys, the backup product's encryption setting), plus `gpg --list-packets` or the product's verify command on a recent file. State where the passphrase is kept and that it is not on the destination.

Removable media and phones — BitLocker To Go or LUKS on the medium; on Android `getprop ro.crypto.state` (`encrypted`) and `ro.crypto.type` (`file`); on iOS the device passcode implies data protection. If a medium carries unencrypted copies, list it and the remediation plan.

Why it gets sent back

  • “Encrypted at rest” as a sentence with no algorithm, key length or screenshot.
  • Production volumes covered and the backup chain not mentioned.
  • An encrypted backup whose passphrase is stored next to it.
  • Removable media or standby laptops left out of the inventory.
  • DES, 3DES, RC4, MD5-based schemes or RSA under 2048 bits anywhere in the chain.
  • Keys readable by every administrator with no record of who has access.

Related

/backup-and-recovery · /tested-backups · /os-installation-and-hardening

Encryption and data protection

Encryption in transit configurations for all applicable data transfer points (VPN, SSH, SSL, TLS, SMTP, etc.)

#

What they are actually testing

“All applicable data transfer points” is the operative phrase. The auditor expects you to have enumerated them: the public web edge, administrative access, service-to-service traffic, backup transfers, mail, API calls to third parties, and the tunnel that carries the private network. For each one they want the configured protocol and the proof that weak options are refused — not that strong ones are available.

The usual gap is internal traffic: plain HTTP between services is acceptable only when it never leaves an encrypted tunnel or a private, non-routable network, and that has to be stated and shown rather than assumed.

Typically maps to: Security / Confidentiality — data is protected during transmission
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • A list of transfer points with the protocol and version used at each, and whether it is enforced (redirect, HSTS, `PasswordAuthentication no`, cipher allow-list).
  • For TLS endpoints: the server configuration (protocols, ciphers, certificate source) and an external test result per hostname.
  • For SSH: the effective daemon configuration showing key-only authentication and the cipher, MAC and key-exchange lists.
  • For the VPN: the protocol and its cryptographic suite, and the configuration file with keys removed.
  • For SMTP: TLS enforcement on the mail service, or a statement that no mail is sent from the environment.
  • A sentence on internal plain-text traffic and why it is acceptable, with the evidence that it stays inside the tunnel or private network.

Where to get it

TLS endpoints — The web server or proxy's `ssl_protocols` / `ssl_ciphers` (nginx or nginx-proxy-manager), the listener policy on AWS ALB / Azure Application Gateway / GCP HTTPS LB / OCI Load Balancer; then an external check: `openssl s_client -connect host:443` for the negotiated version and cipher, `-tls1_1` to show refusal, `curl -sI https://host/` for `Strict-Transport-Security`, and `curl -sI http://host/` for the redirect. An SSL Labs report is a fine supplement, dated.

SSH — `sshd -T | grep -iE 'ciphers|kexalgorithms|macs|passwordauthentication|permitrootlogin'` is the effective configuration — better than the file, which may be overridden by `sshd_config.d`. Key-only authentication on every host, and a reason if root login is permitted.

VPN — IPsec: the IKE and ESP proposals (AES-GCM, SHA-2, DH group 14+); OpenVPN: `tls-version-min`, `cipher`/`data-ciphers`, `auth`; WireGuard: there is nothing to negotiate — Curve25519, ChaCha20-Poly1305 and BLAKE2s are fixed by the protocol, so the configuration with keys stripped and the peer list is the whole evidence.

Backups and file transfer — `scp`/`rsync -e ssh`/`sftp` in the job definition, or the backup product's transport setting; if a transfer rides inside the VPN as well, say so — double coverage is a point in your favour.

SMTP — The mail relay's `smtpd_tls_security_level` / `smtp_tls_security_level` (Postfix) or the provider's TLS enforcement setting (Google Workspace, Microsoft 365, SES). If the environment sends no mail, a grep of the configuration for SMTP settings returning nothing is adequate evidence.

Internal and third-party APIs — The base URLs in configuration (`https://`), certificate verification not disabled in client code (`verify=False`, `-k`, `InsecureSkipVerify` are findings), and for cloud SDKs the request signing scheme.

Why it gets sent back

  • A certificate screenshot offered as proof of configuration.
  • TLS 1.0/1.1 or RC4/3DES/CBC-only ciphers still enabled.
  • HTTP listeners that serve content without redirecting.
  • SSH with password authentication enabled and no stated reason.
  • Internal plain-text traffic not mentioned, or mentioned without the evidence that it is confined.
  • Transfer points missing from the list — the auditor will find the mail service or the backup job you forgot.

Related

/network-and-firewall-configuration · /devops-pipeline-security-hardening

Encryption and data protection

Configuration to only store and transmit encrypted representations of passwords

#

What they are actually testing

Two halves: nothing stores a password in a form that can be read back, and nothing carries one over a channel where it can be read in flight. “Encrypted representation” in storage really means a salted, slow hash (bcrypt, scrypt, Argon2, PBKDF2) or SCRAM for database roles; reversible encryption of passwords is a weaker answer that needs justification. In transit it means TLS on every login form and API, and key-based rather than password-based protocols where possible.

The auditor will also look sideways at configuration files and environment variables holding service credentials, and at logs — a password printed in a debug line fails this control regardless of how well the database hashes it.

Typically maps to: Security — credentials are protected in storage and transmission
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • For each in-scope system that authenticates users: the hashing algorithm and parameters, shown from the configuration or a hash prefix from the store — never the hashes themselves.
  • For database roles and service accounts: the engine's password storage setting.
  • Evidence that every authentication path is over TLS or SSH (HSTS, redirects, `PasswordAuthentication no`), cross-referenced to the encryption-in-transit item.
  • Where credentials for services are kept (vault, secrets manager, `.env` with restricted mode) and who can read them.
  • A check that application and web logs do not contain passwords.

Where to get it

Identity providers and SaaS — The IdP's password policy page and its statement on storage (most publish it); for self-hosted Keycloak the `password hashing` realm settings (`pbkdf2-sha256`, iterations).

Your own applications — The call site: werkzeug `generate_password_hash` (scrypt/pbkdf2), Django `PASSWORD_HASHERS`, Rails `has_secure_password` (bcrypt), Spring `BCryptPasswordEncoder`, PHP `password_hash`. Prove it from data with `SELECT username, substr(password,1,10) FROM users` — the prefix (`$2b$`, `scrypt:`, `pbkdf2:sha256:`, `$argon2id$`) identifies the scheme without exposing the hash.

Databases — PostgreSQL `SHOW password_encryption` (`scram-sha-256`) and `SELECT rolname, left(rolpassword,13) FROM pg_authid`; MySQL `default_authentication_plugin` (`caching_sha2_password`); SQL Server logins are hashed by the engine — show `LOGINPROPERTY` or the policy settings.

Basic-Auth and shared admin credentials — Acceptable only over TLS and only when the server compares against a hash (`check_password_hash`) or in constant time; a plain `==` against a value from `.env` is a finding to record and fix.

Service credentials and secrets — The secrets manager or vault in use (AWS Secrets Manager, Azure Key Vault, OCI Vault, HashiCorp Vault, a Fernet-encrypted store), or `stat -c '%a %U' .env` showing mode 600 and a named owner; the git history showing none were committed.

Logs — `grep -ci 'password='` across the last day of application, proxy and authentication logs returning zero — and the logging configuration that masks form bodies or query strings on login routes.

Why it gets sent back

  • MD5, SHA-1 or unsalted SHA-256 password hashes.
  • Reversible encryption of user passwords with no reason.
  • A login form or admin console reachable over plain HTTP.
  • Passwords visible in configuration committed to version control.
  • Credentials in logs, URLs or query strings.
  • The password policy document offered instead of the storage configuration.

Related

/security-settings · /user-management-permissions · /access-logging

Infrastructure

Configuration settings of the centralized antivirus console including the scan schedule, update settings, and update log

#

What they are actually testing

The request assumes a fleet of endpoints managed from one console, which is the world it was written for. The test behind it is simpler: malware protection exists on the systems that can run untrusted content, it is kept current, and someone would notice if it stopped. Three artefacts answer that — the schedule, the update setting, and the update log showing updates actually arriving.

Server fleets without endpoints, container platforms and hosts too small to carry a signature scanner need a different answer, and an honest one is accepted: which hosts handle user-supplied files, where those files are scanned, and what compensating controls cover the rest (immutable images, no interactive users, execution prevention). What fails is silence.

Typically maps to: Security — protection against malicious software
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The console or configuration view: scan schedule, real-time protection on/off, signature or engine update frequency and source.
  • The update log for a recent window showing successful updates at the configured frequency.
  • A recent scan log or report, including any detections and what was done with them.
  • The population covered — agents installed versus the asset list — and the systems deliberately out of scope with the reason and compensating control.

Where to get it

Endpoint platforms (Defender, CrowdStrike, SentinelOne, ESET) — The policy assigned to the in-scope group, the device list with last-seen and signature version, and the threat or detection report for the period.

Linux servers with ClamAV — `/etc/clamav/freshclam.conf` (`Checks`, mirror, `UpdateLogFile`) and `/var/log/clamav/freshclam.log` are the update settings and log; the cron line running `clamscan` or `clamdscan` over upload and data directories, plus its log, is the schedule and scan evidence. Point it at the directories where user-supplied files land, not the whole disk.

Cloud-native scanning — AWS GuardDuty Malware Protection, Azure Defender for Servers/Storage, GCP Security Command Center, OCI Cloud Guard: the enablement and target configuration, and the findings page for the period.

Containers and images — Image scanning in the registry or pipeline (Trivy, Grype, ECR/ACR/Artifact Registry scanning) covers what runs; show the policy, the last scan results, and that containers run from scanned images only.

Hosts that cannot carry a scanner — Say which (a single-core, low-memory VM is a legitimate case), scan their inbound files elsewhere — for example on the host that receives the nightly backup — and show the compensating controls: auditd on execution, no interactive users, patching evidence.

Why it gets sent back

  • A statement that antivirus is installed with no schedule, update log or coverage list.
  • Signature updates that stopped weeks before the extract.
  • Scans configured but a log showing they never ran.
  • Scope that silently omits servers because “servers don't get viruses”.
  • A detection in the report with no record of handling.

Related

/security-patch-management · /devops-pipeline-security-hardening · /os-installation-and-hardening

Infrastructure

IDS/IPS configurations for network perimeter and at critical network points, with alert notifications and an example alert

#

What they are actually testing

Three claims are tested together: detection exists where traffic enters and between zones, detections reach a person, and the whole chain has fired at least once. The “example alert” is not decoration — it is how the auditor confirms the configuration is live rather than installed. A rule set with no alert in the period raises the question of whether anything would be noticed.

Small environments rarely run a dedicated IDS appliance, and do not have to. Host-based intrusion prevention (fail2ban, CrowdSec, OSSEC/Wazuh), cloud threat detection and a reverse proxy's abuse rules together satisfy the control when the coverage is described and the alert path is shown.

Typically maps to: Security — the system is monitored for intrusions; anomalies are detected and evaluated
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The detection configuration at the perimeter (cloud threat detection, WAF/IPS on the edge, host IPS on the internet-facing hosts) and at critical internal points (the VPN hub, the database segment, the jump host).
  • The notification routing: where alerts go, to whom, by which channel, and what the response expectation is.
  • At least one real alert from the period with its timestamp, the triggering event, the notification as received, and what happened next.
  • Rule or signature update settings and the last update time.

Where to get it

Cloud threat detection — AWS GuardDuty (enablement, finding types, EventBridge rule to SNS/Slack), Azure Defender for Cloud alerts and their action group, GCP Security Command Center notifications, OCI Cloud Guard detector and responder recipes with the notification topic. Export one finding and the message it produced.

Network IDS/IPS — Suricata or Snort: the rule sources (`suricata-update` list and schedule), the interfaces monitored, the `eve.json` output and the shipper; an appliance's IPS profile and the threat log filtered to the period.

Host-based prevention — fail2ban: `jail.local` (jails, `findtime`, `maxretry`, `bantime`, `ignoreip`, the action), `fail2ban-client status <jail>` for current and total bans, and `journalctl -u fail2ban | grep Ban` for the extract; where services run in containers, the ban action must insert into `DOCKER-USER`, not `INPUT`, or it does nothing. CrowdSec and Wazuh: the collections or rules enabled and the decisions/alerts list.

Reverse proxies and WAF — ModSecurity/OWASP CRS or the cloud WAF's managed rule set on the edge, plus the rate-limit rules; the blocked-request log is the example alert if it is routed to a person.

Alert routing — The webhook, mail or push configuration (PagerDuty, Opsgenie, ntfy, Slack) and the on-call expectation; an alert that only lands in a log file is detection without notification — say so and show the plan.

Why it gets sent back

  • An IDS installed with no rules enabled or no interface attached.
  • Alerts that go only to a log file or an unread mailbox.
  • No example alert, or an example from a test with no production trigger.
  • Perimeter covered and internal critical points not mentioned.
  • Signature feeds not updated in the period.
  • fail2ban or similar whose bans do not apply to the traffic it is meant to stop.

Related

/network-and-firewall-configuration · /monitoring-logging-solutions · /incident-handling

Infrastructure

Firewall ruleset configuration

#

What they are actually testing

Four words that mean “show me every layer that decides which packets reach your systems, and prove it is as tight as your diagram claims”. The auditor compares the exported rules with the architecture and with what a scan from outside shows; the common finding is a port open at a layer nobody looked at — a cloud security list wider than the host firewall, or a container runtime publishing a port straight past the host firewall.

The ruleset is also a change-controlled object: the auditor wants to see that changes to it are recorded, which links this request to change management and to the audit-log requests.

Typically maps to: Security — network boundaries are protected; access is restricted to authorised traffic
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The rule export from every enforcement layer: cloud security groups/lists, host firewall, container runtime rules, any appliance or WAF, and the VPN's own access list (allowed IPs per peer).
  • A short description of the intended policy (what should be reachable from where) so the rules can be compared with it.
  • An external reachability check against the public addresses, dated, showing only the intended ports answer.
  • Evidence that rule changes are logged, reviewed or audited.

Where to get it

Cloud — AWS `describe-security-groups` and NACLs; Azure NSG rules (`az network nsg rule list`); GCP `gcloud compute firewall-rules list`; OCI Security Lists and Network Security Groups per subnet/VNIC — export from the console or `oci network security-list list`. Remember the cloud layer sits in front of the host and is the one most often left wide.

Linux hosts — firewalld: `firewall-cmd --list-all` per zone and `--permanent` for the on-disk state; ufw: `ufw status verbose`; raw nftables/iptables: `nft list ruleset` or `iptables -S`. Include `ss -tulpn` so the auditor can see what is listening behind the rules.

Container hosts — Docker and Podman publish ports with their own chains and bypass the host zone: export `iptables -S DOCKER-USER` and `docker ps --format '{{.Names}} {{.Ports}}'`, and show either that published ports are bound to internal addresses or that `DOCKER-USER` drops external traffic to them.

Verification from outside — `nmap -Pn -p-` or a plain TCP connect test from a host on a different network against each public address, with the date and the source. The result should match the intended policy exactly; any extra open port is a finding to fix before submission or to disclose with a date.

VPN access lists — WireGuard `AllowedIPs` per peer, OpenVPN `ccd` and `iroute`, IPsec selectors — the tunnel's own ACL is part of the ruleset when internal services are reachable only through it.

Change control — The ticket or commit for a recent rule change, and the audit trail on the firewall configuration (auditd watch, cloud audit log for security-group changes).

Why it gets sent back

  • A network diagram offered instead of the rules.
  • One layer exported (usually the host firewall) with the cloud layer omitted.
  • Rules with `0.0.0.0/0` on administrative ports and no justification.
  • No external verification, or one that contradicts the ruleset.
  • Container-published ports reachable from the internet while the host firewall looks tight.
  • A ruleset with no change history.

Related

/network-and-firewall-configuration · /network-configuration-monitoring · /infrastructure-audit

Physical and environmental

Environmental monitoring (temperature, humidity, dew point): thresholds, who is notified, an example alert

#

What they are actually testing

This is an availability control: equipment fails when it runs hot or condenses, and the auditor wants to see that someone would know before that happens. Three artefacts, exactly as the request lists them — the thresholds that are configured, the people or channel that receive a breach, and one alert that actually fired — and the third is the one that proves the first two are live.

Who answers this depends on where the equipment is. In a colocation or cloud facility the monitoring belongs to the provider, and your evidence is their attestation plus what you receive from them. On premises, or in a server room, office rack or anywhere else you run hardware you own, it is yours — including the small installations people forget, like a standby machine in a cupboard or a device on a windowsill.

Typically maps to: Availability — environmental protections are in place and monitored
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The list of locations with in-scope equipment and, per location, who is responsible for environmental monitoring (provider or you).
  • For provider-managed space: the provider's SOC 2 / ISO 27001 report section on environmental controls, your review of it, and any environmental notices received in the period.
  • For your own space: the sensor inventory, the configured thresholds for temperature, humidity and dew point (warning and critical), and the notification routing with named recipients or an on-call rotation.
  • An example alert from the period as it was received (message, time, sensor, value), with what was done about it.
  • Evidence the sensors are alive: a recent reading per sensor, and an alert for sensor silence if you have one.

Where to get it

Cloud providers — AWS, Azure, GCP and OCI do not expose environmental data for their regions. Evidence is the provider's current SOC 2 Type 2 report (AWS Artifact, Microsoft Service Trust Portal, GCP Compliance Reports Manager, OCI Compliance documents) with the physical/environmental section identified, plus your documented review of it and any service health notices for the period.

Colocation — The contract's environmental SLA, the provider's attestation, and the monitoring portal or e-mail notices they give customers; if you have your own sensors in the rack, treat them as on-premises below.

On-premises and server rooms — The monitoring system's threshold page (APC/Schneider NetBotz or PowerChute, Eaton, Vertiv, Zabbix/PRTG/LibreNMS templates for environmental sensors, SNMP OIDs on the rack PDU), the notification contacts or action group, and the alert history filtered to the period. Typical thresholds: warning at 27 °C and critical at 32 °C inlet, humidity 20–80 % RH, dew point under 17 °C — state yours and why.

Small and improvised installations — A laptop, phone or single-board machine acting as a server still has thermal sensors: `sensors` (lm-sensors), `/sys/class/thermal/thermal_zone*/temp`, `/sys/class/power_supply/*/temp`, or `dmesg` throttling messages. A cron job that reads them, compares with a threshold and sends a push or mail is a legitimate monitor if the alert is shown to work. A cheap USB or Wi-Fi temperature/humidity sensor logging to the same system covers the room.

Notification routing — The contact list or escalation policy (PagerDuty, Opsgenie, a mail distribution list with its membership, an ntfy or Slack channel with its subscribers) — named people or a rotation, with the expectation that they respond.

No alert in the period? — Trigger one deliberately under control — a sensor held briefly out of range, or a threshold lowered for a test — record it as a test with date and outcome, and put the threshold back. A documented test is accepted; silence is not.

Why it gets sent back

  • “Our provider handles that” with no provider report and no review of it.
  • Thresholds with no recipients, or recipients with no thresholds.
  • A sensor dashboard screenshot with no alert and no test.
  • Equipment locations missing from the inventory — the standby host at someone's home counts if it is in scope.
  • An alert that went to a mailbox nobody reads.
  • Sensors that have not reported in weeks.

Related

/controlled-environment · /cooling-capacity · /data-center-management · /physical-cloud-hybrid

Backup and recovery

Backup configuration for critical databases, notification settings and an example alert

#

What they are actually testing

Narrower and more specific than the general backup request: it names the databases, and it asks for three things in order — how the backup is configured, how a failure would reach a person, and proof that the path works. The example alert is the part people cannot produce, because backup scripts that have never failed have never alerted. The auditor is not asking you to have had a failure; they are asking you to show the alert mechanism is real.

“Critical databases” means you need to say which ones those are. A list that omits a store because it is “only SQLite” or “only analytics” will be questioned if that store holds data the system cannot operate without.

Typically maps to: Availability — data is backed up; backup failures are detected and acted on
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The list of critical databases with engine, host and the data each holds, and the reasoning for anything left out.
  • Per database: the backup method (dump, snapshot, continuous archiving), schedule, retention, destination, encryption, and the consistency approach (a dump or snapshot taken safely while the engine is running).
  • The notification configuration: what condition raises an alert (non-zero exit, missing run, stale backup, size anomaly), who receives it, by which channel.
  • An example alert from the period, or a documented test that produced one, with the message as received and the follow-up.
  • Where the current status is visible day to day (a dashboard, a status page, a cron-status marker) so a missed run is noticed even without an alert.

Where to get it

PostgreSQL — `pg_dump`/`pg_dumpall` in the job (safe while running — a logical dump, not a copy of the data directory), or `pg_basebackup` with WAL archiving (`archive_mode`, `archive_command`); on RDS/Azure/Cloud SQL/OCI the automated backup window, retention days and PITR setting from the console.

MySQL / MariaDB — `mysqldump --single-transaction` or `mariabackup`/`xtrabackup` in the job; binary log retention if PITR is claimed.

SQLite and embedded stores — The job must use `sqlite3 <db> '.backup <file>'` or the online backup API, or stop the writer — a plain `cp` of a live file with a `-wal` sidecar is a corrupt backup waiting to be restored. Show the command.

Home-grown scripts — The script, its cron or timer line, `set -e`/exit-code handling, and the line that reports: a `[cron-status] exit=N` marker, a healthcheck ping (healthchecks.io, Cronitor), a push (ntfy, Pushover) or mail on failure. Include the check for a run that never started — a job that crashes before its own alert line produces nothing, so something else must notice the missing marker.

Backup products — The job definition, the notification or SMTP settings page, the alert policy, and an entry from the alert history.

The example alert — If nothing failed in the period, break a backup on purpose: point the job at an unwritable destination or an invalid credential, let it run, capture the alert as received, restore the job and record the test with its date. Attach both the alert and the test note.

Verification — A recent restore of one of these backups (see the restore-test request) closes the loop; at minimum show the integrity check the job performs after writing.

Why it gets sent back

  • A backup script with no notification path.
  • Alerting only on the script's own exit code, with nothing watching for a run that never happened.
  • No example alert and no test, with “it has never failed” as the explanation.
  • A live copy of a database file taken without a consistent-snapshot method.
  • A backup destination on the same host or volume as the database.
  • Critical databases missing from the list because they are small.

Related

/backup-and-recovery · /tested-backups · /scheduled-maintenance

Change management

Evidence that rollback procedures are documented

#

What they are actually testing

Not the same as the version-control request, although people answer it with the same screenshot. That one tests that prior code exists; this one tests that somebody has written down how to get back to it — for each kind of change, including the ones git does not undo: a database migration, an infrastructure change, a configuration or secret rotation, a failover. The word “procedures” is plural and the auditor will ask about each.

What makes the evidence strong is that the procedure is specific (commands, not intentions), kept where the people who would need it at 3 a.m. can find it, and has been exercised — a rollback that was actually performed, or a drill, turns a document into a control.

Typically maps to: Security / Availability — changes can be reversed; recovery from a failed change is planned
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The rollback section of the change or release procedure, covering application deployments, database schema changes, infrastructure-as-code changes, configuration and secrets, and any failover mechanism.
  • Where the document lives and its last-reviewed date (wiki, runbook repository, README in the deployment repo).
  • Evidence that change records reference the rollback plan — a template field in the ticket or pull request, filled in for sampled changes.
  • At least one rollback or failback that was executed in the period (or a drill), with the record of what was run and the outcome.

Where to get it

Application deployments — The step in the deploy tooling: previous image tag or release kept and the command to redeploy it (`kubectl rollout undo`, Helm `rollback`, a compose file pinned to the previous tag, `git revert` plus the pipeline). Show the retention of prior artefacts so the command has something to roll back to.

Database changes — Down-migrations where the tool supports them (Alembic, Flyway undo, Rails `db:rollback`), and the honest statement that destructive migrations are rolled back by restoring the pre-change backup — with that backup step written into the procedure and its restore command.

Infrastructure-as-code — Revert the commit and re-apply (`terraform apply` of the previous state, Ansible re-run), with the note on resources that cannot be recreated non-destructively.

Configuration, secrets and certificates — The previous value or file kept (a dated `.bak` copy before every edit, the secrets manager's version history) and the command to restore it; for certificates the previous chain and the reload step.

Failover and failback — If the system has an automatic failover, the failback procedure is a rollback procedure: who may run it, what must be true first (data replicated, which copy wins), and the guard that stops it running accidentally.

Showing it is used — A ticket or PR template with a “Rollback plan” field, sampled changes with it filled in, and the record of a real rollback — the commands run, the timestamp, who ran them, the verification afterwards. A recent restore or DR drill satisfies the exercise requirement if it followed the written steps.

Why it gets sent back

  • A git history offered as a rollback procedure.
  • A procedure that covers code but not the database, infrastructure or configuration.
  • “Redeploy the previous version” with no statement of where the previous version is kept or how long.
  • A document nobody can find from the deployment tooling or the on-call page.
  • No evidence it was ever exercised, when the period contained a change that was reverted.
  • Rollback steps that depend on a person who has left.

Related

/cicd-setup · /infrastructure-as-code · /backup-and-recovery · /tested-backups · /incident-handling

Infrastructure

Every system connected to the in-scope network and the network devices (master asset inventory)

#

What they are actually testing

The inventory is the population for half the other requests — patching, logging, antivirus, access reviews all get sampled from it — so the auditor wants it first and wants it complete. “Every system connected” is deliberate: not the servers you think of as production, but everything that can talk on the in-scope network, including the VPN peers, the standby laptop, the phone acting as a backup target, the router, the managed switch and the cloud load balancer. A device that is on the network and not on the list is a finding on its own.

The second test is that the list is maintained rather than written for the audit: an owner, a last-verified date, and a reconciliation against something the network itself knows — the VPN peer table, DHCP leases, the cloud instance list.

Typically maps to: Security — assets are inventoried; the boundary of the system is known
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The master inventory: one row per asset with identifier, type, hostname and addresses (including VPN address), location or cloud region, OS or firmware, owner, role or data classification, and in-scope yes/no with the reason.
  • Network devices in the same list or a companion one: routers, switches, firewalls, VPN hubs, cloud VCNs/VPCs, subnets and security lists.
  • A reconciliation of the list against a live source — `wg show` peers, DHCP or ARP tables, the cloud provider's instance list, the monitoring system's host list — dated inside the period, with differences explained.
  • The maintenance process: who updates it, when (on change, plus a periodic review), and the last review date.

Where to get it

Cloud — `aws ec2 describe-instances` / `az vm list` / `gcloud compute instances list` / `oci compute instance list` per account and region, plus the network objects (VPCs/VCNs, subnets, security groups or lists, load balancers, gateways). Export, do not screenshot, and include stopped instances.

Self-hosted and VPN-connected — The VPN hub's peer table (`wg show`, the OpenVPN status file, the IPsec SA list) is the authoritative list of what can reach the private network — every peer public key must map to a row in the inventory. Add DHCP leases or ARP from the router for anything on the same LAN.

Network devices — Model, firmware version, management address, admin owner, and whether the device is yours or the ISP's. A home or office router carrying in-scope traffic is in scope.

Phones, laptops, single-board machines used as servers — They are assets like any other: model, OS, role (backup target, standby, monitor), address, owner, physical location.

Keeping it alive — A monitoring or dashboard host list that polls each asset doubles as the inventory check: anything the dashboard knows that the list does not, or the reverse, is the discrepancy to resolve. Store the inventory in version control (a CSV or YAML) so its history shows maintenance.

Scope decisions — For every row marked out of scope, one line on why (no in-scope data, no path to the in-scope network). Auditors accept scoping; they do not accept silence.

Why it gets sent back

  • A list of servers with no network devices.
  • Assets missing that the VPN or the cloud console plainly shows.
  • No owner, no date, no evidence of maintenance.
  • A list produced for the audit with no reconciliation to a live source.
  • Standby, test or personal devices on the in-scope network left off because they are not “production”.
  • Out-of-scope assets with no stated reason.

Related

/asset-tagging · /configuration-management-database · /network-configuration-monitoring · /infrastructure-audit

Data handling

All data disposals in the period: system disposals, hardware purging, document destruction

#

What they are actually testing

A population request: everything decommissioned, wiped or destroyed in the period, with proof that the data on it did not leave with it. The three categories are listed because people forget two of them — a decommissioned server or cloud volume is a disposal, a reused SD card or laptop is a disposal, and the box of printed records is a disposal. Zero disposals in a year is possible and is accepted when stated explicitly with the reason; a blank is not.

The evidence per disposal is the method and the proof: a wipe log or certificate, a cloud deletion record, a shredding certificate, or a note that the medium was encrypted and the key destroyed — crypto-erase is a valid method when the encryption is documented.

Typically maps to: Security / Confidentiality — data and media are disposed of securely when no longer needed
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The disposal log for the period: date, asset or medium, what data it held, method (wipe standard, physical destruction, crypto-erase, cloud deletion), who performed it, who approved it, and the proof reference.
  • The proof itself per entry: wipe tool output or certificate, vendor destruction certificate, cloud console or API deletion record, photo of destroyed media where that is the method.
  • The disposal procedure the entries follow, including the approval step and the inventory update that removes the asset.
  • The inventory diff: assets present at the start of the period and absent at the end, each matched to a disposal entry.

Where to get it

Cloud resources — Deleted instances, volumes, snapshots, buckets and database instances appear in the provider's audit log (CloudTrail, Azure Activity Log, GCP Cloud Audit, OCI Audit) with actor and time — export the delete events for the period. Note that provider-side media sanitisation is covered by their SOC 2 report, which you attach.

Disks, SSDs and removable media — `nwipe`/`shred` output or a DBAN/Blancco certificate; for SSDs the ATA Secure Erase or NVMe `sanitize` log (`nvme sanitize`, `hdparm --security-erase`) because overwrite passes do not reach every block. For an SD card or USB stick that held backups: either destroy it or show that its contents were encrypted and the passphrase retired.

Phones, laptops and reused hardware — Factory reset after file-based encryption is a crypto-erase on Android and iOS — record the reset date and that encryption was on (`ro.crypto.state`). A laptop repurposed as a standby host counts as disposal of its previous role: wipe log before reinstall.

Paper — The shredding vendor's certificate of destruction with date and weight or box count, or the in-house shredder log; for small volumes a dated note by the person who shredded is accepted.

Nothing disposed? — A signed statement for the period, with the inventory diff showing no assets left, is the evidence. Mention anything decommissioned but still held (a fenced old server kept for its data) so the auditor does not find it first.

Vendors — Where a vendor disposes on your behalf, the contract clause, the certificate per batch, and the vendor's own attestation.

Why it gets sent back

  • An empty log with no statement that nothing was disposed.
  • Disposals with a date but no method or proof.
  • Hardware “wiped” by deleting files or a quick format.
  • Cloud resources deleted with no audit-log entry to show who and when.
  • Assets that vanished from the inventory with no matching disposal.
  • Paper and removable media missing from the log entirely.

Related

/asset-tagging · /warranty-tracking · /backup-and-recovery

Change management

All changes to firewalls, security groups, routers and other network security controls in the period

#

What they are actually testing

A population request against the ruleset: every change to anything that decides which traffic is allowed, across the period, so the auditor can sample and trace each one to a request, an approval and a reason. It is the dynamic counterpart of the firewall ruleset request — that one shows the state, this one shows how it got there.

The trap is layers you do not think of as firewalls: cloud security lists, the VPN's allowed-address table, a container runtime that publishes a port, a reverse-proxy manager that opens a hostname. Each change at each layer belongs on the list, and the auditor will compare the list with the audit trail of the device or console — a change in the log that is not on your list is the finding.

Typically maps to: Security / Change management — changes to network security controls are authorised, recorded and reviewed
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The change list for the period across all layers: date, layer or device, what changed (rule added/removed/modified, with before and after), who made it, the ticket or PR, the approver, and the reason.
  • The source the list was built from, per layer: the cloud audit log export, the device's configuration change log, the version-control history, the auditd records.
  • For a sample the auditor will pick: the request, the approval before the change, and the verification afterwards (a reachability test, a monitoring check).
  • Emergency changes identified as such, with the retrospective approval.

Where to get it

Cloud security groups and lists — CloudTrail `AuthorizeSecurityGroupIngress`/`RevokeSecurityGroup*`/`ModifyNetworkAcl*`; Azure Activity Log on `Microsoft.Network/networkSecurityGroups/*`; GCP Cloud Audit `compute.firewalls.*`; OCI Audit events on `SecurityList`, `NetworkSecurityGroup` and `UpdateSecurityList`. Export the period, every event is a row.

Host firewalls and routers — firewalld/nftables/ufw: the auditd watch on `/etc/firewalld`, `/etc/nftables.conf` or `/etc/ufw` (`ausearch -k firewall-config`) and the shell history or commit of the change; appliances: the configuration change log or `show archive log config`, with the admin account.

VPN access control — Changes to peers and allowed addresses are network security changes: the dated backup copies of the VPN configuration (`wg0.conf.bak-<date>`), the auditd watch on the directory, the failover script's log that rewrote the peer table — each with who and why.

Container and proxy layers — A port newly published in a compose file, a proxy host added in nginx-proxy-manager (its `audit_log` table), a WAF rule changed — these come from version control and the tool's own audit log respectively.

Infrastructure-as-code — If the rules live in Terraform or similar, the merged pull requests touching the security resources are the population, with reviewer and merge time; the apply log is the deployment record.

Building the list — Merge every source into one table, then reconcile against the current ruleset: start-of-period state plus the changes should equal the end-of-period state. A difference means an unrecorded change — find it before the auditor does.

Why it gets sent back

  • Only one layer covered, usually the cloud console, with host, VPN and proxy changes omitted.
  • Changes with no requester, approver or reason.
  • A list that does not match the device's own change log.
  • Approval timestamps after the change with no emergency designation.
  • “No changes in the period” contradicted by dated configuration backups or failover logs.
  • Before/after state missing, so nobody can tell what the change opened.

Related

/network-and-firewall-configuration · /network-configuration-monitoring · /measure-before-you-change · /cicd-setup

Access control

Account lockout settings for in-scope production servers

#

What they are actually testing

The server version of the lockout request, and the answer is different from the application one because on a well-run server the strongest evidence is that there is nothing to lock out: password authentication is disabled and access is by key, certificate or an identity-aware proxy. The auditor is testing that repeated failed logins cannot continue indefinitely; a daemon that never accepts a password satisfies that more completely than any threshold.

Where passwords do exist — console access, local accounts, Windows, a sudo password — the lockout policy has to be shown and has to match your own policy numbers. Host intrusion prevention (fail2ban, CrowdSec) is accepted as the lockout mechanism for network logins when its jail covers the service and the ban actually applies.

Typically maps to: Security — logical access; brute-force attempts against servers are limited
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Per server class: the effective authentication methods (key-only, password, both) and, where passwords are possible, the lockout threshold, lockout duration and reset window.
  • The configuration source for each: `sshd -T`, PAM faillock/tally, Windows account lockout policy, the IPS jail definition.
  • The population of in-scope servers, so the auditor sees the setting is fleet-wide and not from the one host you exported.
  • Evidence it works: a recent lockout or ban record, or a controlled test with its log lines.

Where to get it

Linux — SSH — `sshd -T | grep -iE 'passwordauthentication|kbdinteractive|permitrootlogin|maxauthtries|logingracetime'` — effective values, not the file. `passwordauthentication no` is the headline; `MaxAuthTries` and `LoginGraceTime` are the per-connection limits the auditor will still want to see.

Linux — local passwords and sudo — PAM: `/etc/security/faillock.conf` (`deny`, `unlock_time`, `fail_interval`) and the `pam_faillock` lines in `/etc/pam.d/system-auth`/`common-auth`; `faillock --user <name>` shows current counters. If there is no console or local password login in practice, say so and show the account listing with locked passwords (`passwd -S`).

Host intrusion prevention — fail2ban `jail.local` for the `sshd` jail: `maxretry`, `findtime`, `bantime`, `ignoreip`; `fail2ban-client status sshd` with the banned count, and `journalctl -u fail2ban | grep Ban` as the extract. CrowdSec: the enabled scenarios and `cscli decisions list`.

Windows — `net accounts` or the Group Policy result (`gpresult /h`) for Account lockout threshold, duration and reset counter; event ID 4740 in the Security log as the extract.

Cloud consoles and bastions — Where the only path to a server is an identity-aware proxy, SSM Session Manager, Azure Bastion, IAP or an OCI Bastion session, the IdP's lockout policy is the control — reference the application lockout evidence and show the server has no other inbound path.

Phones and appliances as servers — dropbear or other minimal daemons: show key-only (`-s` flag, no `/etc/passwd` password), the bind address limited to the VPN, and that the VPN itself is key-based.

Why it gets sent back

  • A lockout number from the policy document with no configuration behind it.
  • `sshd_config` file contents when a `sshd_config.d` drop-in overrides them — use `sshd -T`.
  • Password authentication enabled with no lockout mechanism and no reason.
  • fail2ban installed with no jail for the service in question, or bans that do not apply to the traffic path.
  • One host's settings offered for a fleet.
  • Root login permitted with a password.

Related

/security-settings · /os-installation-and-hardening · /access-logging

Access control

Account lockout settings for in-scope production databases

#

What they are actually testing

The database version, and the awkward one, because most open-source engines have no account lockout at all. The auditor knows this. What they are testing is whether you know it, and what you did about it: either the engine's own policy where one exists, or a compensating set of controls — the database is unreachable except from named hosts or a private network, direct human login is rare and goes through something that does lock out, and failed logins are logged and watched.

A database that answers on a public address with password authentication and no lockout is the finding this request exists to surface.

Typically maps to: Security — logical access; brute-force attempts against databases are limited
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • Per database engine: the native lockout policy if the engine has one, with the threshold and duration.
  • Where it has none: the reachability controls (bind address, firewall or security group, VPN-only path, `pg_hba.conf` host rules), the authentication method, and the failed-login logging with evidence someone watches it.
  • The role listing with how each role authenticates (password, certificate, IAM, local socket), so the auditor can see which accounts a lockout would even apply to.
  • For managed cloud databases: the provider-level setting (IAM authentication, password policy) and the network access configuration.

Where to get it

PostgreSQL — No native lockout. Evidence is `pg_hba.conf` (which hosts and methods are allowed — `scram-sha-256` from the application subnet only, `reject` elsewhere), `listen_addresses`, the network rule in front, `log_connections = on` with failed attempts visible in the log, and the extension or proxy that adds lockout if you have one (PgBouncer in front does not; a `fail2ban` jail on the Postgres log does).

MySQL / MariaDB — MySQL 8: `connection_control` plugin (`connection_control_failed_connections_threshold`, `_min_connection_delay`) and `FAILED_LOGIN_ATTEMPTS` / `PASSWORD_LOCK_TIME` per account (8.0.19+); MariaDB: `max_password_errors`. `SHOW VARIABLES LIKE 'connection_control%'` and `SELECT user, host, User_attributes FROM mysql.user`.

Oracle Database — The profile assigned to each account: `SELECT profile, resource_name, limit FROM dba_profiles WHERE resource_name IN ('FAILED_LOGIN_ATTEMPTS','PASSWORD_LOCK_TIME')` and `SELECT username, profile, account_status FROM dba_users`.

SQL Server — Logins with `CHECK_POLICY = ON` inherit the Windows lockout policy: `SELECT name, is_policy_checked, is_disabled FROM sys.sql_logins`, plus the server's authentication mode (Windows-only is the stronger answer).

Managed cloud databases — RDS/Aurora and Cloud SQL: IAM database authentication enabled and the security group or authorised networks; Azure SQL: Entra-only authentication; OCI Autonomous Database: the access control list / private endpoint and the `DEFAULT` profile's `FAILED_LOGIN_ATTEMPTS`.

SQLite and embedded stores — No accounts, no network, no lockout: show the file permissions and that a single application process is the only reader — the application's own login lockout is the relevant control.

Why it gets sent back

  • “Not supported by the engine” with no compensating control described.
  • A database reachable from outside the private network with password authentication.
  • `pg_hba.conf` with `host all all 0.0.0.0/0 md5`.
  • Lockout configured on the application but direct database logins by humans unaccounted for.
  • Failed logins not logged, so nobody could notice a brute-force attempt.
  • The role listing missing, so the auditor cannot tell which accounts have passwords at all.

Related

/security-settings · /network-and-firewall-configuration · /access-logging

Access control

Password or authentication settings for in-scope production databases, MFA if applicable

#

What they are actually testing

The database counterpart of the application authentication request, and the one where “MFA if applicable” most often means “not directly applicable, so show the path that has it”. Database engines rarely do MFA themselves; the auditor accepts MFA on the only route to the database — the VPN, the bastion, the SSO in front of the admin tool, the cloud IAM — provided you show that no other route exists.

The settings being tested: how each role authenticates, how passwords are stored and whether a policy (length, expiry, reuse) exists where the engine supports one, that superuser or admin accounts are not shared and not used by applications, and that the network path is restricted.

Typically maps to: Security — logical access; users are authenticated before access to data
Indicative only — your own control matrix and agreed scope decide this.

What to produce

  • The role or user listing per database with authentication method, privilege level (superuser/admin or not), last password change where available, and whether the account is human or service.
  • The authentication configuration: allowed methods per host (`pg_hba.conf`), password storage algorithm, password policy parameters where the engine has them, certificate or IAM authentication settings.
  • The MFA evidence for the human path: the VPN or bastion's MFA, the IdP policy on the SQL client or admin console, with the statement and network evidence that there is no path without it.
  • Separation: that applications use their own least-privilege roles and that superuser passwords are held in a vault with restricted access.

Where to get it

PostgreSQL — `pg_hba.conf` (methods per source — `scram-sha-256`, `cert`, `peer` for local), `SHOW password_encryption` (`scram-sha-256`), `SELECT rolname, rolsuper, rolcanlogin, rolvaliduntil FROM pg_roles`, and `ssl = on` with `hostssl` rules if clients are remote. PostgreSQL has no password complexity policy without `passwordcheck` — state that and lean on the IdP or vault that generates the passwords.

MySQL / MariaDB — `default_authentication_plugin` (`caching_sha2_password`), `validate_password` component settings, `default_password_lifetime`, and `SELECT user, host, plugin, password_expired FROM mysql.user`.

Oracle Database — `dba_profiles` password resources (`PASSWORD_LIFE_TIME`, `PASSWORD_VERIFY_FUNCTION`, `PASSWORD_REUSE_MAX`), `dba_users` with profile and `authentication_type`, and whether external or Kerberos/RADIUS authentication (which carries MFA) is configured.

SQL Server — Authentication mode (Windows-only preferred), `sys.sql_logins` with `is_policy_checked` and `is_expiration_checked`, and Entra ID authentication for Azure SQL, which brings the tenant's MFA.

Managed cloud — RDS/Aurora IAM authentication, Cloud SQL IAM database authentication, Azure SQL Entra-only, OCI Autonomous Database with IAM or Kerberos — the setting plus the IAM policy and MFA condition on the role that may connect.

MFA on the only path — If humans reach the database through a VPN or bastion: the VPN's MFA policy or key-based peer list, the bastion's IdP MFA, and the network evidence (bind address, security group, `pg_hba` host rules, an external port test) that the database answers nowhere else. Admin web tools in front of the database (pgAdmin, phpMyAdmin, a proxy manager) need their own MFA or SSO shown.

Service accounts — One role per application, no superuser, password held in the application's secret store with mode and owner shown, rotation date if the policy requires it.

Why it gets sent back

  • The application's authentication evidence offered for the database.
  • `md5` password storage, or `trust` in `pg_hba.conf` for remote hosts.
  • Applications connecting as superuser or `root`.
  • MFA “not applicable” with no evidence that the only path to the database has it.
  • A shared admin account with no record of who holds the password.
  • Role listing without privilege level, or missing the service accounts.

Related

/security-settings · /user-management-permissions · /network-and-firewall-configuration

A note on what this is

This is a preparation aid written from the perspective of the people who operate the systems being audited. It is not a control framework, not authoritative, and not a substitute for your practitioner. The criteria mappings are orientation; the actual scope, control objectives and evidence expectations are agreed with your audit firm, and two organisations answer the same request differently. See SOC 2 for what the examination is, and the AICPA SOC suite for the authoritative definitions.

API

The same library as JSON, for scripting:

curl -s 'https://devops.majbase.com/soc2-evidence/api?q=restore+test'
curl -s 'https://devops.majbase.com/soc2-evidence/api/auth-settings-prod-apps'