Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Input validation with allow lists

After this lesson: I can check input against clear rules before the program trusts it.

55 min lesson2 commandsSQL injection lab: attack a weak shop search, fix the query and prove the fix

Input validation checks data before use

Input validation is checking that data has the shape a program expects before the program uses it. It matters because every box a visitor can type into is a way to hand the program something it never planned for.

A sign-up form asks for an age. One visitor types -5. Another types forty thousand letters. A program that never checked will try to use both.

By the end of this lesson

Note

I can check input against clear rules before the program trusts it.

Key words

WordWhat it meansExample
Validationchecking that input has the shape the program expects
Allow lista list of the only values or shapes that are acceptedGB, FR, DE
Deny lista list of known bad values that are refused
Rangethe smallest and largest value allowed13 to 120
Server-side checka check on the server, which the visitor cannot skip
Rejectto refuse input instead of quietly changing it

The cells students fill in are left blank in this preview.

Check the type, the length and the range

Most input has a shape. An age is a whole number. A username is short text. A quantity is a small number above zero.

Check three things. The type: is it digits when it should be? The length: is it short enough? The range: is the number between the smallest and largest value you allow? Refuse anything that fails any of the three.

Worked example: rules for a sign-up form

FieldRuleAcceptedRefused
Username3 to 20 letters, digits or _maria_22ma, a b!
AgeWhole number from 13 to 1201912, 19.5, abc
CountryOne of GB, FR, DE, JP, ESGBUK, empty
QuantityDigits only, 1 to 1040, 11, -2

The cells students fill in are left blank in this preview.

A quantity check

Code: python
# Change text to "12", then "-3", then "x", then "" and press Run each time.
text = "7"
if text.isdigit() and 1 <= int(text) <= 10:
    print("accepted")
else:
    print("refused")

Allow lists beat deny lists

A deny list names the bad input and accepts the rest. It fails the day an attacker finds a bad input you did not name.

An allow list names the good input and refuses the rest. A country code must be one of five codes. Anything else is refused, however clever it is. Use an allow list wherever the good values can be listed.

Worked example: a deny list leaks

Typed textDeny list of <script>Allow list of letters
mariaAcceptedAccepted
<script>RefusedRefused
<SCRIPT>Accepted by mistakeRefused
<img src=x>Accepted by mistakeRefused

The cells students fill in are left blank in this preview.

Two lists, one input

Code: python
# Change country to "GB", then to "<script>", and press Run.
country = "<b>"
deny_list = ["<script>", "<iframe>"]
allow_list = ["GB", "FR", "DE", "JP", "ES"]
print("deny list accepts:", country not in deny_list)
print("allow list accepts:", country in allow_list)

Validate on the server and reject bad input

A form can check input in the visitor's browser. That helps honest visitors. It protects nothing, because an attacker can send the request without using your page at all.

The server-side check is the one that counts. When input fails it, reject it with a short message. Do not quietly repair it. A repaired value can turn out to be a new valid attack.

A common mistake

Warning

Many people think a check in the browser is enough, such as a maximum length on the box. It is not. An attacker skips the page and sends the request directly. Keep the browser check for kindness, and repeat every rule on the server.

See what the shop checks

Your lab shop is a search page that takes one word. The next two lines send it harmless requests. The first sends no word at all and counts the lines that come back. The second sends a word a hundred letters long and prints only the status number.

A search with no word at all

Command
curl -s http://app:8000/search | wc -l

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A search with a very long word

Command
curl -s -o /dev/null -w "%{http_code}\n" "http://app:8000/search?q=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

What the shop does not check

The first line prints 5: with no word, the shop searches for nothing, and all five public products match. The second prints 200: it accepted a hundred letters without comment.

Neither is an attack. Both show a shop with no rules about presence or length. Ask what rules the search box should have.

About this lab and its source: SQL injection lab: attack a weak shop search, fix the query and prove the fix

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.