Information Security

04

Access Control

The Four A's · Access Control Models · Passwords · Second Factors · Biometrics

Fall 2026 · Amirkabir University of Technology
@1995parham

The Most-Used Service

  • Lecture 3 ended on a key that proves possession, not permission. Permission is X.800's access control service
  • The question is always the same shape: may this subject perform this action on this object?
  • Subject — a user, a process, a service. Object — a file, a row, an endpoint

Everything in this lecture is one function: allow(s, a, o)

Lecture 1's grade service answered half of it: it checked the role, but took the name from an X-User header that anyone can type. This lecture builds the other half.

The Four A's

AuthenticationAre you who you claim to be?
AuthorizationGiven that, are you allowed to do this?
AccountingWhat did you consume, and for how long?
AuditingWhat did you do, recorded so it can be reviewed
  • RADIUS and Diameter ship the first three as AAA; the fourth is added when the record has to survive a dispute
  • Some texts split identification off from authentication instead — claiming a name is not the same as proving it

Identify, Then Authenticate, Then Authorize

  • Identification — "I am parham". Public, and not a claim of anything
  • Authentication — evidence for that claim
  • Authorization — the rights that identity carries

They fail differently, and confusing them is a real bug class. An authenticated user who reaches another user's records has an authorization failure — the login worked perfectly.

That bug has a name — broken access control — and it has been number one in the OWASP Top Ten, the industry's list of web-application risks, since 2021. Lecture 6 returns to it.

The Four Steps, Drawn

A grade request passes identify, authenticate and authorize; a failed login exits with 401, a failed role check with 403, and every exit is written to the audit log

By the end of this lecture each box is one line of the grade service. Lecture 1 had only the last two.

Audit Is Not Optional

  • Prevention fails. The log is what turns a breach into a known breach
  • Record the decision, not just the action: who, what, when, and allowed or denied
  • Denied attempts are the interesting ones. A thousand denials from one account is the alert
  • Send logs somewhere the subject cannot edit — an attacker with root edits the local log first
  • Do not log the secret. Passwords, tokens and card numbers in a log file are a breach waiting for a log reader

The Access Matrix

The abstract model: subjects down the side, objects across the top.

An access matrix of parham, student and root against grades.db, report.pdf and /etc/shadow; the grades.db column is marked as an ACL kept with the object, the student row as a capability held by the subject

Nobody stores it as a matrix — it is enormous and mostly empty. You store it by column or by row, and that choice has consequences.

Columns or Rows

  • Access control list — store a column, attached to the object
    • "Who may touch this file?" is instant
    • "What may this user touch?" means walking every object in the system
    • Unix permissions, Windows ACLs, S3 bucket policies
  • Capability — store a row, held by the subject as an unforgeable ticket
    • Fast to check, easy to delegate — and hard to revoke
    • A bearer token — whoever holds it may use it — is a capability. Lecture 6 worries about where it is stored

DAC — the Owner Decides

  • Discretionary access control: whoever owns an object sets its permissions
  • Flexible, familiar, and the default nearly everywhere
  • Two weaknesses, both structural:
    • One careless owner can expose anything they own — chmod 777 is one keystroke
    • Rights are transitive. A program you run acts with your rights, so any program you run can give your data away

DAC on This Machine


$ ls -l salaries.txt
-rw-r--r--  1 parham  wheel  7 Aug 18 00:51 salaries.txt

$ chmod 640 salaries.txt && ls -l salaries.txt
-rw-r-----  1 parham  wheel  7 Aug 18 00:51 salaries.txt

$ chmod u=rw,g=r,o= salaries.txt      # the same thing, spelled out

$ umask
022
    

umask 022 is why the file was world-readable when it was created. The default is permissive, and defaults are what most files get.

Two Bits Worth Knowing


$ ls -l /usr/bin/su
-rwsr-xr-x  1 root  wheel  121904 Jun 25 02:29 /usr/bin/su

$ ls -ld /private/tmp
drwxrwxrwt  110 root  wheel  3520 Aug 18 00:51 /private/tmp
    
  • s — setuid. The program runs as its owner, not as you. That is how an ordinary user changes a password in a root-owned file
  • t — sticky. Everyone may write in /tmp, but only the owner of a file may delete it
  • Every setuid binary is a privilege boundary — a bug in one is a local root exploit

