Site security: OWASP, HTTPS, updates and passwords
Why:Sites aren’t hacked out of personal dislike but automatically and in bulk, by catching on out-of-date plugins, weak passwords and forgotten keys. Afterwards you know the ten commonest classes of problem on the OWASP Top 10 list in plain words, you set up HTTPS and security headers, you turn on two-factor login, and you write a short protection plan for your project.
30 minutes
Read by speech synthesis (Qwen3-TTS); the voice is not a person’s.
The OWASP Top 10 is a list of the most common classes of web application weakness. HTTPS, updates, passwords and a second factor guard against mass attacks.
Who gets hacked
The first thought: “who needs my little site?” The answer: a bot. Break-ins today are almost always automatic. Programs go round the clock through addresses on the internet, knock at /wp-login.php, look for a .env file, try the known hole of a known plugin, and if they find a way in, they take what there is: they plant hidden ads and links to their own sites, send spam from your address, steal inquiries and passwords. They don’t care what the site is. What matters is that it is open.
A practical conclusion follows. You don’t need a defense against intelligence agencies. You need to close what can be closed cheaply: updates, passwords with a second factor, encryption, input, keys. It is boring, and it works. The rest is a question of the project’s size.
This lesson is about the site as a whole, and the next one is about the form on its own.
OWASP and its list of ten
OWASP (the Open Worldwide Application Security Project, formerly the Open Web Application Security Project) is an open nonprofit community that publishes free materials on application security. Its best-known product is the OWASP Top 10: a list of the ten commonest classes of problem, updated every few years. The latest edition came out in 2025. It isn’t a checklist but a compass: “here is where things most often get broken in practice.”
Let’s read all ten in plain words, with what the author of a small site does about each.
- A01 Broken access control. A person gets what they aren’t entitled to: they changed the number in the address
/request/105to106and saw someone else’s request; they opened/adminwithout logging in. The cure: the server checks permissions on every request and doesn’t trust what arrived. - A02 Security misconfiguration. A default password, an open control panel, debugging left on, no security headers, a file listing visible to all. The cure: configure and check, and turn off what isn’t needed.
- A03 Software supply chain failures. The site rests on other people’s work: the CMS, plugins, themes, libraries, build tools. If there is a hole there, or someone swapped a package, the hole is yours. The cure: update, don’t install extras, and take things from reliable sources.
- A04 Cryptographic failures. No HTTPS, passwords stored in plain text or with a weak hash, secrets lying around anywhere. The cure: HTTPS everywhere, hash passwords (see the lesson on authentication), keep secrets apart.
- A05 Injection. Text typed by a person turns into code or part of a query. If you paste text as it is into a database query, a quotation mark and a scrap of SQL can pull out the whole database (SQL injection). If you print someone else’s text on a page without escaping it, someone else’s script runs for other visitors (XSS, cross-site scripting). The cure: parameterized queries, escaping on output, and checking input.
- A06 Insecure design. The mistake isn’t in the code but in the idea: a password reset by date of birth, a form with no rate limit whose spam can’t be stopped. The cure: think about abuse before building.
- A07 Authentication failures. Weak passwords, no second factor, no protection against guessing. The cure: long unique passwords, a second factor, a limit on attempts.
- A08 Software or data integrity failures. Code or data changes without being checked: a script was included from someone else’s server with no check of authenticity, or an auto-update with no signature check. The cure: verify the source, pin versions, and give included scripts a checksum (Subresource Integrity).
- A09 Security logging and alerting failures. An attack goes unseen because nobody records or reads errors and odd logins. The cure: a log of errors and logins, and an alert on anything unusual.
- A10 Mishandling of exceptional conditions. An error shows the visitor the insides (a file path, a database query), or makes a protection “switch off.” The cure: a clear message for the person, details in the log, and when something fails, deny rather than allow.
The diagram below shows the link between this list and the measures. Switch measures on and see which automatic attempts are stopped.
These are ordinary automatic attempts that hit any site. Switch measures on and see what is stopped.
- A bot guesses passwords on the admin login page
Got through. OWASP category: A07 Authentication Failures. - A script tries the known hole of an out-of-date plugin
Got through. OWASP category: A03 Software Supply Chain Failures. - A script is pasted into the “Comment” field to run for other visitors
Got through. OWASP category: A05 Injection (XSS). - A quotation mark and a scrap of SQL are typed into a form field to pull out the database
Got through. OWASP category: A05 Injection (SQL). - A stranger on open Wi-Fi reads what the form sends
Got through. OWASP category: A04 Cryptographic Failures. - Your site is shown as an invisible layer on someone else’s page to hijack a click
Got through. OWASP category: A02 Security Misconfiguration. - A scanner looks for a .env file and keys in a public repository
Got through. OWASP category: A02 Security Misconfiguration. - In the address /request/105 the number is changed to 106 and someone else’s request is read
Got through. OWASP category: A01 Broken Access Control. - A bot submits the inquiry form a thousand times with ads
Got through. OWASP category: A06 Insecure Design.
Stopped 0 of 9. Nothing yet: the site is open to anyone who tries.
Text alternative: a feed of attempts (password guessing, a hole in a plugin, a script and SQL in a form, interception on Wi-Fi, embedding the site in someone else’s page, a search for keys, a swapped number in the address, spam) and a list of measures. Each attempt is stopped by at least one suitable measure: a second factor or rate limiting, updates, input checks, HTTPS, headers, keeping secrets out of the code, permission checks.
Check yourself: what is an injection and how is it prevented?
Text typed by a person gets into code or a query as part of a command. It is prevented with query parameters (the value travels apart from the query text), with escaping when printing to a page, and with checking input on the server.
What an injection looks like
SQL injection is simpler than it seems. Suppose a site looks up an inquiry by phone number like this:
query = "SELECT * FROM requests WHERE phone = '" + phone + "'" If the person types an ordinary number, everything works. But if they type ' OR '1'='1, the query becomes “select all inquiries where the phone equals nothing or one equals one,” that is, it returns the whole table. The right way is to pass the value apart from the query text:
SELECT * FROM requests WHERE phone = $1 (the value is passed separately) The database itself knows that $1 is a value and not a command, and typed text never becomes part of the query. You saw such parameters in the lessons on SQL. XSS works the same way, only the victim is different: not the database but the browser of another visitor. Modern templating systems (Svelte, for example) escape output automatically until you ask them to insert “raw” HTML.
HTTPS and security headers
You learned about certificates in the lesson on HTTPS and TLS and in the lesson on certificates in practice. Here is what to add.
- HTTPS everywhere. The whole site, all resources (or the browser blocks “mixed content”), and a redirect from
http://tohttps://. - HSTS is the header
Strict-Transport-Security: max-age=31536000. It tells the browser: for a year, visit this site only over HTTPS and don’t even think of http. Without it, a person who types the address withouthttpstravels for a moment over an open connection, and can be intercepted. Turn it on when you are sure the whole site works over HTTPS. - Content-Security-Policy (CSP) is a list of sources from which the page may take scripts, styles and images, and where it may send forms. A script that got into the page through XSS but didn’t come from an allowed source won’t be run by the browser. A reliable minimum:
default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'. Services you embed (an analytics counter, a map) are added to the policy explicitly. The words'unsafe-inline'and'unsafe-eval'almost entirely drain the policy of meaning: avoid them. - X-Content-Type-Options: nosniff means the browser doesn’t “guess” a file’s type and doesn’t run an image as a script.
- Referrer-Policy: strict-origin-when-cross-origin means that only your site’s name goes to other sites, not the full page address (which may hold data).
- frame-ancestors (in CSP) or the older X-Frame-Options stop your site being embedded in someone else’s page. Otherwise “clickjacking” is possible: an invisible layer with your button beneath someone else’s picture.
- Cookie flags (you know them from the lesson on authentication):
Secure(only over HTTPS),HttpOnly(unavailable to scripts),SameSite=Lax(not sent from other sites).
Where these headers are set. In the web server’s settings (nginx, Caddy), in a _headers file on Netlify and Cloudflare Pages (a line with the path /*, and the headers below it, indented), or in the platform’s settings. Not every platform allows it: on GitHub Pages you can’t set your own headers (part of a policy can be passed through a meta tag, but not all of it: frame-ancestors doesn’t work in meta). If you can’t do without them, put a proxy or a CDN in front of the site to add the headers.
You check the result with the free MDN HTTP Observatory: it opens your site and gives a grade with a list of what isn’t configured. Write down the grade “before” and “after,” and come back to it when you add a new library: it is easy to break what you configured.
Check yourself: why is unsafe-inline never left in a CSP?
It allows a script written straight into the page to run, and that is exactly how XSS works: the injected script ends up inside the page. With unsafe-inline the policy hardly protects at all.
Updates
Most sites are broken into through something out of date: a CMS version, a plugin, a theme, a library. The holes are published, and automatic scanners check who hasn’t closed them yet. So:
- Update regularly, and critical fixes at once. For WordPress and other CMSs, turn on automatic security updates. For a project in code: an
npm auditreport, GitHub Dependabot or similar. - Don’t install extras. Every plugin is someone else’s code and your responsibility. If you turned it off and don’t use it, delete it.
- Take things from reliable sources, and look at when a plugin was last updated and how many people use it. An abandoned plugin is a hole on a delay.
- Pin versions and check the sources when you include scripts from other servers (this is the very A03 and A08).
- Have a copy, and know how to restore it (the lesson on backups in the infrastructure part): an update sometimes breaks the site, and a rollback has to be possible.
Passwords and a second factor
The commonest way in is a stolen or guessed password. What works.
- A unique long password for every site, remembered by a password manager (Bitwarden, KeePass and others; the managers built into browsers and systems are fine too). If one site loses its database, the rest stay closed.
- Length matters more than cleverness. Current guidance (NIST SP 800-63B) recommends long passwords, allowing paste and managers, checking against lists of leaked passwords, and not changing a password on a schedule for no reason: frequent changes breed “Password1,” “Password2.”
- A second factor (2FA): besides the password, something else is needed. An authenticator app (codes that change every thirty seconds), a hardware key or a passkey (from the lesson on authentication) is stronger than SMS (a number can be intercepted). But SMS is better than nothing.
- Protect the chain. First protect the owner’s email: the other passwords are reset through it. Then the domain registrar (whoever owns the domain owns the site), the hosting or deployment service, the repository and the site admin.
- Save the backup codes of the second factor somewhere safe: a lost phone must not become a lost site.
- Visitors’ passwords (if you have a login) aren’t stored by you: only a hash made with a slow function (argon2id, bcrypt), as in the lesson on authentication. Better not to create a login at all if you can do without it.
You can check whether your email address has appeared in known breaches in a service called Have I Been Pwned (in the sources): it shows which leaks included the address.
Secrets
A key to a payment system, a database password, a receiver’s token: all of these are secrets. Their main rule: a secret must not get into the repository. For that, put it in your server’s or platform’s environment variables (every deployment service does this) or in a secrets manager, and add the local .env file to .gitignore. If a secret did get into the repository, even for a minute, consider it compromised: revoke it and issue a new one. Automatic scanners find keys in public repositories within seconds, and deleting a file doesn’t delete it from the history.
If you are hacked anyway
Tell yourself in advance what you will do while nothing is on fire.
- Stop the damage: take the site down (a holding page) and close the way into the admin.
- Change the access: the passwords and keys of all linked accounts, revoke tokens, end other people’s sessions.
- Restore from a clean copy and update everything that was out of date. Simply “cleaning” a hacked site is unreliable: you don’t know what remains.
- Find how they got in, from the logs, or it will happen again.
- Assess whether people’s data leaked, and if it did, notify whoever the law says. When a leak touches personal data, the organization in charge of that data has duties and deadlines. The next lessons cover them: GDPR requires notifying the regulator within 72 hours in many cases, and US states have their own breach notification laws.
Practice
You are in the era of 2022: the site builds automatically and deploys to a platform. Draw up a one-page protection plan for your project: a headers file, a second factor for five accounts, an update rule, a place for secrets and a note on the main action form.
The check reads text: it makes sure the headers include HSTS, a CSP with the needed rules and without 'unsafe-inline', nosniff and a referrer policy, and that the accounts table names the protection. It can’t check that you really turned on a second factor. You will do that: open each account’s settings and turn it on, then test your real site in the Observatory.
What used to be different
In the 2000s “security” often meant one password for everything and no updates: sites on homemade scripts weren’t updated for years. Forms built an SQL query by gluing strings together, and passwords were stored as plain text, which people learned from reports of leaks. Platforms and standards appeared, and with them habits: HTTPS became the norm (with free certificates), browsers began to warn about unsafe sites, and the OWASP lists became a common language. But the main enemy stayed the same: “I’ll update later” and “I have nothing worth stealing.”
Practice
Preparing your page…
Test
Question 1 of 6
Cards
These cards go into your review once you finish the lesson.
What is the OWASP Top 10?
Answer
A list of the ten commonest classes of web application weakness, compiled by the OWASP community. It isn’t a complete checklist but a compass: the 2025 edition begins with broken access control.
What does a protected connection need?
Answer
HTTPS with a valid certificate, a redirect from http, HSTS (the browser uses only https), and the flags Secure, HttpOnly and SameSite on cookies.
Why CSP, and which rules are essential?
Answer
It limits the sources of code and the addresses forms send to. The basic set is default-src, object-src, base-uri, frame-ancestors and form-action, with self and none written in single quotes. No unsafe-inline.
Which accounts do you protect first?
Answer
The owner’s email (the other passwords are reset through it), the domain registrar, the hosting, the repository and the admin. On each: a unique password from a manager and a second factor.
Where do you keep keys and passwords?
Answer
In environment variables and a secrets manager, never in the code or the repository (.env goes in .gitignore). If a secret reaches the repository, revoke it and issue a new one.
Sources
- OWASP Top 10:2025 CC BY-SA, in English
- OWASP Cheat Sheet Series CC BY-SA, in English
- Content Security Policy (MDN) CC BY-SA, in English
- MDN HTTP Observatory CC BY-SA, in English
- OWASP: Password Storage Cheat Sheet CC BY-SA, in English
- NIST SP 800-63B: Digital Identity Guidelines, Authentication public domain, in English
- Have I Been Pwned: check an email address against known breaches link only, in English
- Web Almanac 2024: Security CC BY-SA, in English
Next
You now know: the basics of site security (OWASP), site hygiene, security headers and HTTPS.