Authentication

Usernames and passwords, limiting attempts, and stronger ways to prove who someone is.

F6.2Robust programsGCSE15 min

Covers OCR J277 2.3.1 Defensive design · AQA 8525 3.2.11 Robust and secure programming · Edexcel 1CP2 6.4.4 Authentication

Do this lesson in the simulator

A robot that obeys anyone who types a command is a robot anyone can crash. Authentication is checking that a user is who they claim to be before letting them in. In this lesson BugBot only drives for its registered operators, and only after they prove it.

Something you know

The most common kind of authentication is a username and password: the username says who you claim to be, and the password proves it, because only that person should know it.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

operators = {"ada": "robot42", "alan": "turing1"}

name = input("Username? ")
password = input("Password? ")
if name in operators and operators[name] == password:
    print("welcome", name)
    led("green")
else:
    print("access denied")
    led("red")

Run this in the simulator

The operators are stored in a dictionary from lesson F3.6: each username is a key, and its password is the value. name in operators checks the username exists; operators[name] == password checks the password belongs to that user, not just to someone.

Try ada and robot42, then ada and turing1. The second is a real password, but not Ada's.

Say less when it fails

Notice the program says access denied whether the username or the password was wrong. If it said "no such user", an attacker would learn which usernames exist, and could concentrate on guessing their passwords. Good authentication gives away as little as possible.

The login checks the username is known, then that the password is that user's. Both failures print the same access deniedUsername? Password?is the username known?name in operatorsyesis it that user's password?operators[name] == passwordyeswelcomenonoaccess deniednot a username at allada with turing1(a real password, but Alan's)ada with robot42
Both checks must say yes. Ada with turing1 gets past the first check but not the second, because turing1 belongs to Alan. Both ways of failing end in the same box, so an attacker cannot tell a wrong username from a wrong password.

Limiting attempts

A password can be guessed if an attacker is allowed unlimited tries. So programs allow only a few:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

operators = {"ada": "robot42", "alan": "turing1"}

def login(tries=3):
    """Ask for a username and password up to `tries` times. Return the username, or None."""
    for attempt in range(tries):
        name = input("Username? ")
        password = input("Password? ")
        if name in operators and operators[name] == password:
            return name
        print("access denied")
        tone(220, 0.3)
    return None

user = login()
if user:
    print("welcome", user)
    led("green")
    forward(50, distance=20)
else:
    print("locked out")
    led("red")

Run this in the simulator

After three wrong attempts the function gives up and returns None, which is false in the if, so the robot never moves. Real systems also lock the account for a while, or ask for another check.

Better than a password

  • Two-factor authentication asks for two different kinds of proof: something you know (a password) and something you have (a code sent to your phone).
  • Biometrics use something you are: a fingerprint or a face.
  • Real systems never store passwords as plain text the way this lesson's dictionary does. They store a hash, a scrambled version that cannot be turned back into the password, so a stolen list of hashes does not give away the passwords.
Only the hash of Ada's password is stored. At login, what is typed is hashed and the two hashes are comparedsaving Ada's passwordrobot42hash0735fdff7d...the only thingstoredcannot be turned backlogging in: hash what is typed, then comparerobot42hash0735fdff7d...same as stored:welcometuring1hash708e0755e4...not the same:access denied
A hash of the same password always comes out the same, so the system can check a password it never keeps. Someone who steals the list of hashes still does not have the passwords. The hashes shown are the start of real SHA-256 hashes.

Authentication and validation together

They answer different questions. Validation asks is this input sensible? Authentication asks is this person allowed? A robust program does both: the login is checked first, and then every command the user gives is validated.

Task: operator login

Use operators = {"ada": "robot42", "alan": "turing1"}. Ask Username? and Password? up to three times. For each wrong pair print access denied. When a pair is right, print welcome <name>, turn the LED green and drive 20 cm forward. The task types alan and wrong, then alan and turing1.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

operators = {"ada": "robot42", "alan": "turing1"}

Challenges

  1. After three failures, make the robot flash red and refuse any more attempts.
  2. Add a new operator by asking for a username and a password twice, and only saving it if both passwords match. What is that second check called?
  3. Add a validation check that passwords are at least 8 characters with at least one digit.

Past paper questions on this

Real exam questions that test this, each worked through with a program you can run.