Robust and Secure Programming
Validation, authentication, defensive design and testing, plus the three kinds of error and how to find each one, written around the idea that a program must survive users who do the wrong thing.
Validation and authentication
Validation checks that data entered is reasonable. It cannot check that data is correct, and knowing the difference is worth marks.
| Check | What it tests | Example |
|---|---|---|
| Range | The value falls between two limits | An age between 0 and 120 |
| Presence | Something was actually entered | A name box is not left blank |
| Length | The right number of characters | A password of at least 8 characters |
| Type | The data is of the expected type | A quantity is a whole number |
| Format | The data matches a required pattern | An email contains an at sign |
| Look up | The value is one of a known set | A county chosen from a list |
A date of birth of 01/01/1990 passes every check you can write, and is wrong if the person was born in 1991. Validation checks plausibility, nothing more. Say that in an exam and you will separate yourself from most candidates.
Authentication
Authentication checks that a user is who they claim to be. Usernames and passwords are the common method, often strengthened by:
- requiring a minimum length and a mixture of character types,
- limiting the number of failed attempts before an account is locked,
- asking for specific characters from a memorable word,
- two factor authentication, which requires something you know and something you have.
Authentication answers "who are you". Validation answers "is that a sensible thing to type". They are different jobs and questions distinguish them.
- Validation checks: presence, type, range, length, format and lookup.
- Validation checks that data is sensible. It cannot know whether it is true.
- Verification asks the user again, for example with a confirm password box, to catch typing slips.
- Three kinds of evidence: something you know, have or are.
- Two factors together are far stronger than one: a stolen password alone is not enough.
- Passwords should be stored as hashes, never as typed, so a leak does not reveal them.
Defensive design and maintainability
Defensive design means writing a program that keeps working when things go wrong, rather than one that only works when everything goes right.
Anticipate misuse. Assume every input will eventually receive an empty string, a negative number, a word where a number was expected and a value a thousand times larger than you planned for. Handle each of those deliberately.
Fail helpfully. A message saying what was wrong and what to do lets the user recover. A crash does not.
Authenticate before acting. Check permission before doing anything sensitive, not after.
Maintainability
Maintainable code is code that another programmer, or you in a year, can safely change.
- Meaningful identifiers.
totalPricerather thantporx2. - Comments that explain why, not what.
count ← count + 1does not need a comment saying it adds one. It might need one saying why the first record is skipped. - Indentation that reveals structure, so the body of a loop is visibly inside it.
- Subroutines so each task lives in one named place.
A question asking how a program could be made more maintainable expects specific examples, so name a poor identifier in the code you have been given and say what you would rename it to.
Testing and the three kinds of error
Syntax errors
A syntax error breaks the rules of the language, such as a missing bracket or a misspelled keyword. The program will not run at all, and the translator reports the error, usually with a line number.
Logic errors
A logic error means the program runs perfectly and produces the wrong answer. Using + where you meant -, or < where you meant ≤, gives a logic error. Nothing reports it, which is precisely why these are the expensive ones.
Runtime errors
A runtime error happens while the program is executing: dividing by zero, reading past the end of an array, opening a file that does not exist. The program starts and then stops.
Test data
Every test plan needs three kinds of data, and AQA asks for them by name.
| Type | Meaning | Example for an age between 0 and 120 |
|---|---|---|
| Normal | Typical data the program should accept | 35 |
| Boundary | Data at the very edge of what is acceptable | 0, 120, and just outside, -1 and 121 |
| Erroneous | Data the program should reject | "cat", an empty entry, 3.5 |
Almost every off by one error shows up at a boundary and nowhere else. A program tested only with 35 will pass while rejecting every newborn and accepting every age up to 121.
Iterative and final testing
Iterative testing happens during development, module by module, so faults are found while the code is still fresh and small. Final testing happens once the program is complete and checks the whole thing against the original requirements. Both are needed: iterative testing finds faults cheaply, final testing proves the program does the job it was asked to do.
- Syntax error: the code breaks the rules of the language, so it will not run at all.
- Runtime error: the program starts, then crashes when it meets something it cannot do.
- Logic error: it runs without complaint but gives the wrong answer, so only testing finds it.
- Test data for an age from 13 to 19, on a number line.
- Normal data is typical and should be accepted.
- Boundary data sits on and just outside each edge, because that is where mistakes hide.
- A test plan records expected against actual. A failed test shows what to fix.
Key terms you must be able to define
- Validation
- An automatic check that data entered is reasonable and of the expected form.
- Authentication
- Checking that a user is who they claim to be, usually with a username and password.
- Range check
- A validation check that a value falls between an upper and a lower limit.
- Presence check
- A validation check that a required value has actually been entered.
- Format check
- A validation check that data matches a required pattern.
- Syntax error
- An error that breaks the rules of the programming language, preventing the program from running.
- Logic error
- An error in which the program runs but produces the wrong result.
- Runtime error
- An error that occurs while the program is executing, causing it to stop.
- Normal test data
- Typical data that the program should accept and process correctly.
- Boundary test data
- Data at the very edge of the acceptable range, on both sides of the limit.
- Erroneous test data
- Data the program should reject, used to check the validation works.
- Iterative testing
- Testing carried out repeatedly during development as each part is written.
Flashcards
Say the definition out loud before you flip the card. Retrieving it from memory is what builds recall, reading it again is not.
Reach the top grade on this topic
The strongest answers here understand what each technique can and cannot do.
They limit validation honestly. A grade 9 answer says validation confirms data is plausible, and explicitly adds that it cannot confirm data is true. That single sentence appears in mark schemes.
They choose test data with a reason. Not "I would test with 0 and 120" but "I would test with 0 and 120 because they are the boundaries, and with -1 and 121 because an off by one error in the comparison would only show up there".
They tell the three errors apart by symptom. Will not run at all is syntax. Runs and gives the wrong answer is logic. Starts and then stops is runtime. Answering by symptom rather than by definition shows understanding.
Mistakes that cost marks here
- Saying validation makes sure data is correct. It makes sure data is reasonable.
- Confusing validation with verification. Verification checks data was entered accurately, for example by typing it twice.
- Giving only one boundary value. A boundary needs testing on both sides of the limit.
- Calling a crash a logic error. If the program stops while running it is a runtime error.
- Writing comments that restate the code. A useful comment explains why, not what.
In 1996 the first Ariane 5 rocket destroyed itself 39 seconds after launch. The cause was a 64 bit real number being converted into a 16 bit integer that could not hold it, in code that was not even needed after lift off.
Knowledge check
Exam-style questions
Five written questions in the style of the real paper. Write a full answer, then let the marker check it against the mark scheme.
- LAn automatic check that data entered is sensible or reasonable
Validation is an automatic check carried out by a program to make sure that data entered is reasonable and of the form expected.
- LNormal data is a typical value inside the range, such as 55
- LBoundary data is at the edge of the range, such as 0, 100, -1 or 101
- LErroneous data is something that should be rejected, such as a word or a blank entry
Normal test data would be 55, a value comfortably inside the acceptable range that the program should accept. Boundary test data would be 0 and 100, the two limits, together with -1 and 101 just outside them, since an incorrect comparison would only show up there. Erroneous test data would be a word such as cat, or a blank entry, which the program should reject rather than process.
- LA syntax error breaks the rules of the programming language
- LA program containing a syntax error will not run, and the translator reports it
- LA logic error means the program runs but produces the wrong result
- LNothing reports a logic error, so it must be found by testing or by tracing the program
A syntax error breaks the rules of the programming language, for example a missing bracket or a misspelled keyword. The translator cannot make sense of the line, so the program will not run at all and the error is reported, usually with a line number, which makes it comparatively easy to fix. A logic error is different in kind. The code is perfectly valid, so the program translates and runs, but the instructions do not do what the programmer intended, so the output is wrong. Nothing reports it, which means it can only be found by testing with data whose correct answer is already known, or by tracing the program by hand.
- LValidation only checks that the data is of a reasonable form
- LA postcode can be correctly formatted but not be the customer's actual postcode
- LValidation cannot check truthfulness, only plausibility
Validation checks the shape of the data, so a format check can confirm that the entry looks like a postcode and a look up check can confirm that it is a postcode that exists. Neither of those establishes that it is the right postcode for this customer. Somebody can enter a real, correctly formatted postcode belonging to a different address, either by mistake or deliberately, and every check will pass. Validation tests whether data is plausible, and no automatic check on the data alone can test whether it is true.
- LUse meaningful identifier names that say what the value holds
- LSo another programmer can understand the code without tracing it
- LUse comments to explain why the code does what it does
- LUse subroutines and indentation so the structure is visible and each task lives in one place
The first is to use meaningful identifiers. A variable called totalPrice tells the next reader what it holds, whereas one called tp or x2 has to be traced through the whole program before it can be safely changed. The second is to use subroutines with sensible indentation, so that each task lives in one named place and the structure of the program is visible on the page. Together those mean that a change to how something is calculated is made once, in an obviously named place, with far less risk of breaking something elsewhere. Comments explaining why an unusual decision was taken support both.