MAC — the System Decides

  • Mandatory access control: a system-wide policy overrides the owner. You cannot share what the policy forbids, even if you want to
  • Every subject has a clearance, every object a label, and the rules compare them
  • Bell–LaPadula protects confidentiality
    • No read up — you cannot read above your clearance
    • No write down — and you cannot copy it downward either
  • Biba protects integrity, and is exactly upside down: no read down, no write up
  • SELinux, AppArmor, iOS and Android sandboxes are all MAC

Same three letters, different idea: lecture 3's MAC is a message authentication code.

RBAC — the Role Decides

  • Rights attach to roles; users are assigned roles. Nobody is granted a permission directly
  • It matches how organizations actually work, which is why it won: teaching assistant is a job, and the job has a fixed set of rights
  • When someone changes job you change one assignment, not forty permissions
  • Two refinements that matter in practice:
    • Role hierarchy — professor inherits everything a lecturer may do
    • Separation of duty — no one person may hold both request payment and approve payment

You Already Wrote One

Lecture 1's fix, read with this lecture's eyes:


var roles = map[string]string{"parham": "instructor", "4001": "student"}

if roles[user] != "instructor" {
	log.Printf("DENIED grade %s=%d by %s", id, grade, user)
	http.Error(w, "forbidden", http.StatusForbidden)
	return
}
    
  • The map is the user → role assignment. The if is the role → right rule. That is RBAC
  • A new teaching assistant is one line in the map, not a new if
  • It is only as good as user, and user still comes from a header. The rest of this lecture fixes that

In the Wild: Twitter, 2020

  • 15 July: callers posing as the IT help desk sent staff to a look-alike VPN login page. Some approved the MFA push that followed
  • From inside, they found staff who held the internal account-support tool, and took over 130 accounts — Obama, Bezos and Musk among them — for a bitcoin scam
  • The tool could take over any account, and over 1,000 employees held that role. No least privilege, no separation of duty
  • The regulator's fix: fewer holders, a second approver for high-risk actions, and hardware keys, which it said would have stopped the attack

Source: NY Department of Financial Services, Report on Investigation of Twitter's July 15, 2020 Cybersecurity Incident

Twitter 2020, Drawn

A phone call sends an employee to a look-alike VPN page; the attacker replays the password on the real VPN and the employee approves the MFA push; inside, the attacker finds holders of the account-support tool, held by over 1,000 staff, and takes over 130 accounts

Two places it could have stopped, both in this lecture: the role check on this slide's model, and a second factor a fake page cannot relay — FIDO2, in the "have" section.

ABAC — the Context Decides

  • RBAC cannot express "during office hours", "from a managed device", "for their own department" without inventing a role per combination — role explosion
  • Attribute-based access control evaluates a rule over attributes of the subject, the object, the action and the environment
  • Very expressive, and much harder to answer the auditor's question: "who can read this today?"
  • Most real systems are RBAC with a few attributes bolted on, and that is a reasonable place to land

Choosing

DACOwner sets itFlexibleLeaks by accident
MACPolicy sets itStrong, uniformRigid, heavy to administer
RBACRole sets itMatches the org chartRole explosion
ABACRules over attributesExpressive, contextualHard to audit

Whichever you pick, the two principles from lecture 1 still decide the outcome: least privilege and deny by default.

Every model trusts the subject's name. The rest of the lecture is how the system knows that name is true.

Three Kinds of Evidence

Something you knowPassword, PIN, the answer to a question
Something you haveA phone, a security key, a smart card
Something you areFingerprint, face, iris, the way you type
  • Multi-factor means factors from different rows. A password plus a security question is one factor asked twice
  • MFA is the single highest-value control on this slide, because it breaks the attack that scales: stolen passwords
  • The next three sections take the rows in order: know, have, are

Passwords Are Still Here

  • Cheap, universal, need no hardware, and everyone already knows how they work
  • And they are reused, guessable, phishable, and shared over chat
  • Two problems that need separating:
    • How the user chooses one — a human-factors problem
    • How you store it — an engineering problem, and entirely your fault when it goes wrong

In the Wild: RockYou, 2009

  • December 2009: an SQL injection flaw in RockYou.com, a maker of social-network apps, exposed about 32 million accounts
  • The passwords were stored in plain text — no hash at all. The injection is lecture 6's bug; the storage is entirely the site's fault
  • Many users had reused their webmail password, so one leak opened other accounts: credential stuffing, at the end of this section
  • The list lives on as rockyou.txt, shipped in Kali Linux as a standard cracking wordlist

