Never Trust Anything: A Rant About User Input Validation
If you’ve been writing code for more than five minutes, you’ve probably realized one universal truth: users will break your shit. Not intentionally (usually), but they will. They’ll put emojis in a phone number field, paste a novel into a “name” input, and somehow bypass your perfectly crafted front-end validation to submit absolute garbage.
That’s why you should never trust anything — especially user input. Every single piece of data that comes into your application needs to be treated like it’s out to ruin your day. Because if you don’t validate it properly, it eventually will.
Validate Everything, All the Time
It doesn’t matter if it’s coming from a form, an API request, or an “internal” system you swear only your trusted team will use (It wont be). Always validate, always sanitize, always assume the worst.
Laravel Makes This Easy (So Use It)
If you’re using Laravel and not leveraging its awesome validation rules, what are you even doing? Laravel has an easy, extendable validation system that makes all of this stupidly easy. Use it in every controller method that processes input. No exceptions. Here are some examples
Text & Email Inputs
Will it fit the database column? No? Why have a Database Exception hit your Monitoring solution at 3AM? Is it safe from XSS? If you don't know what that is, stop everything and go Google it. Now (No seriously you really should know what that is if you are reading this)
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|max:255|unique:users,email',
# Optional but still validated
'bio' => 'nullable|string|max:500|bad_word:en,de',
]);
- required: Field must be filled.
- string: Must be a string (no numbers or arrays sneaking in).
- max:255: Limits database column overflow.
- email: Ensures a valid email format.
- unique:users,email: Prevents duplicate emails.
- bad_word: This one is from a package as an example, how easy extending these rules is.
Number Inputs
Are there any constraints beyond just “is it a number”? Does it fall within a reasonable min/max range? A Booking Provider should probably define a maximum Group size. A “percentage” field shouldn’t accept 99999. And remember kids, a negative number is still a number!
$request->validate([
'age' => 'required|integer|min:18|max:100',
# TODO update according to next gens life expectancy
'price' => 'required|numeric|min:0|max:9999.99',
'guests' => 'nullable|integer|min:1|max:40'
]);
- integer: Ensures it’s a whole number.
- numeric: Allows decimals.
- min & max: Keeps the values reasonable.
Select & Radio Buttons
Did the user actually pick a valid option? If your dropdown has bad, okay and good. and the form submits
sucks ass something has gone very, very wrong.
$request->validate([
'rating' => 'required|in:bad,okay,good',
]);
Checkboxes
Did the required ones actually get checked? Users will forget. Every. Single. Time.
$request->validate([
# Must be checked (1 or "on")
'terms' => 'accepted',
# Can be true/false, 1/0
'notifications' => 'boolean',
]);
- accepted: Ensures “terms” is checked.
- boolean: Allows true/false values, useful for JSON apis.
Seriously, it’s like a seatbelt — skip it, and eventually, you’re going to crash.
Here are some more awesome/useful things:
$request->validate([
'list' => 'required|array',
# Ensures the List only contains strings
'list.*' => 'required|string',
'assoc' => 'required|array',
# Ensures JSON structure
'assoc.nested.key' => 'required|string',
# regular expressions!
'color' => 'regex:/^#[\da-f]{6}$/i',
'steps' => ['required_without:increment,from,to', 'array'],
'steps.*' => ['required', 'integer', 'min:1'],
'from' => ['required_without:steps', 'required_with:to,increment', 'integer', 'min:1', 'lt:to'],
'to' => ['required_without:steps', 'required_with:from,increment', 'integer', 'min:1', 'gt:from'],
'increment' => ['required_without:steps', 'required_with:from,to', 'integer', 'min:1', 'lt:to'],
]);
The last one is a tiny bit more complex, it basically validates either steps or from,to and increment are filled,
it checks from is smaller than to and vice versa and also the increment value should not be larger than to. (There
is also required_if:truthy_field and
required_unless:truthy_field) Go check them out
So, to sum up: never trust user input. Validate everything. Assume users are trying to break your app (even if they don’t mean to). Laravel gives you the tools to do this effortlessly — so use them. Otherwise, you’re just asking for a bad time.