Paramclasses Academy - Blog

Tutorials Details

←
Post 12 of 15
→

PHP Security Best Practices: How to Secure PHP Applications

PHP Security Best Practices: How to Secure PHP Applications

October 16, 2026

PHP Security Best Practices: How to Secure PHP Applications

PHP is widely used to develop dynamic websites, web applications, content management systems, and e-commerce platforms. However, an application can become vulnerable if developers do not follow proper security practices.

PHP security best practices help protect applications against common threats such as SQL injection, cross-site scripting (XSS), cross-site request forgery (CSRF), unauthorized access, and data exposure.

In this tutorial, you will learn how to write more secure PHP code with practical examples.

What Is PHP Security?

PHP security is the process of protecting PHP applications, user information, databases, and server resources from unauthorized access and malicious attacks.

A secure PHP application should:

  • Validate and sanitize user input.

  • Use prepared statements for database queries.

  • Escape output to prevent XSS attacks.

  • Store passwords securely.

  • Protect forms against CSRF attacks.

  • Manage sessions securely.

  • Avoid exposing sensitive error messages.

  • Keep PHP and its dependencies updated.

1. Prevent SQL Injection Using Prepared Statements

SQL injection occurs when an attacker manipulates database queries through untrusted input.

Insecure PHP Code

<?php
$conn = new mysqli("localhost", "root", "", "my_database");

$username = $_GET['username'];

$sql = "SELECT * FROM users WHERE username = '$username'";
$result = $conn->query($sql);
?>

This code directly inserts user input into an SQL query, which can allow an attacker to manipulate the query.

Secure PHP Code

<?php
$conn = new mysqli("localhost", "root", "", "my_database");

$username = $_GET['username'] ?? '';

$stmt = $conn->prepare(
    "SELECT * FROM users WHERE username = ?"
);

$stmt->bind_param("s", $username);
$stmt->execute();

$result = $stmt->get_result();
?>

Prepared statements separate SQL instructions from user-supplied values, helping prevent SQL injection.

Best practice: Use prepared statements with MySQLi or PDO for database queries that include external input.

2. Prevent Cross-Site Scripting (XSS)

Cross-site scripting (XSS) occurs when untrusted content is displayed on a webpage in a way that allows unwanted JavaScript to execute.

For example, displaying a user's comment without escaping it may expose visitors to malicious scripts.

Insecure Example

<?php
echo $_GET['name'] ?? '';
?>

Secure Example

<?php
$name = $_GET['name'] ?? '';

echo htmlspecialchars(
    $name,
    ENT_QUOTES | ENT_SUBSTITUTE,
    'UTF-8'
);
?>

The htmlspecialchars() function converts special HTML characters into their safe HTML representations.

Best practice: Escape untrusted data according to the context where it is displayed. HTML escaping is appropriate for ordinary HTML text and quoted HTML attributes, but JavaScript, CSS, and URL contexts require their own safe handling.

3. Store Passwords Securely

Never store user passwords as plain text or use outdated hashing methods such as MD5 or SHA-1 for password storage.

PHP provides password_hash() and password_verify() for password handling.

Hash a Password During Registration

<?php
$password = "MySecurePassword123";

$hashedPassword = password_hash(
    $password,
    PASSWORD_DEFAULT
);

// Store $hashedPassword in the database.
?>

Verify a Password During Login

<?php
$enteredPassword = "MySecurePassword123";

// Retrieve this hash from the database.
$storedHash = $hashedPassword;

if (password_verify($enteredPassword, $storedHash)) {
    echo "Login successful.";
} else {
    echo "Invalid username or password.";
}
?>

In a real application, retrieve the stored hash from the database using a prepared statement. Do not store the original password.

Best practice: Use password_hash() with PASSWORD_DEFAULT, and use password_verify() to check passwords.

4. Validate and Sanitize User Input

User input should never be trusted automatically. Validate input according to the expected data type and business rules.

