Effective SQL Injection Prevention Techniques in PHP
Implementing prepared statements with parameter binding is the most robust approach to prevent SQL injection in PHP. This method separates SQL commands from data, ensuring that malicious user input cannot alter the query structure. Here's how to implement it using the PDO extention:
try {
// Initialize database connection
$database = new PDO('mysql:host=localhost;dbname=webapp', 'dbuser', 'dbpass');
$database->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Begin transaction for data integrity
$database->beginTransaction();
// Prepare the SQL template
$query = $database->prepare("SELECT user_id, email FROM accounts
WHERE login_name = :login AND pass_hash = :hash");
// Bind input parameters securely
$query->bindParam(':login', $userLogin);
$query->bindParam(':hash', $userHash);
// Assign sanitized values
$userLogin = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
$userHash = hash('sha256', $_POST['password']);
// Execute the prepared statement
$query->execute();
// Fetch matching records
$accounts = $query->fetchAll(PDO::FETCH_ASSOC);
// Commit the transaction
$database->commit();
// Process retrieved data
foreach ($accounts as $account) {
// Handle authenticated session
}
} catch (PDOException $error) {
// Rollback on failure
$database->rollBack();
// Log detailed error for debugging
error_log("Database error: " . $error->getMessage());
}
For legacy applicasions using MySQLi, proper string escaping remains an additional layer of protection. While not as secure as prepared statements, it can serve as a supplementary defense when combined with other security measures:
// Sanitize user inputs
$userName = mysqli_real_escape_string($connection, $_POST['username']);
$userPass = mysqli_real_escape_string($connection, $_POST['password']);
// Build query with escaped strings
$query = "SELECT * FROM members
WHERE username = '$userName' AND password = '$userPass'";
$result = mysqli_query($connection, $query);
Input validation based on expected data types provides another security checkpoint. Numeric values should be verified using type cehcking functions:
// Validate numeric identifier
$userId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($userId !== false) {
// Safe to use in query
$query = "SELECT profile_data FROM user_profiles WHERE id = $userId";
$result = mysqli_query($connection, $query);
} else {
// Handle invalid input
http_response_code(400);
}
Security best practices include:
- Treat all user input as potentially malicious and implement strict validation
- Always utilize prepared statements through PDO or MySQLi extensions
- Avoid constructing SQL queries through string concatenation
- Apply PHP's filter functions for input sanitization and validation
- Ensure magic_quotes_gpc is disabled in PHP configuration
- Exercise caution with LIKE operations, which can introduce pattern matching vulnerabilities
- Implement proper error handling to prevent information disclosure