Web Security Basics
Web security basics are the security checks a functional tester can run safely while testing a web application: HTTPS everywhere, the right cookie flags, input treated as data rather than code, protection against cross-site scripting (XSS) and cross-site request forgery (CSRF), and the security headers that tell the browser to protect the user. None of it needs attack tools: the browser, DevTools and a few harmless inputs are enough.
For example, a tester enters Sara <b>QA</b> as a display name. If every page shows exactly those characters, the input was handled as text. If somewhere QA turns bold, the application inserted user input into the page as markup — the flaw an attacker uses to run a script in another user’s browser. A serious defect, found without any attack code.
Why it matters
Security defects break requirements you already test: data stays private, only its owner changes it, and pages show what users typed. The difference is that someone searches for them on purpose. A session cookie that scripts can read, or a form that another site can submit, turns one small defect into stolen accounts, changed orders and exposed personal data.
Penetration testers go deeper, but they usually test at fixed points, such as before a major release. A functional tester touches every form and every release, so basic checks during normal testing find these defects early, when fixing them is cheapest, and give the security team better information when it looks.
What a functional tester can check safely
To a server, a harmless test value and an attack can look the same, and testing without permission can break the law even with good intentions. So a functional tester stays on the observing side: read what the application sends, enter what a real user could enter, and report what you see. Proving that a weakness can be exploited is the security team’s job.
- Test only systems you own or have written permission to test, preferably a test environment; never third-party sites, and production only if the permission names it.
- Use your own test accounts. Never read, change or delete other people’s data, even when you find that you could.
- Use harmless markers such as
<b>qa</b>, quotes and apostrophes; no script payloads, scanners or attack tools without the security team’s approval. - Stop at the first sign of a weakness, keep the evidence and report it through your team’s restricted channel for security defects, with tokens and personal data masked.
Input handling, XSS and CSRF
Every value from outside is untrusted: form fields, URL parameters, headers, cookies, files. Safe applications defend on the server twice. On the way in, they validate against an allow-list of expected type, length and format; browser checks are only a convenience. On the way out, they encode for the context — in HTML, < becomes <, so it is shown, not interpreted — and pass values to the database as parameters, never as glued-in text. Validation alone is not enough: O'Brien is valid input, so safety comes from how output is written.
Cross-site scripting (XSS) is the result of missing or wrong output encoding: input becomes markup, so a script an attacker typed runs in the victim’s browser with the victim’s session. It is stored when the value is saved and shown to others later, reflected when the response echoes it straight back from the request, and DOM-based when the page’s own JavaScript writes it into the page, for example with innerHTML.
Cross-site request forgery (CSRF) abuses the fact that browsers attach cookies automatically. While you are logged in to a shop, a page on another site submits a hidden form to the shop’s change-email address, and a shop that checks only the cookie obeys. The main defence is an anti-CSRF token in each form that changes data, checked by the server; SameSite cookies are a second layer, not a replacement. Lax still sends cookies when the user follows a link from another site, one reason data must never change through a GET request.
- Enter
' " < > &and<b>qa</b>in text fields, then look everywhere the value appears later: lists, admin screens, e-mails, exports. Expected: the characters as typed, never bold text, and Arabic or emoji input still intact. - Error pages stay generic: no stack trace, SQL or file path. An apostrophe that causes a database error suggests text glued into a query; report it and do not probe further.
- Forms that change data carry a hidden token or send one in a header, and nothing is deleted, paid or changed through a plain link.
- With permission, replay such a request without its token, for example copied as cURL with the token removed. Expected: the server refuses it.
Security headers
Security headers are response headers that switch on protections in the browser. They are a second layer that limits the damage of a defect such as XSS; they do not fix it. Check the main HTML pages in the Network panel or paste their headers into the Security Header Analyzer, and read each value, not just its presence.
Content-Security-Policy(CSP) lists where scripts, styles, images and frames may load from.default-src 'self'allows only the page’s own origin (same scheme, host and port) and blocks inline scripts;'unsafe-inline'for scripts lets an injected inline script run, and a*source lets one load from any site.frame-ancestorsin the CSP, or the olderX-Frame-Options: DENY, stops other sites from framing the page to trick users into clicking hidden buttons (clickjacking).X-Content-Type-Options: nosniffstops the browser from guessing file types, so a file served as text is not run as a script.Referrer-Policylimits how much of the URL other sites receive;Permissions-Policyswitches off features the site does not use, such as the camera.- Headers such as
Server: nginx/1.18.0orX-Powered-Byreveal the software, often with its version, and tell attackers which known flaws to try: remove them or make them generic.
Try it: Security Header Analyzer Try it: HTTP Headers Parser
Worked example: a safe check of a profile page
A shop’s test plan includes a security smoke check of its new profile page, with the scope confirmed in writing by the security lead: the test environment, two test accounts, harmless inputs only. The tester works with the browser and DevTools.
http://test.shop.example/profile ends in a 301 redirect to HTTPS, and the page’s response headers are shown below. The Application panel shows the session cookie sid with Secure and SameSite=Lax but without HttpOnly. The display name Sara <b>QA</b> appears as typed on the profile page, but bold in the admin customer list. An apostrophe in the order search returns a 500 page with a database error. The profile form sends a hidden csrf_token, while Delete address is a plain link to /profile/address/delete?id=12. At the first sign of each weakness the tester stops probing it, saves the evidence and moves on to the next check.
The report lists the findings most serious first: the markup in the admin list (probable stored XSS, running in an administrator’s session), the database error, the GET delete link, the missing HttpOnly, the missing CSP and frame protection and, as a low-severity note, the nginx version.
GET /profile HTTP/1.1
Host: test.shop.example
HTTP/1.1 200 OK
Server: nginx/1.18.0
Content-Type: text/html; charset=utf-8
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin| Check | Observed on the test environment | Risk when the check fails |
|---|---|---|
| HTTPS redirect | http:// ends in a 301 redirect to https://Pass | Pages, form data and cookies can be read or changed on the network |
| HSTS header | Set, with max-age=31536000Pass | A typed http:// address can be intercepted before the redirect |
| Session cookie flags | Secure and SameSite=Lax, but no HttpOnlyFail | An injected script can read the session identifier |
| Markup in the display name | As typed on the profile page, bold in the admin listFail | Stored XSS: a script would run in an administrator’s session |
| Apostrophe in the order search | A 500 page with a database errorFail | Possible injection; the error reveals internals |
| Requests that change data | Profile form sends a token; Delete address is a GET linkFail | CSRF: another site makes the user’s browser act |
| CSP and frame protection | Neither header is setFail | No second barrier against XSS; the page can be framed for clickjacking |
| Other headers | nosniff and Referrer-Policy set; Server shows nginx/1.18.0Caution | The version tells attackers which known flaws to try |
A failed check is a finding to report, not proof of an exploit: the security team confirms how serious it is.
Common mistakes
- Trying security checks on production or on other companies’ sites without written permission: a harmless test value can be logged and treated as an attack.
- Using script payloads or scanners copied from a tutorial instead of harmless markers, or probing further after the first sign of a weakness instead of reporting it.
- Checking input only where it is entered, never where it is displayed later: admin screens, e-mails, exports and PDFs.
- Treating browser checks such as
maxlengthortype="email"as protection; only the server’s checks count. - Ticking a header as present without reading its value: a
Content-Security-Policythat allows'unsafe-inline'scripts protects little against XSS. - Filing security findings in a tracker everyone can read, or pasting live cookies and tokens into the report.
The QA perspective
Security checks belong in normal test design. When a story adds a form, user content, a cookie, a redirect or an action that changes data, add the matching checks to its test conditions and ask early: where will this input be displayed, and how are data-changing actions protected? Agree the scope and the safe inputs with the security lead in the test plan.
Report findings like any defect — steps, the harmless input, where it appeared, request and response with secrets masked — but with restricted visibility, and agree severity with the security team, who judge exploitability. After the fix, confirmation-test every place the value appears, and keep the checks in the regression suite: headers and cookie flags often change with a new server, proxy or framework version.
Practice exercise
Review a clinic booking page safely
You have written permission for a security smoke check of a clinic booking site’s test environment. Below are the appointments page response and four observations: (1) the http:// address loads with no redirect; (2) the Cancel link; (3) a note you typed as the patient in the booking form, shown on the doctor’s screen with "results" in italics; (4) the request the login form sends.
For each item, decide whether it passes or is a finding, name the risk and write a bug title. Order the findings by risk, then list the next safe checks and one check you would not do without the security team.
GET https://test.clinic.example/appointments
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Powered-By: Express
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
X-Frame-Options: DENY
Set-Cookie: session=c2Vzc2lvbi0x; Path=/; HttpOnly
(1) GET http://test.clinic.example/appointments -> 200 OK
(2) <a href="/appointments/cancel?id=481">Cancel</a>
(3) Bring <i>results</i>
(4) GET /login?user=sara&password=[masked]- Paste the response headers into the Security Header Analyzer and the
Set-Cookieline into the Cookie Analyzer; note what is present, missing or weak. - Mark each of the four observations as a pass or a finding.
- For every finding, write the risk in one sentence and a bug title.
- Order the findings and list the next safe checks, plus one that needs the security team’s approval.
Show the model answer
Passes: X-Frame-Options: DENY blocks framing and the session cookie is HttpOnly. A CSP with default-src 'self' is present but weak: 'unsafe-inline' in script-src lets an injected inline script run, which matters for finding A.
Findings, most serious first, lettered so they are not confused with the observation numbers. A (observation 3) "Appointment note is rendered as HTML on the doctor’s screen": probable stored XSS, which the CSP would not stop because it allows 'unsafe-inline' scripts. B (observation 4) "Login sends the password in the URL of a GET request", where history, server logs and the Referer header keep it. C (observation 1) "Appointments page is served over HTTP without redirect or HSTS"; with no Secure flag, the session identifier travels unencrypted.
D (observation 2) "Cancel appointment is a GET link without CSRF protection": another site can lead a logged-in patient’s browser to it; the cookie has no explicit SameSite, and even SameSite=Lax would send it when a link is followed. E (response headers) Low: X-Powered-By: Express reveals the framework; nosniff and Referrer-Policy are missing.
Next safe checks: the note on the patient’s screen, in reminder e-mails and printouts; quotes and apostrophes in the note and the other text fields. Not without the security team: a script in the note, a scanner, or changing the id to cancel another patient’s booking.
Frequently asked questions
Is this the same as penetration testing?
No. A penetration test is an authorised, simulated attack by security specialists, who try to exploit weaknesses and judge their impact. The checks in this lesson observe how the application behaves, with normal tools and harmless inputs. They catch common defects early and make a penetration test more productive; they do not replace it.
Why test with <b> instead of a real script?
A formatting tag proves the same fact — the input reached the page as markup — without running code in anyone’s browser, touching other users or setting off security alarms the way an attack would. Whether a script would run, and what it could do, is for the security team to confirm.
The site redirects to HTTPS. Does the session cookie still need Secure?
Yes. Without Secure the browser also sends the cookie with a plain http:// request to the same host, such as a typed address or an old link, before the redirect happens. HSTS reduces those requests, but Secure is the cookie’s own protection and costs nothing.
Does a good grade from a header checker mean the site is secure?
No. Headers are a second layer that limits damage; they say nothing about access control, injection, sessions or business rules. Use the grade as a list of missing or weak headers, and read each value yourself.
References
- OWASP Web Security Testing Guide (WSTG)The reference methodology for testing web applications and APIs for security weaknesses, test by test.
- OWASP Cheat Sheet Series: Cross Site Scripting PreventionPreventing XSS: output encoding for each context, sanitizing user-authored HTML, safe DOM sinks and framework protections, with CSP only as defence in depth.
- OWASP Cheat Sheet Series: Cross-Site Request Forgery PreventionCSRF defences: synchronizer tokens, SameSite cookies and origin checks.
- OWASP Cheat Sheet Series: HTTP HeadersWhich security headers to send (HSTS, CSP, X-Content-Type-Options, frame protection) and which to remove (Server, X-Powered-By).
Practise with Mutqan tools
Grade HTTP security headers (CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP/CORP) from pasted headers or a public URL.
Parse Set-Cookie headers and check Secure, HttpOnly, SameSite, Domain, Path, expiry and size best practices.
Escape HTML special characters (<, >, &, quotes) into entities to prevent XSS.
Parse raw HTTP request/response headers into a structured table with explanations.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.