Source: The Register, RockYou password snafu exposes webmail accounts · Kali Linux, wordlists

In the Wild: Adobe, 2013

  • October 2013: a leaked Adobe file held over 130 million accounts; Adobe put the active users at about 38 million
  • The passwords were not hashed but encrypted — Triple DES, in ECB mode, with one key for every password
  • Lecture 3's ECB picture again: the same password gave the same ciphertext, and the users' password hints sat beside it in plain text. Nearly 1.9 million accounts shared the ciphertext of 123456
  • Encryption can be undone by whoever holds the key. A password store must not be undoable at all

Source: CSO Online, Adobe confirms stolen passwords were encrypted, not hashed

Adobe 2013, Drawn

Three users with the same password: under one-key 3DES-ECB they share one ciphertext block and their hints are in plain text beside it; under a salted KDF each stored value is unrelated

The researchers never needed Adobe's key. Identical blocks and the hints beside them were enough.

Never Store It Twice the Same


$ printf 'hunter2' | shasum -a 256
f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7
$ printf 'hunter2' | shasum -a 256
f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7

$ openssl passwd -6 hunter2
$6$MrTqQCcVCjfH/jFG$rM65BZ4dxysbt8.J82C6D8e5IW8sC.3Ufq...
$ openssl passwd -6 hunter2
$6$9kSX7tN9L2s6M5N.$g.pyl63tv1ad97rS8/Il3yZSEaZ34j25vs...
    
  • Unsalted: the same digest, on every machine, forever. Precompute it once and it is broken everywhere — a rainbow table
  • Salted: a random value per password, stored beside it. Two identical passwords now look nothing alike, and every attack must be redone per user

The Salt, Drawn

hunter2 hashed twice with SHA-256 gives the same digest f52fbd32 both times; hunter2 with salt MrTqQCcV and with salt 9kSX7tN9 gives two unrelated SHA-512 crypt hashes

The values are the ones from the run on the previous slide. The salt is not a secret; its only job is to make every user a separate attack.

In the Wild: LinkedIn, 2012

  • June 2012: about 6.5 million LinkedIn password hashes were posted to a password-cracking forum
  • They were SHA-1 with no salt — the left half of the drawing. Everyone who chose the same password had the same hash, so one guess cracked them all
  • In 2016 the same breach turned out to hold about 117 million accounts, offered for sale. LinkedIn reset passwords unchanged since 2012
  • A salt alone would not have saved them: SHA-1 is fast. The next two slides are the rest of the fix

Source: Krebs on Security, As Scope of 2012 Breach Expands, LinkedIn to Again Reset Passwords for Some Users

Salt Is Not Enough

A salt stops precomputation. It does nothing about speed — and speed is what an offline attacker has: one who stole the hash file and guesses on their own hardware, with no login form to slow them.


sha256(password)                      0.000 ms/hash    3,682,588 guesses/sec
pbkdf2-hmac-sha256, 600k iter        57.918 ms/hash           17 guesses/sec
scrypt, N=2^17 r=8 p=1              198.368 ms/hash            5 guesses/sec
    
  • On one laptop core. A password hashed with SHA-256 falls 200,000 times faster than the same password behind PBKDF2, and 700,000 times faster than behind scrypt
  • A password hash is supposed to be slow. It is the only place in this course where slow is the requirement

What To Actually Use

A password hash (or KDF, key derivation function) is a hash built to be slow:

  • Argon2id — the current recommendation. Tunable in time, memory and parallelism
  • scrypt · bcrypt — both fine, both still widely deployed
  • PBKDF2 — acceptable, and often the only option in a certified environment
  • Never a bare MD5, SHA-1 or SHA-256
  • Memory-hard ones need RAM per guess, which takes away the GPU's thousands of cores
  • Tune the cost so verification takes a few hundred milliseconds on your hardware, and revisit it as hardware improves

The Grade Service Gets a Login


// bcrypt, cost 12: the salt and the cost are stored inside each hash
var users = map[string]string{
	"parham": "$2a$12$PED31kv6I3xrnjq.XvRgN.q5BIlLvFA6Ae2BklreAvhrgcLO/RZz6",
	"4001":   "$2a$12$fHdcH0q0mpgv4UNlutXXQuEmZDZ2eAqQ5tPaTvGd9DIH9XMtbSUmy",
}