For example, if a form expects an email address, validate that the submitted value is a valid email address.

<?php
$email = trim($_POST['email'] ?? '');

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    exit("Please enter a valid email address.");
}

echo "Email format is valid.";
?>

Validation checks whether input meets the required rules. Sanitization transforms data when necessary.

For example, use trim() to remove surrounding whitespace and filter_var() to validate an email address.

Best practice: Validate input on the server even if client-side JavaScript validation is already implemented. Client-side validation can be bypassed.

5. Protect Forms Against CSRF Attacks

Cross-site request forgery (CSRF) is an attack in which a user's authenticated browser is tricked into submitting an unwanted request to a website.

A common defense is to generate a CSRF token, store it in the user's session, and validate it when processing a form.

Generate a CSRF Token

<?php
session_start();

if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>

Add the Token to a Form

<form method="POST" action="update-profile.php">
    <input
        type="hidden"
        name="csrf_token"
        value="<?= htmlspecialchars(
            $_SESSION['csrf_token'],
            ENT_QUOTES,
            'UTF-8'
        ) ?>"
    >

    <button type="submit">Update Profile</button>
</form>

Validate the Token

<?php
session_start();

$submittedToken = $_POST['csrf_token'] ?? '';
$sessionToken = $_SESSION['csrf_token'] ?? '';

if (
    !is_string($submittedToken) ||
    !is_string($sessionToken) ||
    $sessionToken === '' ||
    !hash_equals($sessionToken, $submittedToken)
) {
    http_response_code(403);
    exit("Invalid CSRF token.");
}

// Process the validated form submission here.
?>

This example demonstrates the core token-validation process. Production applications should also use secure session settings, rotate tokens when appropriate, and apply CSRF protection to state-changing requests. Frameworks such as CodeIgniter provide built-in CSRF protection that should generally be preferred over custom implementations.

6. Configure PHP Sessions Securely

Sessions are commonly used to maintain login status and store temporary user-related data.

Before starting a session, configure appropriate cookie security settings.

<?php
ini_set('session.use_strict_mode', '1');

session_set_cookie_params([
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
    'path'     => '/',
]);

session_start();
?>

Important settings:

  • secure: Sends the session cookie over HTTPS only.

  • httponly: Prevents JavaScript from directly reading the cookie.

  • samesite: Helps reduce certain cross-site request risks.

  • session.use_strict_mode: Helps prevent the use of uninitialized session IDs.

Use HTTPS in production when setting secure to true. After a successful login, regenerate the session ID:

<?php
session_regenerate_id(true);

$_SESSION['user_id'] = $userId;
?>

Here, $userId should be the authenticated user's ID retrieved from your database.

7. Hide Sensitive PHP Errors in Production

During development, detailed error messages are useful for debugging. In production, they can reveal file paths, database details, and internal application information.

For production, configure PHP to log errors instead of displaying them to website visitors.

display_errors = Off
log_errors = On

Make these changes in the appropriate PHP configuration for your server, then restart PHP or the relevant web service if required.

Best practice: Review error logs securely and avoid displaying database credentials, stack traces, or other sensitive details to users.

8. Protect Database Credentials

Avoid publishing database passwords and other secrets in publicly accessible files or source-control repositories.

Use environment variables or a protected configuration system.

Example using environment variables:

<?php
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPass = getenv('DB_PASS');

if (
    !$dbHost ||
    !$dbName ||
    !$dbUser ||
    $dbPass === false
) {
    throw new RuntimeException(
        'Database configuration is missing.'
    );
}

$pdo = new PDO(
    "mysql:host={$dbHost};dbname={$dbName};charset=utf8mb4",
    $dbUser,
    $dbPass,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]
);
?>

Configure these environment variables on your development or production server. Do not place real credentials in publicly accessible files.

9. Use HTTPS to Protect Data in Transit

HTTPS encrypts data exchanged between the user's browser and your server.

