The uncomfortable statistic in security is not how often companies are breached. It is how long it takes them to notice. Intrusions routinely sit undetected for weeks or months, which gives an attacker time to look around, move between systems and take what they want at a pace that attracts no attention.
This is the last of three articles. The first covered filtering traffic at the edge. The second covered arranging AWS so a single failure stays contained. This one is about noticing, and about the habits in code that stop you needing to notice in the first place.
Turn on threat detection and actually read it
AWS GuardDuty watches the signals your account already produces, such as API calls, network flows and DNS queries, and flags patterns that suggest trouble. Credentials being used from a location they have never been used from. An instance talking to an address associated with malware. Reconnaissance against your account. Unusual data access.
Two things matter about it. The first is that it needs enabling, which takes minutes. The second is that its findings need to reach a person, because an alert nobody reads is the same as no alert at all. Route findings to a channel your team actually watches, and decide in advance who looks at them.
Where files are uploaded by users, malware scanning belongs in the path. Any site that accepts attachments, documents or images is a potential distribution point, and scanning on upload keeps an infected file from being stored and later served to someone else.
Log the things you will wish you had logged
After an incident, the questions are always the same: what did they reach, when did it start, is it still happening, what left the building. Those questions are answerable only if the right logging was already on, because nobody can enable it retroactively.
The baseline we set up:
- VPC flow logs, recording which network conversations were attempted and whether they were accepted. This is what tells you whether anything actually reached your database tier.
- API audit logging, recording every action taken against the cloud account and by which identity. This is how you reconstruct what a compromised credential did.
- Application and access logs centralised in CloudWatch, rather than sitting on a server that may be the thing you are investigating.
- Alarms on the handful of events that are never routine: root account use, security group changes, failed logins at volume, unusual spend.
That last point is the one most often skipped. Logs you never look at only help after the fact. Alarms on a small number of genuinely abnormal events are what turn a log into a warning. The temptation is to alert on everything, which produces noise, which teaches everyone to ignore it.
Keep secrets out of code
Moving from infrastructure to the application itself, the single most common serious mistake is a credential committed to a repository. Database passwords, API keys, tokens. Usually added during a rush, usually never removed.
Git keeps history, so deleting the line later does not remove it. Anyone with repository access, now or in the future, can retrieve it. Automated scanners watch public repositories constantly, and a key committed to a public repository should be considered compromised within minutes.
Keep credentials in a managed secret store and read them at runtime. Keep configuration files out of version control. Use scanning in your pipeline to catch anything that slips through, because eventually something will.
Validate what comes in, encode what goes out
Most application vulnerabilities still come down to trusting input. SQL injection, cross-site scripting, command injection and path traversal are all variations of the same mistake: data from a user ending up somewhere it is treated as an instruction.
The defences are well established and worth repeating because they are still missed:
- Parameterised queries, always. Never build SQL by joining strings together with user input, no matter how harmless the input looks.
- Validate on the server, whatever the browser does. Client-side validation is a convenience for users and no obstacle at all to an attacker.
- Encode output for its context. The same string needs different treatment in HTML, in an attribute, in JavaScript and in a URL.
- Check authorisation on every request. Hiding a button does not protect the endpoint behind it. The server decides whether this user may perform this action on this record, every time.
That last one deserves emphasis. Broken access control, where one user can reach another user data by changing an identifier in a request, is consistently among the most common serious findings in web applications, and it is entirely invisible from the outside until somebody tries.
Keep dependencies current
A modern application rests on hundreds of packages written by other people. When a vulnerability is published in one of them, the details become public at the same moment, and automated scanning for unpatched versions begins almost immediately.
Running an unmaintained framework version is a decision, whether or not anyone made it deliberately. Automated dependency alerts, a regular update cadence and a test suite you trust enough to update confidently are what keep this manageable rather than terrifying.
Use AI to audit your own code, with your eyes open
The tooling that made attacks cheap is just as useful on defence, and it is the one place where the current wave of AI genuinely helps a small team punch above its weight. Pointed at a codebase, a capable model will find string-built SQL queries, endpoints missing an authorisation check, credentials sitting in configuration files, unsafe handling of uploads and a long tail of small mistakes that a human reviewer skims past at four in the afternoon.
We use AI-assisted review as a standing part of our process, and it earns its place. It never gets bored, it reads every file rather than the ones that looked interesting, and it is good at the repetitive pattern matching that security review mostly consists of.
Three limits are worth knowing before you rely on it:
- It is confidently wrong sometimes. It will flag safe code as vulnerable and occasionally wave through something that matters. Findings are a list to investigate, not a verdict.
- It does not know your business rules. It can see that an endpoint checks authorisation. It cannot know whether this particular role should be able to see this particular record. Access control logic still needs a human who understands the domain.
- It must never be handed production credentials or real customer data. Review the code, not the live system. Scrub configuration before it goes anywhere near a prompt.
Treat it as a reviewer that never gets tired, not an auditor that signs anything off.
If a customer asks you about SOC 2
As soon as you start selling to larger organisations, the security questionnaire arrives, and somewhere in it is a question about SOC 2. It is worth understanding what is actually being asked.
SOC 2 is an independent audit, carried out by a licensed firm, against criteria covering security, availability, processing integrity, confidentiality and privacy. It is not a certificate you buy or a checklist you self-assess. An auditor examines your controls and your evidence that those controls operated over a period of time.
To be clear about where we stand: Logic Providers is a development and technology consulting company. We are not auditors, we do not issue attestations, and we hold no certification of our own. What we can tell you is how the engineering side relates to it.
The controls an audit asks about are, to a striking degree, the ones described across these three articles: restricted network access, encryption in transit and at rest, least privilege, credentials that are not long-lived secrets, separated environments, change management through a reviewed deployment pipeline, logging that is retained, and monitoring that produces alerts someone acts on.
Teams that build this way from the start find an audit is largely an exercise in gathering evidence they already generate. Teams that bolt it on afterwards find it expensive and slow, because the evidence does not exist for a period that has already passed and the architecture has to change first. If a compliance requirement is anywhere on your roadmap, the cheapest moment to build like this is before anyone asks.
Security is a practice, not a project
The pattern across all three articles is layering. Filter at the edge so less arrives. Arrange the infrastructure so a breach stays contained. Detect so you learn about it in hours rather than months. Write code that does not hand over the keys. No single layer is sufficient and each one buys time for the others.
It is also ongoing. New vulnerabilities appear, infrastructure drifts as teams change it, and the cost of attacking continues to fall as tooling improves. The work is in reviewing periodically rather than hardening once and assuming it holds.
Logic Providers builds and operates web platforms with these practices as the default rather than an upgrade. If you would like a review of your current setup, get in touch.