Security Considerations
Chapter Thirty-One
Syllabus topic Module 1, "System Architecture Design: ... Security considerations".
Pages 187 to 194 of 499
In one line
Security design means deciding, before building, what an attacker could want from the system and how they could try to get it, and then choosing a defence for each threat, from how passwords are stored and sessions kept to who may do what, what input is trusted, where secrets live and what travels over the network, and writing every decision down so that it can be tested.
In the wording to use when asked: security considerations in system design identify the assets worth protecting, the entry points an attacker could use and the threats to each, and select controls, mapped to a recognised catalogue of risks such as the OWASP Top 10, covering authentication, session management, access control, input validation, output encoding, cryptography for data at rest and in transit, secret management, logging, dependencies and personal data, recorded in a security design that later security testing verifies.
Think like an attacker first
Start from three lists.
What is worth attacking? In the worked system: students' accounts and passwords, their personal data (names, college email addresses), their orders, the owner's control of the menu and the stock, and the system's availability at 12:15, when everyone orders.
Who might attack, and why? A student who wants to see or cancel a friend's order, or order without limits. Someone on the college Wi-Fi reading what others send. A stranger who finds the server and tries passwords. Someone who finds the team's repository. And automated programs that try every site they find.
Where can they get in? Every one of the nineteen API endpoints (Chapter 30), the pages, the network between the phones and the server, the server machine itself, the repository, and the people: the owner's password is worth more than any student's.
Every control in this chapter answers a line in one of those lists. A control that answers nothing on them is decoration; a line with no control is a hole.
The OWASP Top 10
The OWASP Top 10 is the Open Worldwide Application Security Project's list of the ten most important categories of security risk to web applications. The current edition, 2025, names them:
| Category | The worked design's answer | |
|---|---|---|
| A01 | Broken Access Control | a role check on every route, deny by default; students see only their own orders; requests from other sites refused (S1, S2, S7) |
| A02 | Security Misconfiguration | security headers on every response; error details never sent; the application and the database listen only on the machine itself (S11, S14, S17) |
| A03 | Software Supply Chain Failures | two dependencies, at exact versions, installed from a lock file with integrity hashes (S15) |
| A04 | Cryptographic Failures | passwords hashed with scrypt; sessions stored as hashes; HTTPS for any public deployment (S3, S6, S12) |
| A05 | Injection | every value in SQL a placeholder; text never inserted as HTML (S8, S9) |
| A06 | Insecure Design | this chapter: threats listed and answered before building; the stock rule in one statement (Chapter 22) |
| A07 | Authentication Failures | password rules, a limit on failed sign-ins, no account enumeration (S4, S5, S16) |
| A08 | Software or Data Integrity Failures | database constraints and transactions; nothing deserialised but JSON (S10) |
| A09 | Security Logging and Alerting Failures | one log line per request, failures included; no alerting, a recorded limitation |
| A10 | Mishandling of Exceptional Conditions | one error handler; a rollback on any error inside a transaction (S11) |
Security Considerations
The third column is the worked team's own mapping of its controls to the categories, not OWASP's. The S numbers refer to the security design table at the end of this chapter.
Passwords
Storing them
A password must never be stored as it was typed, and never with a fast hash such as SHA-256, which lets an attacker who steals the table try billions of guesses a second. OWASP's Password Storage Cheat Sheet says what to use instead: a slow, memory-hard algorithm, with a unique random salt for every password, so that two students with the same password have different hashes and no precomputed table helps. It ranks Argon2id first, and scrypt where Argon2id is not available.
The worked team uses scrypt, and the reason is written down. Node.js added Argon2 to its built-in crypto module only in version 24.7.0; on Node.js 22, which the project still supports (NFR-12), it does not exist, as the team confirmed by asking each installed version. scrypt exists on all of them. The cheat sheet lists five scrypt settings of equal strength; the team uses N = 2^14, r = 8, p = 5, which needs 16 MiB of memory per hash.
Each stored hash records its own algorithm and settings beside the salt, in the form scrypt$16384$8$5$salt$key. That makes the choice reversible: when Node.js 22 reaches its end of life, the team can move to Argon2id by re-hashing each password the next time its owner signs in, without breaking any password already stored. And a sign-in compares the computed hash with the stored one in constant time, so the time taken reveals nothing about how close a guess was.
Chapter 8 noted the legal side: under the Information Technology rules of 2011, a password is sensitive personal data, and section 43A of the Information Technology Act requires reasonable security practices for it. Hashing it this way is a large part of what "reasonable" means today.
The rules for choosing one
Two respected standards disagree about length. OWASP's Application Security Verification Standard, ASVS 5.0, requires at least 8 characters and strongly recommends 15. NIST's SP 800-63B-4 says that a password used as the only factor shall be at least 15 characters. Both say the same about everything else: no composition rules, no demand for a capital letter, a digit and a symbol, which make passwords harder to remember and no harder to guess, and a maximum long enough for passphrases.
Security Considerations
The worked team chose 8 to 128 characters, with no composition rules, and wrote down why: the accounts guard lunch orders, not money; registration is open only to college email addresses; failed sign-ins are limited; and a student typing on a phone at the start of the break will not type 15 characters. That meets ASVS level 1, falls short of NIST's rule, and says so. A system guarding anything of real value should take 15.
Reading ASVS against the design found a gap: level 1 also requires that registration refuse at least the 3,000 most common passwords (6.2.4), and the first design did not. It is now part of the design, as control S5: registration will refuse any password on a list of common passwords, and the page will ask for a password used nowhere else. Chapter 46 builds and tests it.
Sessions
After a correct password, the server creates a session: 32 random bytes, 256 bits, given to the browser as a cookie called sid. The database stores only a SHA-256 hash of it, the decision recorded as ADR-3 (Chapter 36), so that a stolen copy of the sessions table cannot be used to sign in as anybody: the hash cannot be turned back into the cookie. Here a fast hash is right, unlike for passwords, because the token is long and random, with nothing to guess.
The cookie is:
- HttpOnly: page scripts cannot read it, so even an injected script could not steal it;
- SameSite=Lax: the browser does not send it with a form posted from another site;
- Secure whenever the site uses HTTPS: it is then never sent over plain HTTP (NFR-4);
- limited to 8 hours, and deleted from the database at sign-out, so a stolen cookie stops working.
Access control
Deny by default. Every API route states who may call it, with a guard that refuses anyone else: 401 if nobody is signed in, 403 for the wrong role (Chapter 30). A route with no guard is one of the few deliberately public ones: the menu, the slots, signing in and out, registering, and the health check.
Ownership, not just role. Being a student is not enough to see an order; it must be your order. The order service checks the owner on every read and every cancellation, and answers "not found" for anyone else's, so that it does not even confirm the order exists (NFR-5).
Security Considerations
The rules decide who may move an order. A student may move an order only from placed to cancelled; only the counter staff and the owner may move it any other way (Chapter 23). That check is in the rules, not in the pages, because a page's buttons can be bypassed by anyone who sends the request directly.
Input: validate everything on the server
Every value that arrives is checked on the server, whatever the page already checked: the page's checks exist to help honest users, and an attacker does not use the page. Types, lengths, allowed values and ids are all checked in one place (Chapter 47); a request body over 10 kilobytes is refused before it is read; and the database's own constraints refuse anything that gets past both (Chapter 29).
SQL injection is prevented by never building SQL out of values: every value is sent to MySQL separately, as a placeholder. Where the text of a statement does vary, it varies only by pieces written in the code: how many placeholders there are, whether a slot filter is added, and, when the owner changes some of a menu item's fields, column names taken from a fixed list, never from the request.
Cross-site scripting is prevented by never adding any text to a page as HTML (Chapter 27), and, as a second defence, by a content security policy that forbids the browser to run any script except the site's own files.
Cross-site request forgery, another site making a signed-in student's browser send a request, is prevented three times over: every request that changes something must be JSON, which an ordinary form on another site cannot send; if the browser names the page the request came from, that page must be on this site; and the SameSite cookie is not sent with another site's form posts.
Secrets
Every secret, the database password first, lives in the .env file on the machine that runs the application, which the repository's .gitignore excludes; the repository holds only .env.example, with placeholder values (Chapter 28). A password once committed stays in the repository's history for ever, even after it is deleted from the file, so the rule is: never commit it, not even once.
The database account the application uses can reach only its own two databases, the real one and the tests', and nothing else on the server (Chapter 29). It has every privilege on those two, because the setup script must be able to rebuild them. A stricter design would give the running application a second account allowed only to read and write rows, never to change tables; for one canteen the team judged one account enough, and recorded the choice.
Security Considerations
HTTPS, and the trial's weakness
Chapter 25 drew the trial deployment honestly: the phones reach the lab desktop over the college Wi-Fi with plain HTTP, because the machine has no public name or address and so no certificate the phones would trust. That is the largest security weakness of the trial, and the design says so:
- What is exposed: everything sent between a phone and the server: passwords at sign-in, the session cookie with every request, and the orders.
- To whom: anyone able to capture traffic on the same network.
- What limits it: sessions end after 8 hours or at sign-out; each password is hashed on the server, so the database itself never holds it; and the registration page will ask for a password used nowhere else (S5), so that one captured password does not open a student's other accounts.
- The fix: a public server with a name and a certificate, served over HTTPS, with the cookie's Secure flag turned on,
COOKIE_SECURE=true, which also makes the server send a header telling browsers to use only HTTPS for the site (Chapters 58 and 60). The same move lifts the trial's other limit, that ordering works only on the college Wi-Fi.
A weakness written down with its reason, its limits and its fix is an engineering decision. The same weakness unmentioned is a hole the examiner finds.
Logging, and what is not logged
The server writes one line for every request, including every refused sign-in and every refusal of access, and logs unexpected errors in full; the browser is told only that something went wrong (Chapter 28). Passwords, cookies and request bodies are never logged. There is no alerting: nobody is paged when sign-ins fail in bursts. For one canteen the team accepted that and recorded it as a limitation.
Personal data
The system keeps the least personal data it can do its job with: a name, a college email address, a password hash and the orders (constraint C-6). No phone numbers, no payment details, no photographs. Chapter 8 set out the law: the Digital Personal Data Protection Act, 2023, whose main obligations come into force on 13 May 2027, and until then section 43A of the Information Technology Act. Collecting less is the cheapest security control there is: data never collected can never leak.
The worked security design
This is the table Module 2's security validation tests line by line (Chapter 65). Each control names where it lives in the code, so a reviewer can check it.
| ID | Threat | Control | Where |
|---|---|---|---|
| S1 | a student reads or cancels another's order | the owner is checked on every read and change; others' orders are "not found" | order service |
| S2 | a student uses counter or owner functions | a role guard on every route; deny by default | guards, routes |
| S3 | a stolen database reveals passwords | scrypt, N = 2^14, r = 8, p = 5, a random salt per password, constant-time comparison | passwords module |
| S4 | guessing passwords | five failed sign-ins per address and email in 15 minutes, then 429 with a time to wait | attempts module, auth service |
| S5 | weak or common passwords | 8 to 128 characters, no composition rules; common passwords refused and a password used nowhere else asked for (to be built, Chapter 46) | validation, registration page |
| S6 | a stolen or leaked session | 256-bit random token; only its SHA-256 stored; HttpOnly, SameSite=Lax, Secure under HTTPS; 8 hours; deleted at sign-out | auth service and routes |
| S7 | another site acting for a signed-in student | changes must be JSON and from this site; SameSite cookie | security middleware |
| S8 | SQL injection | every value a placeholder; column names from a fixed list | store |
| S9 | script injected through a menu item's name | text, never HTML; content security policy | ui module; security middleware |
| S10 | malformed or oversized input | every input validated on the server; 10 kB body limit; database constraints | validation, app, schema |
| S11 | error details helping an attacker | one error handler; details only in the server's log | error middleware |
| S12 | eavesdropping on the Wi-Fi | the trial's recorded weakness; fixed by a public server with HTTPS and a Secure cookie | deployment, configuration |
| S13 | leaked secrets | settings in .env, never committed | .gitignore, configuration |
| S14 | the database reached from the network | MySQL and the application listen on 127.0.0.1; only Nginx faces the network | deployment |
| S15 | a dependency with a known vulnerability | two dependencies at exact versions, installed from the lock file; checked for advisories before every release | package files |
| S16 | finding out which emails have accounts | one message for any failed sign-in, with a dummy hash checked for unknown emails so the time taken is the same | auth service |
| S17 | the site framed by another to trick clicks | the browser told never to show it inside another site's frame | security middleware |
Security Considerations
S16 has one recorded exception: registering with an address that already has an account answers "email taken", which reveals that the address is registered. The team accepted that, because only college addresses can register and the answer is the one a real student needs.
Do this for your project
- List your assets, your likely attackers and your entry points.
- Go through the OWASP Top 10 and write your answer to each category.
- Hash passwords with Argon2id or scrypt at OWASP's settings, with a salt per password; never with a fast hash.
- Choose your password rules from a standard, and write down where you differ from it and why.
- Keep sessions server-side, with only a hash of the token stored and the cookie HttpOnly, SameSite and Secure under HTTPS.
- Put a guard on every route, check ownership as well as role, and validate every input on the server.
- Keep every secret out of the repository from the first commit.
- Write your weaknesses down with their limits and fixes, and number every control so that testing can check it.
Security Considerations
Mistakes that cost marks
Passwords stored in plain text, or hashed with MD5 or SHA-256. A fast hash is not password storage.
Checks only in the pages. Anyone can send a request without the page.
Role checks without ownership checks, so any student can read any order by changing the number.
SQL built by joining strings with values from the request.
A database password in the repository, even for one commit.
"Security: we used HTTPS", with nothing else, or claiming HTTPS the deployment does not have.
A list of controls with no threats, or threats with no controls.
Quick revision
- Start from assets, attackers and entry points; every control answers one.
- OWASP Top 10:2025: A01 Broken Access Control, A02 Security Misconfiguration, A03 Software Supply Chain Failures, A04 Cryptographic Failures, A05 Injection, A06 Insecure Design, A07 Authentication Failures, A08 Software or Data Integrity Failures, A09 Security Logging and Alerting Failures, A10 Mishandling of Exceptional Conditions.
- Passwords: Argon2id or scrypt, a salt per password, never a fast hash; the worked settings are scrypt N = 2^14, r = 8, p = 5.
- Password rules: ASVS at least 8 (15 recommended); NIST 15 for single-factor; no composition rules; refuse common passwords.
- Sessions: random token, only its hash stored, cookie HttpOnly, SameSite, Secure, limited life.
- Deny by default; ownership as well as role; validate on the server; placeholders for SQL; text, not HTML.
- Secrets never committed. Weaknesses written down with limits and fixes.
Questions you must be able to answer
1. Why must passwords not be stored with SHA-256, and what should be used instead? Because SHA-256 is fast, so an attacker with a stolen table can try billions of guesses a second. A slow, memory-hard password hashing algorithm should be used instead, Argon2id or scrypt, with a unique random salt for each password, as OWASP's Password Storage Cheat Sheet advises.
2. Why does the worked project use scrypt rather than Argon2id? Because Argon2 was added to Node.js's built-in crypto module only in version 24.7.0, and the project also supports Node.js 22, where it does not exist. scrypt, OWASP's second choice, exists on every supported version; and because each hash records its algorithm and settings, the passwords can be moved to Argon2id later, at each student's next sign-in.
Security Considerations
3. How is the session cookie protected? It is a 256-bit random token, of which the database stores only a SHA-256 hash; it is HttpOnly, so scripts cannot read it; SameSite=Lax, so other sites' forms cannot send it; Secure whenever the site uses HTTPS; it expires after 8 hours and is deleted at sign-out.
4. How does the worked design prevent cross-site request forgery? Every request that changes something must be JSON, which an ordinary form on another site cannot send; if the browser names the page the request came from, it must be this site; and the SameSite cookie is not sent with another site's form posts.
5. What is the trial deployment's largest security weakness, and what is its fix? It serves plain HTTP over the college Wi-Fi, so passwords, session cookies and orders can be read by anyone able to capture traffic on that network. The fix is a public server with a name and a certificate, serving HTTPS, with the cookie's Secure flag on.
6. The worked team allows 8-character passwords although NIST says 15. Is that defensible? It is a recorded decision: it meets OWASP ASVS level 1, which requires 8 and recommends 15, and the team gave its reasons, low-value accounts, college-only registration, a limit on failed sign-ins and typing on phones, while stating that it falls short of NIST's rule for single-factor passwords and that a system guarding anything valuable should take 15.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.