Without HTTPS, sensitive information transmitted over an insecure connection may be exposed to interception.

Use a valid TLS certificate and configure your website to redirect HTTP traffic to HTTPS. Secure cookies and other security controls should also be configured appropriately.

HTTPS does not replace input validation, authentication, or database security; it complements them.

10. Apply Authentication and Authorization Checks

Authentication verifies who a user is. Authorization determines what that user is allowed to do.

For example, a normal user should not be able to delete another user's account simply by changing a user ID in a URL.

<?php
session_start();

if (empty($_SESSION['user_id'])) {
    http_response_code(401);
    exit("Please log in first.");
}

$currentUserId = (int) $_SESSION['user_id'];
$requestedUserId = (int) ($_GET['id'] ?? 0);

if ($currentUserId !== $requestedUserId) {
    http_response_code(403);
    exit("You are not authorized to access this resource.");
}

// Continue with the authorized operation.
?>

This is a simplified ownership check. In production, verify permissions on the server for every protected operation, including administrative actions. For role-based access, check the user's assigned role and permissions rather than relying only on a matching user ID.

11. Validate File Uploads

File upload features can create security risks when applications accept arbitrary files or save them in publicly executable locations.

When implementing uploads:

  • Allow only the file types the application actually needs.

  • Check the file's size.

  • Validate its actual content and MIME type.

  • Generate a random filename instead of trusting the original filename.

  • Store files outside the public web root when practical.

  • Prevent uploaded files from being executed as server-side code.

  • Apply appropriate access controls when files are downloaded.

Do not rely only on the filename extension or the browser-supplied Content-Type header. These values can be manipulated.

12. Keep PHP and Dependencies Updated

Outdated PHP versions, libraries, and frameworks can contain known security vulnerabilities.

Follow these practices:

  • Use a currently supported PHP version.

  • Update Composer dependencies regularly.

  • Remove unused packages and files.

  • Review security advisories for the libraries you use.

  • Back up important data before deploying updates.

  • Test changes in a staging environment when possible.

Keeping an application updated is an important part of long-term security.

PHP Security Best Practices Checklist

Before deploying a PHP application, review the following checklist:

  • Use prepared statements for database queries.

  • Escape untrusted output to prevent XSS.

  • Hash passwords with password_hash().

  • Verify passwords with password_verify().

  • Validate input on the server.

  • Protect state-changing forms against CSRF.

  • Configure session cookies securely.

  • Regenerate session IDs after login.

  • Disable displayed errors in production.

  • Keep database credentials private.

  • Use HTTPS.

  • Check authorization for protected actions.

  • Validate file uploads carefully.

  • Keep PHP, frameworks, and dependencies updated.

Conclusion

PHP security is essential for building reliable and trustworthy web applications. By using prepared statements, secure password hashing, proper output escaping, CSRF protection, secure sessions, and authorization checks, developers can reduce many common security risks.

These practices should be applied throughout development rather than added only before deployment. For projects built with frameworks such as CodeIgniter or Laravel, use their established security features wherever possible and follow the framework's current documentation.

Frequently Asked Questions (FAQs)

1. What are PHP security best practices?

PHP security best practices include prepared SQL statements, password hashing, input validation, output escaping, CSRF protection, secure session configuration, HTTPS, and regular updates.

2. How can I prevent SQL injection in PHP?

Use prepared statements with MySQLi or PDO instead of concatenating user input into SQL queries.

3. How should passwords be stored in PHP?

Use password_hash() to create password hashes and password_verify() to verify login passwords. Never store passwords in plain text.

4. What is XSS in PHP?

Cross-site scripting (XSS) occurs when an application allows untrusted content to execute as script in another user's browser. Proper context-specific output escaping helps prevent it.

5. Is input validation enough to secure a PHP application?

No. Input validation is important, but applications also need secure database queries, output escaping, authentication, authorization, CSRF protection, secure configuration, and regular maintenance.

←
Post 12 of 15
→