SQL injection and file upload are two of the most classic web vulnerabilities. Their common characteristics are: high impact, low exploitation cost, and often stemming from a minor oversight—such as an unfiltered parameter or an unchecked upload interface.
Below are the hardening measures for each, with a focus on actions that can be implemented immediately.
I. SQL Injection
Principle in One Sentence
User input is executed as SQL code rather than being treated as data.
// Dangerous: Directly concatenating user input
$sql = "SELECT * FROM users WHERE name = '" . $_GET['name'] . "'";
// When user input is ' OR '1'='1', the statement becomes:
// SELECT * FROM users WHERE name = '' OR '1'='1'
// The condition is always true, returning all records
Hardening Method 1: Prepared Statements (Fundamental Solution)
// PDO prepared statements (Recommended)
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false, // Critical: Do not use emulated prepares
]);
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->execute(['name' => $_GET['name']]);
$rows = $stmt->fetchAll();
// mysqli prepared statements
$stmt = $mysqli->prepare("SELECT * FROM users WHERE name = ?");
$stmt->bind_param("s", $_GET['name']);
$stmt->execute();
// WordPress using $wpdb->prepare
$rows = $wpdb->get_results(
$wpdb->prepare("SELECT * FROM {$wpdb->posts} WHERE post_title = %s", $title)
);
Prepared statements separate “data” from “code” during transmission, which is the fundamental solution. Escaping characters is merely a remedial measure and should not serve as the primary defense line.
Hardening Method 2: Principle of Least Privilege
# Application accounts should only have necessary permissions, without DROP / FILE, etc.
mysql -e "GRANT SELECT, INSERT, UPDATE, DELETE ON dbname.* TO 'appuser'@'localhost';"
mysql -e "REVOKE FILE, DROP, CREATE ON *.* FROM 'appuser'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"
If injected, limited privileges mean limited damage. Application accounts typically do not need permissions like DROP or FILE.
Hardening Method 3: Do Not Expose Error Details
// Disable error display in production environments
ini_set('display_errors', 0);
ini_set('log_errors', 1);
Detailed SQL error messages directly leak table structures and field names, effectively providing a map to attackers.
II. File Upload
Where the Risk Lies
The risk of upload functionality is not “files being too large,” but rather uploading executable files that are subsequently executed as code.
| Risk | Description |
|---|---|
| Uploading PHP files | Accessing the URL executes arbitrary code |
| Uploading disguised images | Content is code, extension is an image |
| Upload path allows execution | Even with restricted extensions, misconfiguration can lead to execution |
| Unprocessed filenames | May contain path traversal sequences (../../) |
Hardening Method 1: Whitelist Extension Validation
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf'];
$ext = strtolower(pathinfo($_FILES['f']['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed, true)) {
die('File type not allowed');
}
Use a whitelist, not a blacklist. Blacklists will always miss certain extensions (e.g., .phtml, .php5).
Hardening Method 2: Validate Real Type, Ignore Extension
// Determine real type using file headers
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['f']['tmp_name']);
$allowedMime = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($mime, $allowedMime, true)) {
die('File type mismatch');
}
# Verify real type via command line
file /var/www/html/wp-content/uploads/2026/10/demo.jpg
# If a .jpg file is identified as "PHP script" → it is a disguised file
Extensions are controlled by the uploader, but file headers are not. Both should be validated together.
Hardening Method 3: Disable Execution in Upload Directory
location ^~ /wp-content/uploads/ {
location ~ .php$ {
deny all; # Disallow executing php in the upload directory
}
}
# Verification (should return 403)
curl -I https://example.com/wp-content/uploads/test.php
This is the last line of defense and also the most effective one. Even if previous validations are bypassed, as long as the upload directory cannot execute PHP, the file remains static data.
Hardening Method 4: Rename Files and Remove Execute Permissions
// Use random names + fixed extensions; do not use original filenames
$newName = bin2hex(random_bytes(16)) . '.' . $ext;
# Remove execute permissions for the entire upload directory
chmod -R a-x /var/www/html/wp-content/uploads/
Checklist
| Item | Requirement | Verification |
|---|---|---|
| SQL | All queries must use prepared statements | Search code for concatenated SQL |
| SQL | Application account has least privilege | SHOW GRANTS |
| SQL | No errors displayed in production | Trigger an error to check output |
| Upload | Extension whitelist | Attempt to upload .php (should be rejected) |
| Upload | Validate file header | Upload a disguised PHP file |
| Upload | Disable execution in upload directory | Accessing uploads/x.php should return 403 |
| Upload | Random renaming | Check filenames |
# Find concatenated SQL in code (for self-inspection)
grep -rnE '$_(GET|POST|REQUEST)[.*].*(SELECT|INSERT|UPDATE|DELETE)'
/var/www/html --include='*.php' | head -20
Decision Tree
Hardening Order (by Cost-Benefit Ratio)
├─ SQL Injection
│ ├─ ① Convert all to prepared statements ← Fundamental solution
│ ├─ ② Apply least privilege to app account ← Limit scope of damage
│ └─ ③ Disable error display in production ← Avoid leaking structure
└─ File Upload
├─ ① Disable PHP execution in upload dir ← Most effective, do this first
├─ ② Extension whitelist
├─ ③ Validate real file type (file header)
└─ ④ Random rename + remove execute bit
Scenario Walkthrough: Server Compromised After Uploading an Image
Symptom: A site has an image upload feature. One day, unfamiliar files appear on the server.
Investigation reveals they were uploaded via the upload feature—the attacker did not upload genuine images.
- First, confirm what the upload feature actually validates → It only checked the file extension,
which can be forged; it did not validate the real type of the file content. - Confirm whether scripts can be executed in the upload directory → Yes.
These two conditions combined create a complete attack path. - Containment: Immediately disable script execution in that directory → Block at the Web layer to cut off the path instantly.
- Investigate if damage has already occurred → Check for suspicious files in the directory and recent modification times.
- Implement missing validations → Simultaneously add extension whitelisting, real-type detection,
and random renaming for stored files. - Confirm if similar entry points exist → Other upload points and import functions must also be checked;
do not just fix the one that was exploited.
# Containment: Disable script execution in upload directory (Web layer, lowest cost, highest benefit)
# nginx: location ~* ^/wp-content/uploads/.*.php$ { deny all; }
nginx -t && systemctl reload nginx
# Investigation: Sort suspicious files by time
find /path/to/site/wp-content/uploads -type f ( -name '*.php' -o -name '*.phtml' )
-printf '%TY-%Tm-%Td %TH:%TM %pn' | sort
# Confirm that execution processes do not have unnecessary write permissions to the directory
ps -eo user,cmd | grep -E 'nginx|php-fpm' | head -3
ls -ld /path/to/site/wp-content/uploads
Root Cause: The upload feature only validated the extension, not the real content;
and the upload directory allowed script execution. Neither alone is fatal, but together they form a complete chain.
Two Key Takeaways:
- “Disabling script execution in the upload directory” is the lowest-cost, highest-benefit hardening measure.
Standing alone, it cuts off the most common attack path, even if no other validations are in place. - Upload validation requires two actions: Extension whitelist + Real-type detection.
Validating only the extension is equivalent to no validation—it is a field entirely controlled by the client.
8 Common Pitfalls
- Using escaping instead of prepared statements: Escaping has loopholes; prepared statements are the fundamental solution.
- Using blacklists to filter extensions: There will always be overlooked variants.
- Validating only extensions, not content: Disguised files easily bypass this.
- Only validating on the frontend: Requests can bypass the browser.
- Upload directory allows PHP execution: The most dangerous configuration.
- Application account has excessive permissions: Amplifies damage upon injection.
- Error messages expose table structure: Effectively provides a map to attackers.
- Retaining original filenames: May pose risks of path traversal or overwriting.
Real Customer Feedback (From Cloud Marketplace Reviews and Inquiries for Similar Products)
The following excerpts are taken from real buyer reviews and inquiries for similar operations services on Alibaba Cloud / Tencent Cloud / Huawei Cloud cloud marketplaces (service providers include our store and peers, provided solely for reference of real feedback, not fictitious tickets from this site):
1****1 Rating: 5.0 Points 2020-06-18 11:50 1****1 Rating: 5.0 Points 2020-04-26 18:46 1****1 Rating: 5.0 Points 2020-04-26 18:45 1****1 Rating: 5.0 Points 2020-04-26 18:45 1****1 Rating: 5.0 Points 2020-04-26 18:45 1****1 Rating: 5.0 Points 2020-04-26 18:44 He**** She Rating: 5.0 Points 2019-09-26 09:47
1****8 Rating: 5.0 Points 2019-10-29 22:35 Good 1