Skip to main content

Security testing cheatsheet

Learn to test software the way an attacker would — safely, legally, and so developers can fix what you find. Examples use OWASP Juice Shop, an app built to be broken into for practice, running on your own machine. Each section has three parts:

  • In short — the idea in one sentence.
  • Example — a real request against the practice app, with its real response.
  • Try it — a small exercise.
Only test what you are allowed to test

Every technique here is for systems you own or have written permission to test. Running these against someone else's system can be a crime, even with good intentions. The examples target OWASP Juice Shop (deliberately vulnerable, made for this) on localhost. See permission and scope before anything else.

📖 Full guide: Security testing → Intermediate
How to use this page

Read permission and scope first — always. Then start the practice app and go through Part 1 (the ideas and the baseline checks any tester can do). Part 2 is the main web risks. Part 3 is scanners, deeper testing and reporting. The verified results shown are from Juice Shop v20.2.0; the app changes, so your details may differ.

Contents​

Permission & scope · Practice app

Part 1 — Beginner: What security testing is · Think like an attacker · OWASP Top 10 · HTTP & auth recap · Security headers · Reading errors · Baseline checks

Part 2 — Core: Injection & SQLi · Broken access control · Authentication · XSS · Secrets in code · Vulnerable dependencies · Business logic · Common mistakes

Part 3 — Advanced: Automated scanning (ZAP) · SAST & dependency scans in CI · Burp Suite · API & cloud · CVSS & severity · Reporting · Words you'll meet

Permission & scope​

In short: get written permission and a clear scope before you touch a system — this is the difference between security testing and a crime.

A rules of engagement document should say:

ItemExample
In scopestaging.example.com, the mobile app build 4.2
Out of scopeProduction, third-party payment pages, employees (no phishing)
AllowedAutomated scanning (rate-limited), manual testing, test accounts
Not allowedDenial-of-service, deleting data, social engineering
WindowMon–Fri, 09:00–18:00, specific dates
ContactsWho to call if something breaks; how to report criticals fast

For learning, use legal targets built for it: OWASP Juice Shop, the PortSwigger Web Security Academy (free), OWASP WebGoat, DVWA, Hack The Box, TryHackMe.

Practice app​

In short: OWASP Juice Shop is a full shop with dozens of planted vulnerabilities and a hidden scoreboard that tracks the ones you find.

docker run --rm -d -p 3001:3000 --name juice bkimminich/juice-shop
# open http://localhost:3001 (find the hidden /#/score-board)
curl -s localhost:3001/rest/admin/application-version # {"version":"20.2.0"}

Stop it with docker rm -f juice when you're done.

Part 1 — Beginner​

1. What security testing is​

In short: security testing looks for ways the system can be misused — reading data you shouldn't, acting as someone else, or breaking it — and reports them with a fix.

KindWhat it isWho does it
Baseline checksHeaders, auth, obvious flaws during normal QAAny tester
SAST / SCA scansTools read code and dependenciesAutomated, in CI
DAST scansTools attack the running appAutomated + tester
Penetration testSkilled manual attack, time-boxedSpecialist
Secure code reviewManual review of risky codeSpecialist / senior dev

This page focuses on the baseline checks every tester should do, plus the scanners and the main risks — enough to add real value on any team.

