PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
172: Preventing Mass Assignment Vulnerabilities
Wait, how does "mass assignment" actually happen in my code?
It usually happens when you're trying to be efficient. You've got a User profile page where a user can update their bio and location. Instead of writing a line for every single field, you might be tempted to just loop through the $_POST array and update the database record directly.
Here is a classic example of how I've seen developers shoot themselves in the foot:
// DANGER: This is a mass assignment vulnerability
$userId = $_SESSION['user_id'];
$updates = $_POST; // Just grabbing everything from the request
$sql = "UPDATE users SET ";
$sets = [];
foreach ($updates as $column => $value) {
$sets[] = "$column = " . $db->quote($value);
}
$sql .= implode(', ', $sets) . " WHERE id = " . (int)$userId;
$db->query($sql);
On the surface, this looks clever. But here's the problem: the code doesn't care if those fields were actually on your HTML form. An attacker doesn't have to use your form; they can use a tool like Postman or curl to send a request containing is_admin=1. Since your loop blindly accepts everything in $_POST, they've just promoted themselves to administrator.
Can't I just trust the fields I put in my HTML form?
Absolutely not. This is a common mistake when you're first starting out. You have to remember that the browser is essentially a suggestion engine. A user can right-click "Inspect Element" and add a new input field to your form in two seconds, or they can bypass the browser entirely and send a raw HTTP request to your server.
If your PHP code assumes that the only data arriving is the data you provided in the <form> tag, you're leaving the door wide open. Never trust the client. I always tell my juniors: assume the user is actively trying to break your logic and bypass your constraints.
What's the best way to stop this without writing 50 lines of manual assignments?
The gold standard here is "Allow-listing." Instead of trying to block the "bad" fields (which is a losing battle because you'll eventually forget one), you define a strict list of fields that are allowed to be mass-assigned.
I usually handle this by creating a small filter array. Check out the difference here:
$userId = $_SESSION['user_id'];
$requestData = $_POST;
// Define exactly what the user is allowed to change
$allowedFields = ['bio', 'location', 'display_name'];
$filteredData = [];
foreach ($allowedFields as $field) {
if (isset($requestData[$field])) {
$filteredData[$field] = $requestData[$field];
}
}
// Now we only loop through the filtered, safe data
$sets = [];
foreach ($filteredData as $column => $value) {
$sets[] = "$column = " . $db->quote($value);
}
if (!empty($sets)) {
$sql = "UPDATE users SET " . implode(', ', $sets) . " WHERE id = " . (int)$userId;
$db->query($sql);
}
By doing this, even if an attacker sends is_admin=1 or account_balance=99999, those keys simply never make it into the $filteredData array. They are ignored entirely. It's a few extra lines of code, but it's the only way to sleep soundly at night.
📋 Practical Task
Fixing the Vulnerable Account Settings Page
You have been handed a legacy script for an account settings page. The current code uses a foreach loop to update the settings table based on the $_POST data, but it's currently vulnerable to mass assignment. An attacker has discovered they can change their subscription_plan to "premium" for free by injecting the field into the request.
Your Task: Rewrite the update logic to implement an allow-list. Ensure that only theme_color, email_notifications, and timezone can be updated via this request. Any other fields sent in the $_POST array must be ignored.
// VULNERABLE CODE TO FIX:
$userId = $_SESSION['user_id'];
$data = $_POST;
$updates = [];
foreach ($data as $key => $val) {
$updates[] = "$key = '" . mysqli_real_escape_string($conn, $val) . "'";
}
$sql = "UPDATE settings SET " . implode(', ', $updates) . " WHERE user_id = $userId";
mysqli_query($conn, $sql);
There are no comments for now.