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.

HTTPS and cookie flags

HTTPS encrypts the traffic between browser and server and proves the server’s identity with a certificate; the next lesson, on HTTP and HTTPS, explains how. Without it, anyone on the network path can read and change pages, form data and cookies.

The cookie attributes from the lesson on cookies and web storage are security controls too. On the session cookie, Secure keeps it off unencrypted connections, HttpOnly hides it from scripts, including an injected one, and an explicit SameSite=Lax or Strict stops the browser from sending it with most requests that start on other sites. Check them in the Application panel or with the Cookie Analyzer.

  • Every http:// address redirects to https:// (301 or 308) before any content is served or any cookie is set.
  • No mixed content: an HTTPS page must not load scripts, styles or images over http://. Browsers block such requests or upgrade them to HTTPS, and the Console shows a warning.
  • Strict-Transport-Security (HSTS, RFC 6797), for example max-age=31536000, makes the browser use only HTTPS for the host until it expires. Without it, a typed http:// address goes out unencrypted and can be intercepted before the redirect.
  • Passwords, tokens and personal data never appear in URLs: URLs are kept in history and server logs and can leak to other sites in the Referer header.

Try it: Cookie Analyzer

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 &lt;, 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.

Try it: HTML Entity Encoder

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-ancestors in the CSP, or the older X-Frame-Options: DENY, stops other sites from framing the page to trick users into clicking hidden buttons (clickjacking).
  • X-Content-Type-Options: nosniff stops the browser from guessing file types, so a file served as text is not run as a script.
  • Referrer-Policy limits how much of the URL other sites receive; Permissions-Policy switches off features the site does not use, such as the camera.
  • Headers such as Server: nginx/1.18.0 or X-Powered-By reveal 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
Eight safe checks on a shop’s profile page in a test environment, what the tester observed and the risk each check guards against: two pass, five fail and one is a warning.
CheckObserved on the test environmentRisk when the check fails
HTTPS redirecthttp:// ends in a 301 redirect to https://PassPages, form data and cookies can be read or changed on the network
HSTS headerSet, with max-age=31536000PassA typed http:// address can be intercepted before the redirect
Session cookie flagsSecure and SameSite=Lax, but no HttpOnlyFailAn injected script can read the session identifier
Markup in the display nameAs typed on the profile page, bold in the admin listFailStored XSS: a script would run in an administrator’s session
Apostrophe in the order searchA 500 page with a database errorFailPossible injection; the error reveals internals
Requests that change dataProfile form sends a token; Delete address is a GET linkFailCSRF: another site makes the user’s browser act
CSP and frame protectionNeither header is setFailNo second barrier against XSS; the page can be framed for clickjacking
Other headersnosniff and Referrer-Policy set; Server shows nginx/1.18.0CautionThe 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 maxlength or type="email" as protection; only the server’s checks count.
  • Ticking a header as present without reading its value: a Content-Security-Policy that 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

Your task

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.

Material
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]
Steps
  1. Paste the response headers into the Security Header Analyzer and the Set-Cookie line into the Cookie Analyzer; note what is present, missing or weak.
  2. Mark each of the four observations as a pass or a finding.
  3. For every finding, write the risk in one sentence and a bug title.
  4. Order the findings and list the next safe checks, plus one that needs the security team’s approval.
Show the model answer
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

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.

Validation

Parse Set-Cookie headers and check Secure, HttpOnly, SameSite, Domain, Path, expiry and size best practices.

Validation

Escape HTML special characters (<, >, &, quotes) into entities to prevent XSS.

Encoding

Parse raw HTTP request/response headers into a structured table with explanations.

Web

Go deeper

Your progress on this topic

Part of the QA Roadmap