Try it: open Juice Shop and find the hidden scoreboard (/#/score-board). It lists the challenges — your practice checklist.

2. Think like an attacker​

In short: for each feature, ask what an attacker wants and how they'd get it — then test that path.

A quick threat model (STRIDE) for a login form:

ThreatQuestion
SpoofingCan I log in as someone else?
TamperingCan I change data I shouldn't (price, role)?
RepudiationAre actions logged so they can't be denied?
Information disclosureDoes it leak data or internal details?
Denial of serviceCan I make it slow or crash?
Elevation of privilegeCan a normal user become admin?

Where the valuable data is (accounts, payments, personal data) is where to spend your time.

3. OWASP Top 10​

In short: the OWASP Top 10 is the industry list of the most common serious web risks — a great coverage checklist. Check owasp.org/Top10 for the current edition.

Risk themeOne-line check
Broken access controlReach other users' data/actions by changing an id
Cryptographic failuresSensitive data not encrypted; HTTP; weak hashing
InjectionInput runs as SQL, commands, or script (XSS)
Insecure designThe logic itself is unsafe (refund > payment)
Security misconfigurationVerbose errors, default settings, missing headers
Vulnerable componentsOld libraries with known CVEs
Identification & auth failuresWeak login, sessions, MFA bypass
Software & data integrityUnsigned updates, unsafe deserialization
Logging & monitoring failuresAttacks go unnoticed
Server-side request forgeryServer fetches attacker-controlled URLs

Juice Shop has challenges for every one of these.

4. HTTP & auth recap​

In short: security testing lives in the HTTP request — method, headers, body, cookies — so you must be comfortable reading and changing it.

Key status codes for security: 401 (not authenticated), 403 (authenticated but not allowed), 500 (server error — often leaks details).

Tokens: modern apps use a JWT (JSON Web Token) — three base64 parts (header.payload.signature) sent as Authorization: Bearer <token>.

J=http://localhost:3001
# a normal login returns a JWT
curl -s -X POST $J/rest/user/login -H 'Content-Type: application/json' \
-d '{"email":"test@test.com","password":"wrong"}' -w " [%{http_code}]\n" | head -c 80
# [401] ← wrong credentials, as it should be

New to this? See the API testing cheat sheet, sections 1–9.

5. Security headers​

In short: a few response headers block whole classes of attack — their absence is an easy, real finding.

Real result on Juice Shop:

curl -s -I http://localhost:3001/ | grep -iE 'content-security-policy|strict-transport|x-frame-options|x-content-type-options'
# X-Content-Type-Options: nosniff
# X-Frame-Options: SAMEORIGIN
# (no Content-Security-Policy, no Strict-Transport-Security)
HeaderBlocksJuice Shop
Content-Security-PolicyCross-site scripting, data injection❌ missing — a finding
Strict-Transport-SecurityDowngrade to HTTP❌ missing
X-Frame-Options / CSP frame-ancestorsClickjacking✅ SAMEORIGIN
X-Content-Type-Options: nosniffMIME sniffing✅ present
Cookie flags Secure, HttpOnly, SameSiteCookie theftCheck per cookie

Try it: check the headers of a site you own. Which are missing?

6. Reading errors​

In short: error messages and 500s often leak the database type, queries, file paths or stack traces — useful to an attacker, and a finding on their own.

Real result — a broken input in Juice Shop's search:

curl -s "http://localhost:3001/rest/products/search?q=test')--" -w " [%{http_code}]\n" | head -c 90
# <title>Error: SQLITE_ERROR: incomplete input</title> [500]

That one line tells an attacker the database is SQLite and that user input reaches the SQL query — the door to SQL injection (next part). Production apps should return a generic error and log the detail server-side.

7. Baseline checks​

In short: without any special tools, every tester can run a short security baseline during normal testing.

[ ] HTTPS everywhere; HTTP redirects to HTTPS
[ ] Security headers present (section 5)
[ ] Wrong login is rejected (401) with a generic message (no "user not found")
[ ] Logout ends the session; Back button doesn't show private pages
[ ] Change an id in a URL/request → you can't see others' data (section 9)
[ ] Enter ' " < > in fields → no 500, no script runs, no SQL error
[ ] Errors are generic (no stack traces, no SQL, no file paths)
[ ] Sensitive data isn't in the URL, page source, or localStorage
[ ] Cookies have Secure, HttpOnly, SameSite

Try it: run this baseline on Juice Shop. You'll already find several issues.

Part 2 — Core​

8. Injection & SQLi​

In short: injection happens when input is treated as code; SQL injection (SQLi) is the classic — input changes the meaning of a database query.

Real result — the famous Juice Shop login bypass:

J=http://localhost:3001
curl -s -X POST $J/rest/user/login -H 'Content-Type: application/json' \
-d '{"email":"'"'"' OR 1=1--","password":"anything"}' \
| python3 -c "import json,sys;a=json.load(sys.stdin)['authentication'];print('logged in as', a['umail'])"
# logged in as admin@juice-sh.op

The email ' OR 1=1-- turns the login query into "match any row", so the app logs you in as the first user — the admin — with no password. The fix is parameterised queries (the database treats input as data, never code), plus input validation.

To test: put ', ", ' OR 1=1--, 1;-- into fields and parameters, and watch for 500s, SQL errors, or unexpected success.

Try it: try the same payload in the email field of Juice Shop's login page in the browser.

9. Broken access control​

In short: the most common serious flaw — a logged-in user reaches data or actions that should be someone else's, usually by changing an id (BOLA / IDOR).

Real result — one token reads several users' baskets:

J=http://localhost:3001
TOKEN=$(curl -s -X POST $J/rest/user/login -H 'Content-Type: application/json' \
-d '{"email":"'"'"' OR 1=1--","password":"x"}' | python3 -c "import json,sys;print(json.load(sys.stdin)['authentication']['token'])")
for id in 1 2 3; do curl -s -o /dev/null -w "basket $id -> %{http_code}\n" "$J/rest/basket/$id" -H "Authorization: Bearer $TOKEN"; done
# basket 1 -> 200
# basket 2 -> 200
# basket 3 -> 200

Each basket belongs to a different user, yet one token reads them all — there's no check that the basket belongs to the caller. The fix: check ownership on the server for every object (basket.userId == currentUser.id).

To test: log in as user A, then request user B's ids on every endpoint that takes one — orders, baskets, messages, documents, profile. Also try admin endpoints as a normal user (function-level access control).

Not everything is broken — GET /api/Users returns 401 without a token, which is correct. Report the failures, note the passes.

10. Authentication​

In short: test how logins, sessions and tokens can be abused — weak passwords, no rate limit, tokens that never expire, resettable by anyone.

CheckHow
Rate limitingMany wrong logins fast → lockout or delay, or brute force is possible
Generic errors"Invalid email or password", never "no such user" (which reveals valid emails)
Password rulesWeak passwords accepted? Common-password list used?
Token expiryDoes an old JWT still work hours later?
Token contentsDecode the JWT payload — any sensitive data? Is the signature checked?
Password resetCan you reset someone else's? Predictable reset tokens? Security-question guessing
MFACan it be skipped by calling the API directly?
# decode a JWT payload (test tokens only — never paste real tokens into websites)
echo "<JWT>" | cut -d. -f2 | base64 -d 2>/dev/null; echo

Try it: in Juice Shop, decode the admin token from section 9. What's in the payload?

11. XSS​

In short: cross-site scripting (XSS) is injection into a web page — attacker-controlled script runs in another user's browser, stealing sessions or acting as them.

TypeWhere the payload lives
ReflectedIn the request (URL, form), echoed straight back
StoredSaved (comment, name) and shown to others later — the most dangerous
DOM-basedJavaScript writes untrusted input into the page

Real result — Juice Shop reflects search input into the response body:

curl -s "http://localhost:3001/rest/products/search?q=%3Ciframe%3E" | grep -o "iframe" | head -1
# iframe ← the tag comes back in the response

Whether it executes depends on how the page renders it; that reflection is the signal to test further. A classic test payload is <img src=x onerror=alert(1)>. The fix: escape output for its context (HTML, attribute, JS, URL) and set a Content-Security-Policy (the header missing in section 5).

warning

Only run XSS payloads on apps you're authorised to test. alert(1) is a harmless proof; never use real attack payloads against systems you don't own.

12. Secrets in code​

In short: API keys, passwords and tokens committed to a repo are one of the easiest and most damaging leaks — scan for them.

Where they hideFind them with
Source files, configgitleaks detect, GitHub secret scanning
Git history (even if deleted later)gitleaks detect scans all commits
Front-end bundles, mobile appsSearch the built files; anything shipped to a client is public
Docker images, CI logsTrivy secret scan; review pipeline output
docker run --rm -v "$PWD:/repo" zricethezav/gitleaks:latest detect --source=/repo --no-banner

A secret in Git history is compromised even after you delete the line — rotate it (change the key), don't just remove it.

13. Vulnerable dependencies​

In short: most code in an app is third-party libraries; known vulnerabilities in them (CVEs) are found automatically and often the easiest thing to fix.

npm audit --audit-level=high            # Node projects
pip-audit # Python
docker run --rm -v "$PWD:/src" aquasec/trivy:latest fs --scanners vuln /src # any project + images
ToolScans
npm audit, pip-audit, bundler-auditLanguage dependencies
Trivy, GrypeDependencies, container images, IaC
Dependabot, RenovateAuto-raise update PRs
OWASP Dependency-CheckMany ecosystems

Triage results: is the vulnerable code path actually used? Fix HIGH/CRITICAL that are reachable first.

14. Business logic​

In short: flaws where every request is valid but the rules are broken — scanners can't find these; you find them by understanding the domain.

FlawTest
Negative quantity / priceOrder −5 items → refund?
Coupon abuseApply the same coupon many times; stack coupons
Refund > paymentRefund more than was paid
Skip a stepGo straight to "order confirmed" without paying
Change a hidden fieldPost "price": 0 or "role": "admin" in the body
Race conditionsRedeem a one-time voucher twice at the same instant

Juice Shop has several of these (place an order with tampered totals, apply another user's coupon, etc.). The fix is always server-side validation of the rule — never trust the client.

Try it: in Juice Shop, add an item to the basket, then use the API to change the quantity to a negative number. What happens to the total?

15. Common mistakes​

In short: how security testing goes wrong.

MistakeBetter
Testing without written permissionGet scope in writing; use legal practice targets
Running a scanner and pasting raw outputTriage: confirm, remove false positives, prioritise
Only checking the happy path is "secure"Test as the wrong user, with no token, with bad input
Real attack payloads on shared/practice appsHarmless proofs (alert(1)); never destructive
Reporting "SQL injection" with no reproExact request, response, and impact
Ignoring the passesNote what's correctly protected too
Testing production without a window/planAgree timing, limits, rollback, and who's on call

Part 3 — Advanced​

16. Automated scanning (ZAP)​

In short: OWASP ZAP is a free web scanner; its baseline scan is passive (it spiders and observes, doesn't attack) — safe to run in CI.

Real result against Juice Shop:

docker run --rm --add-host=host.docker.internal:host-gateway ghcr.io/zaproxy/zaproxy:stable \
zap-baseline.py -t http://host.docker.internal:3001
Total of 88 URLs
WARN-NEW: Content Security Policy (CSP) Header Not Set [10038] x 5
WARN-NEW: Cross-Domain Misconfiguration [10098] x 5
WARN-NEW: Timestamp Disclosure - Unix [10096] x 5
WARN-NEW: Cross-Origin-Embedder-Policy Header Missing or Invalid [90004] x 10
…
FAIL-NEW: 0 WARN-NEW: 8 PASS: 59
  • Baseline (passive): safe, fast, CI-friendly — confirms the missing CSP from section 5.
  • Full scan (zap-full-scan.py): active — it attacks; only on non-production with permission.
  • Every result still needs a human to confirm and rate.

17. SAST & dependency scans in CI​

In short: run three fast scanners on every pull request — code (SAST), dependencies and images (SCA), and secrets — and gate on new HIGH/CRITICAL.

# .github/workflows/security.yml (excerpt)
- name: SAST — Semgrep
run: docker run --rm -v "$PWD:/src" semgrep/semgrep semgrep scan --config auto --error /src
- name: Dependencies & secrets — Trivy
run: docker run --rm -v "$PWD:/src" aquasec/trivy fs --scanners vuln,secret --severity HIGH,CRITICAL --exit-code 1 /src
- name: DAST — ZAP baseline (passive) against staging
run: docker run --rm ghcr.io/zaproxy/zaproxy zap-baseline.py -t https://staging.example.com
TypeToolFinds
SASTSemgrep, CodeQL, SonarQubeRisky patterns in your code
SCATrivy, Grype, Dependency-CheckVulnerable dependencies & images
SecretsGitleaks, TrivyCommitted keys and passwords
DASTZAP baselineRuntime issues (headers, cookies)

Gate on new HIGH/CRITICAL only, or teams switch the scanner off.

18. Burp Suite​

In short: Burp Suite is the standard tool for manual web security testing — a proxy that sits between browser and server so you can read and change every request.

FeatureUse
ProxyIntercept and edit requests live
RepeaterRe-send a request with tweaks (the manual tester's workhorse)
IntruderAutomate variations (id enumeration, fuzzing) — throttled
DecoderEncode/decode base64, URL, JWT
ScannerActive scan (Pro only)

The free Community edition covers Proxy, Repeater and Decoder — enough to learn with. OWASP ZAP is a fully free alternative with a similar proxy and Repeater-like "Requester".

Try it: point Burp or ZAP at Juice Shop, add an item to the basket, and use Repeater to change the quantity in the captured request.

19. API & cloud​

In short: APIs and cloud config have their own top-10 lists — use them as checklists.

OWASP API Security Top 10 — the biggest is again broken authorization (BOLA): test every endpoint with the wrong user, no token, and the wrong role. Full checklist in the API testing cheat sheet.

Cloud (AWS/GCP/Azure): the common wins are public storage buckets, over-broad IAM roles, open security groups, and logging turned off. Scan infrastructure-as-code with Checkov or Trivy and check against the CIS Benchmarks.

LLM features: the OWASP Top 10 for LLM Applications — prompt injection, data leakage — see the AI/LLM testing cheat sheet.

20. CVSS & severity​

In short: rate each finding with CVSS (a 0–10 score) plus business context, so teams fix the worst first.

CVSSRatingExample from Juice Shop
9.0–10.0CriticalSQLi login bypass to admin
7.0–8.9HighBOLA reading other users' baskets
4.0–6.9MediumMissing CSP header (enables XSS impact)
0.1–3.9LowTimestamp disclosure, verbose error

CVSS gives a consistent base score; always adjust for your context — a "medium" on a page holding medical data may be your top priority.

21. Reporting​

In short: each finding is a runnable proof plus impact and a concrete fix; the report opens with the risks that matter most.

ID / Title:   SEC-01 — Authentication bypass via SQL injection on /rest/user/login
Severity: Critical (CVSS ~9.8)
Affected: POST /rest/user/login (Juice Shop v20.2.0, local)
Steps: curl -s -X POST $J/rest/user/login -H 'Content-Type: application/json' \
-d '{"email":"'"'"' OR 1=1--","password":"x"}'
Result: HTTP 200; logged in as admin@juice-sh.op without a valid password
Impact: Full account takeover of any user, including admin
Fix: Use parameterised queries; validate input; never build SQL by string concatenation
Reference: OWASP A03:2021 Injection

The report starts with an executive summary: overall risk, count by severity, and the top three to fix now. Report criticals immediately by phone/secure channel — don't wait for the written report.

22. Words you'll meet​

In short: the jargon, in one line each.

WordMeaning
Rules of engagementThe signed agreement on what/how/when you may test
SAST / DAST / SCAStatic (code) / dynamic (running app) / software-composition (dependencies) analysis
SQLiSQL injection — input changes a database query
XSSCross-site scripting — attacker's script runs in a victim's browser
BOLA / IDORBroken Object Level Authorization / Insecure Direct Object Reference — reaching others' data by id
CSRFCross-site request forgery — a victim's browser is tricked into a request
SSRFServer-side request forgery — the server is tricked into fetching a URL
JWTJSON Web Token — a signed token carrying user info
CVEA public id for a known vulnerability
CVSSCommon Vulnerability Scoring System — 0–10 severity
PayloadThe input crafted to trigger a vulnerability
False positiveA scanner warning that isn't a real problem

For the service, including penetration-test phases, see the security testing guide.