Security and QA ยท first published on Medium, 13 April 2026
Security Guarantees in Everyday Testing
What this is about
This article is not about penetration testing or advanced security techniques.
It is about something more fundamental. It is about how everyday testing and design decisions define whether a system is safe to use, not just whether it works.
The value for you as a reader is simple. You will learn how to identify security risks early, shape better API contracts, and prevent issues before they become expensive to fix.
Security, in this context, is not a separate discipline. It is a set of guarantees that must exist in every flow.
Security is not a separate lane
Security is often treated as a specialist activity, something reviewed later by a dedicated team or tested through a separate exercise. That is too late for many of the issues that matter most.
By the time a flaw reaches system testing or a formal security review, the contract is already defined, the implementation is already shaped, and the cheapest design decisions have already been missed.
This does not mean every tester must become a security expert. It means security concerns must be built into normal product thinking, requirement review, design review, and everyday testing.
A strong engineering organisation does two things at once. It brings in security specialists early, and it expects product, engineering, and quality to think clearly about misuse, abuse, and unsafe assumptions from the start.
What this looks like in practice
In most teams, a functional question sounds like this: Can the user perform the action successfully?
A security-aware question sounds different: should this user be allowed to perform this action at all, on this data, using this input, under these conditions?
That shift sounds small, but it changes the system before code is written. It affects API contracts, field definitions, ownership rules, validation strategy, and error handling.
This is why security should appear at the requirement and design stages, not only in execution. If the guarantees are unclear at the start, tests will only discover symptoms later.
A practical example
Consider an API for updating payout details.
At first glance, the feature seems straightforward. A client sends a request to update payout information for a user account. The happy path is simple, and many teams would move quickly to implementation.
But this type of flow carries several risks that are often overlooked when the focus stays purely functional.
Imagine a request like this.
PATCH /payout-details
{
"user_id": "user_123",
"account_id": "acc_789",
"session_token": "token_abc",
"bank_account": "GB12BARC20000012345678",
"nickname": "Primary account"
}
From a functional perspective, the questions are obvious.
Does the update succeed? Is the data stored correctly? Does the response return the updated values?
From a security perspective, the important questions are different.
What guarantees that the session_token belongs to the same user as the user_id? What guarantees that account_id also belongs to that same user? What happens if one identifier is valid and another is manipulated? What prevents a user from updating another person's payout details by changing only one field? What prevents unsafe content from entering a free text field, such as a nickname?
These are not edge cases. These are standard failure modes.
The design problem behind the API
The weakness in flows like this is often not a missing test. It is an incomplete contract.
If an API accepts multiple user-linked identifiers and trusts the client to keep them aligned, the system is already exposed. If a token proves identity, but the request body can point to someone else's data, the design is weak before implementation begins.
The correct response is not to write more tests around a weak shape. The correct response is to improve the contract.
A stronger design would remove unnecessary identifiers and derive identity from the authentication layer.
For example, instead of blindly trusting a session token, the system should validate a signed token, such as a JWT, extract the user identity from it, and ignore any conflicting identifiers provided by the client.
If an identifier, such as an account_id in our example, must be supplied, the system must enforce internal ownership validation and explicitly reject any mismatch.
Free text fields, such as nickname, should not accept arbitrary input. They should enforce strict formats, length limits, and allowed character sets to prevent injection or misuse.
At the same time, the system must be careful in its responses. Error responses should not expose whether a specific account exists or belongs to another user. A generic authorization failure is often safer than a detailed explanation that can be abused.
Making the system observable
Even with a strong contract, misalignment attempts will happen. These can result from bugs, integration errors, or malicious behaviour.
The system must therefore be observable. Any mismatch between identifiers, invalid ownership attempts, or repeated unauthorized actions should be logged, monitored, and surfaced.
This is not only about security. It is also about product quality. These signals often reveal integration issues or incorrect assumptions made by clients. A secure system does not just prevent unsafe actions. It also makes them visible.
What QA should do before development starts
This is where quality adds real value. Before implementation starts, QA should review the requirements and ask direct questions.
What proves identity? What proves ownership? Which fields are client-controlled, and which should be derived by the system? Which inputs are too permissive? Which assumptions are being made across services? What should happen when authentication is valid but authorization fails?
These questions should not stay inside a QA document. They should be discussed openly with engineering, product, and security.
If a security team exists, involve them early. They should review requirements, challenge unsafe assumptions, and help identify risks before they harden into code. They should also review the test concerns and cases.
This is not a handoff. It is a collaboration.
What testing should validate later?
Once the implementation is in place, the tests should validate the guarantees agreed upon earlier.
Using the payout details example, that means verifying that authentication is always enforced, that authorization is consistent, that mismatched identifiers are rejected, and that no unsafe input is accepted.
It also means verifying that responses do not leak sensitive information and that repeated invalid attempts are handled consistently.
This is where normal testing skills matter. A good tester already considers edge cases, hidden assumptions, and real user behavior. The same mindset applies here.
You do not need to be a security specialist
A tester is not expected to replace a security engineer, just as a security engineer is not expected to replace product, architecture, or quality. But not being a specialist is not a reason to ignore obvious risks.
If you can think like a user, question assumptions, follow data across system boundaries, and spot where one field can contradict another, you already have part of the skill set needed to improve security outcomes.
What leadership should understand
Security defects are often expensive, not because they are technically complex, but because they were allowed into the design unnoticed.
A team that brings security thinking into requirements, design, and routine testing will usually implement safer systems faster and more cheaply than a team that treats security as a late-stage review.
That is not because they test more. It is because they ask better questions earlier.
Closing thought
Security should not appear only when something looks obviously dangerous. It should be part of how everyday flows are shaped, discussed, and validated.
The best time to catch a security issue is before it becomes code. The next best time is before it reaches customers.
That is why security belongs within normal-quality thinking, not beside it.
Want a guardrailed assistant like this on your site, or AI automation with the same rigour?