func login(r *http.Request) (string, bool) {
	name, pass, ok := r.BasicAuth()
	if !ok {
		return name, false
	}
	err := bcrypt.CompareHashAndPassword([]byte(users[name]), []byte(pass))
	return name, err == nil
}

	user, ok := login(r)
	if !ok {
		log.Printf("FAILED login as %q", user) // the name, never the password
		http.Error(w, "login first", http.StatusUnauthorized)
		return
	}
    

The last block replaces r.Header.Get("X-User"). The role check from lecture 1 stays exactly as it was.

Three Requests, Three Answers


$ curl -u parham:guess -d 'id=4002&grade=18' localhost:8080/grade
login first
$ curl -u 4001:tulip-river-42 -d 'id=4001&grade=20' localhost:8080/grade
forbidden
$ curl -u 'parham:correct horse battery staple' \
       -d 'id=4002&grade=18' localhost:8080/grade
4002 is now 18

$ cat server.log
2026/10/14 09:49:04 FAILED login as "parham"
2026/10/14 09:49:04 DENIED grade 4001=20 by 4001
2026/10/14 09:49:05 grade 4002=18 by parham
    
  • 401 is authentication failing. 403 is authorization failing. Two different bugs, two different codes
  • Every exit is in the log, and no password is

What the Clock and the Wire Say


$ curl -s -o /dev/null -w '%{http_code} in %{time_total}s\n' \
       -u parham:guess -d 'id=4002&grade=18' localhost:8080/grade
401 in 0.184779s
$ curl -s -o /dev/null -w '%{http_code} in %{time_total}s\n' \
       -u nobody:guess -d 'id=4002&grade=18' localhost:8080/grade
401 in 0.000426s

$ echo NDAwMTp0dWxpcC1yaXZlci00Mg== | base64 -d
4001:tulip-river-42
    
  • 0.19 s is the KDF doing its job: about five guesses a second, per core
  • 0.0004 s says nobody is not an account. The clock leaks which names exist. Exercise: fix it
  • The header is base64, not encryption, and it is sent on every request. Sessions and TLS are lecture 6

The Rules Changed

NIST SP 800-63B says most of the old advice was counterproductive:

  • No forced periodic rotation — it produces Summer2026! then Autumn2026!. Rotate on evidence of compromise
  • No composition rules — demanding a symbol and a digit adds almost no entropy and a great deal of annoyance
  • Length over complexity — allow long passphrases, and allow every character including spaces
  • Check against known-breached lists — this is the change that actually removes weak passwords
  • Allow paste, so password managers work. Blocking it drives people to worse passwords

Attacks on Passwords

Online guessingAgainst your login form. Rate limit, lock out, add delay
Offline crackingAgainst a stolen database. Only the KDF slows it down
Credential stuffingReusing another site's breach. Nothing you do to your own hashing helps
Password sprayingOne common password against every account, to stay under the lockout
PhishingThe user types it in for you. Beats every storage decision

Only two of the five depend on how you hash. The other three need a second factor.

In the Wild: Colonial Pipeline, 2021

  • May 2021: ransomware took Colonial's systems offline, and the fuel pipeline with them, for days
  • The way in, the CEO told the US Senate: one password for a legacy VPN account that was no longer meant to be in use
  • "It was a complicated password" — so strength did not matter. It was stolen, not guessed: the bottom rows of the table before
  • It "only had single-factor authentication". A second factor — the next section — or deleting the unused account would have stopped it

Source: Reuters (via Carrier Management), Hackers Stole a Single Password to Disrupt Colonial Pipeline

Something You Have

The factor a stolen password does not bring with it:

  • OTP over SMS — better than nothing, and the weakest of these. SIM swap, SS7 interception, and the code is phishable
  • TOTP — an authenticator app. A shared secret plus the clock, so it works offline
  • FIDO2 / WebAuthn — a key pair per site, held in hardware
    • The one design that is phishing-resistant: the browser signs over the real origin, so a look-alike domain gets a signature that does not verify. Twitter 2020's fake VPN page would have got nothing
    • Nothing shared, nothing to type, nothing to leak from the server

In the Wild: Reddit, 2018

  • June 2018: an attacker got into a few Reddit employees' accounts at its cloud and source-code hosting providers
  • Those accounts had a second factor — codes by SMS. The attacker intercepted the texts sent to the employees' phone numbers
  • The attacker read source code and internal files, and took an old backup of early users: usernames, salted password hashes, emails
  • Reddit moved its staff to token-based 2FA. SMS is a second factor, but the weakest on the previous slide

Source: BleepingComputer, Reddit Announces Security Breach After Hackers Bypassed Staff's 2FA

TOTP Is Twenty Lines


counter := make([]byte, 8)
binary.BigEndian.PutUint64(counter, uint64(t/step))

mac := hmac.New(sha1.New, secret)
mac.Write(counter)
sum := mac.Sum(nil)

offset := sum[len(sum)-1] & 0x0f            // dynamic truncation
code := binary.BigEndian.Uint32(sum[offset:offset+4]) & 0x7fffffff
    

T=59           code=94287082
T=1111111109   code=07081804
T=1111111111   code=14050471
T=1234567890   code=89005924
T=2000000000   code=69279037
    

Those are the RFC 6238 test vectors, reproduced exactly.

What That Code Tells You

  • It is an HMAC of the current time, cut down to six or eight digits. No magic
  • The server needs the same secret — so a server breach hands over everyone's second factor
  • Both sides need the same clock. That is why a drifting phone stops working, and why servers accept a window of one or two steps
  • And it is phishable: a fake site asks for the six digits and forwards them within thirty seconds. WebAuthn exists because of this

Something You Are

  • Fingerprint · face · iris · retina · voice · hand geometry · gait · keystroke dynamics
  • Two phases: enrollment builds a template, then each use compares a fresh sample against it
  • The comparison is never exact. Your finger is never presented twice the same way, so the system computes a similarity score and compares it to a threshold
  • That threshold is the whole design

FAR, FRR and the Threshold

As the threshold moves from lenient to strict, the false acceptance rate falls and the false rejection rate rises; they cross at the equal error rate; a phone unlock sits on the lenient side, a vault door on the strict side

One knob moves both. The equal error rate (EER) compares systems; the setting you deploy is chosen by consequence. A high FRR has a security cost too — it is what makes staff prop the door open.

The Two Facts People Forget

A biometric is a username, not a password.

  • It is not secret. You leave fingerprints on every glass and your face is on every camera
  • It cannot be revoked. A leaked password is changed in a minute. A leaked fingerprint template is leaked for life, and you have ten
  • So: excellent as a convenient local factor beside a device you hold; poor as a remote credential on its own
  • This is exactly how phones use it — the fingerprint unlocks a key held in secure hardware; it is never sent anywhere

Attacking and Defending It

  • Presentation attack — a lifted print in gelatin, a printed photograph, a recorded voice
    • Answer: liveness detection — pulse, depth, involuntary movement
  • Template theft — the stored template is the irrevocable part
    • Answer: never store raw biometrics; keep the template in secure hardware, on the user's own device
  • Bias — error rates differ measurably across demographic groups. A single published accuracy figure hides that
  • And a legal dimension the other factors do not have: biometric data is special-category personal data nearly everywhere

In the Wild: Touch ID, 2013

  • September 2013, the month the iPhone 5s shipped: the Chaos Computer Club unlocked its Touch ID sensor with a fake finger
  • The print was photographed from a glass surface at 2400 dpi, laser-printed onto a transparency, and filled with latex milk or wood glue
  • A presentation attack, exactly. The sensor checked the pattern, and the fake carried the pattern
  • It is the "not secret" fact two slides back: the owner had left the print on a glass

Fine as a local factor beside a device you hold. Never the only one.

Source: Chaos Computer Club, Chaos Computer Club breaks Apple TouchID

Check Yourself

  1. A student logs in with the right password and changes another student's grade. Which step failed — identify, authenticate or authorize? Which status code should the grade service have sent?
  2. An ACL and a capability store the same matrix. Which one answers "what may this user touch?" quickly, and which one is hard to revoke?
  3. Two users choose hunter2. Why do their stored hashes differ, and why is a salt alone still not enough?
  4. A fake site asks for your six-digit code. Why does that work against TOTP but not against WebAuthn?

Where This Leaves Us

  • Authenticate and authorize are two checks with two failures: 401 and 403. Authorize with roles, default deny, least privilege
  • Store passwords with a slow, salted KDF, add a second factor that resists phishing, and keep biometrics local
  • Log every decision, including the denials, somewhere the subject cannot reach

The grade service now has a slow, salted hash, a role check and a log. It has no second factor, and it sends the password in the clear on every request — lecture 6 fixes that.

Next: the same questions, one layer down — where the subject is a process and the object is the file system.

Fork me on GitHub