in

6 Essential Security Tips to Protect Your PHP Website from Hackers

As a fellow developer, I know how thrilling it is to finally launch a new PHP website after all the hard work building it. But before you pop open the champagne, make sure your site is locked down tight against hackers!

PHP‘s widespread popularity has made it a prime target for cyber attacks. Over 30% of breaches in 2019 involved web applications, according to Verizon‘s Data Breach Investigations Report. And web apps built on PHP are involved in 80% of those cases due to the language‘s ubiquity.

Failing to secure your PHP site puts both your business and users at tremendous risk. The average cost of a data breach now exceeds $3.86 million, according to IBM‘s Cost of a Data Breach Report. Beyond fines and legal impacts, you also risk irreparable damage to your brand reputation and user trust.

Fortunately, you can prevent the vast majority of hacks by following security best practices. I‘ve personally learned many of these the hard way from incidents at startups I‘ve worked with. Take the time now to implement these essential PHP security tips before you end up as another statistic!

Use Parameterized Queries to Block SQL Injection

SQL injection attacks are used in over 65% of web app breaches because they‘re so effective at gaining unauthorized data access. By injecting malicious SQL into input fields, hackers can trick the database into running exploit code and exposing information.

For example, say we had a simple login form:

$username = $_POST[‘username‘]; 

$sql = "SELECT * FROM users WHERE username = ‘$username‘";

A hacker could inject code like this:

‘ OR ‘1‘=‘1

Resulting in:

SELECT * FROM users WHERE username = ‘‘ OR ‘1‘=‘1‘

This modified query will dump the entire user table since 1=1 is always true!

The simplest way to block SQL injection is using parameterized queries, also called prepared statements. Instead of inserting raw user input directly into the SQL, you use placeholder variables which get sanitized behind the scenes:

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

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

$stmt->execute();

Parameterizing forces the input to conform to the expected datatype, preventing injection of unintended code.

Over 50% of developers still build apps vulnerable to SQL injection according to Veracode, so don‘t be part of that statistic! Parameterize all your queries.

Validate and Sanitize All Inputs

Another alarmingly common weakness is failing to properly validate and sanitize untrusted inputs. This leads to cross-site scripting (XSS) which allows hackers to inject JavaScript code into your site.

XSS commonly happens when outputting user input back onto a page, like displaying the username after login. Imagine a user signs up with:

<script>alert(‘XSS!‘)</script>

If displayed unescaped on the site, this will execute and pop up an alert box!

More malicious actions include stealing session cookies or redirecting users to phishing pages impersonating your site.

The first line of defense is validating all inputs match expected formatting. For example, restrict usernames to letters and numbers only.

Next, sanitize any dangerous characters before outputting. Encode or escape special chars like < > & " ‘ that could allow code injection.

In PHP, use htmlspecialchars() or set HTML escaping in templating engines like Twig.

Validation and sanitization seems simple, but over 30% of web apps still don‘t properly implement it. Don‘t skip this crucial step.

Require CSRF Tokens on All Forms

Cross-site request forgery (CSRF) is a devious attack that tricks logged in users into unknowingly submitting malicious requests.

Say a bank website is vulnerable to CSRF. An attacker creates an external page with code that secretly submits a funds transfer when loaded by the victim while logged into their bank account:

<img src="https://bank.com/transfer.php?amt=2000&toAcct=1234">

When the user visits this malicious third-party page, it automatically transfers their money without consent!

The key defense is to require a CSRF token on all state-changing requests like funds transfers. This token confirms the request originated from your real site, not an external origin.

Here‘s how to implement it:

  1. Generate a random CSRF token and store it in the user‘s session on login.

  2. Add the token as a hidden field in your forms.

  3. Verify the submitted token matches the session one before accepting the request.

With this protection, cross-site requests will fail validation since they won‘t have the correct origin-specific token. Easy to add, but critical for blocking CSRF attacks.

Force HTTPS and TLS Encryption

Always use HTTPS and encrypt traffic between your PHP application and users. This prevents man-in-the-middle attacks that snoop on unencrypted HTTP requests in transit.

Configure your server for HTTPS by installing an SSL/TLS certificate from a trusted Certificate Authority like Let‘s Encrypt. Redirect all HTTP requests to HTTPS in your application code.

Enable HTTP Strict Transport Security (HSTS) to force browsers to only connect over HTTPS, even if users type a raw http:// URL. This completely eliminates unsecured access to your site.

There are different certificate types like domain validation (DV), organization validation (OV), and extended validation (EV). The level of identity verification conducted increases accordingly. I recommend EV certificates for maximum browser trust. Here‘s a comparison:

Validation Type Identity Verified Cost Browser UI
DV Domain ownership only Free – $50/year Normal
OV Basic organization details $150-$300/year Normal
EV Full legal entity $400+/year Green bar

The performance impact of TLS is minimal on modern hardware. Just a 2-3% increase in page load time according to Google studies. Well worth it for the privacy and security of your users‘ data.

Limit File Permissions on a Need Basis

Overly permissive file permissions are like leaving your doors and windows wide open for hackers. Only grant access on an as needed basis.

Start by restricting permissions of sensitive files like config files and credentials to owner read-only:

-rw------- config.php

For most application code files like classes and controllers, allow group read and owner write:

-rw-r--r-- MyController.php

Public asset files like CSS, JavaScript and images can be world readable but not writable:

-r--r--r-- main.css

And remember to limit executable script permissions only to the core files that require it.

Starting with the most restrictive and selectively enabling more access prevents many attacks like remote code execution by compromising uploads.

Hide Error Details from Users

By default PHP displays verbose error messages directly in the browser. This reveals sensitive paths and code snippets that attackers leverage to fingerprint your app.

Turn off error display in production with:

ini_set(‘display_errors‘, ‘0‘); 

Then log errors to a file you can access instead. You still get notified of issues, but avoid exposing details publicly.

For any errors that do bubble up to users, show generic messages and don‘t reveal specifics. For example "Internal server error" instead of leaking code traces.

Obscuring internals from external visibility makes reconnaissance more difficult. Implementing security through obscurity in addition to actual controls provides defense in depth.

Follow Session Management Best Practices

Sessions allow persisting logins across multiple requests, but introduce additional risk you need to stay on top of.

Start each new session by regenerating the session ID with session_regenerate_id(true). This prevents session fixation attacks.

Set the session cookie as HTTP only so it can‘t be accessed by JavaScript and isn‘t vulnerable to XSS data leakage. Use a Content Security Policy (CSP) to restrict external resource access.

Store session data in encrypted cookies rather than on the filesystem when possible. Limit idle session lifetimes and implement sliding expiration to keep credentials fresh.

Little things like this go far in hardening the security posture of your PHP apps. Make session management a priority.

Securely Hash Passwords with Salts

Never ever store plain text passwords! That‘s just asking for disaster when (not if) your database gets compromised one day.

Always securely hash passwords during registration and authenticate against the hash. Use PHP‘s built-in password_hash() and password_verify() functions.

These handle salting and hashing the passwords with bcrypt by default. Here‘s a secure registration flow:

// Registration

$hashedPassword = password_hash($password, PASSWORD_DEFAULT);

// Store $hashedPassword in database

// Login

$enteredPassword = $_POST[‘password‘];

// Fetch $hashedPassword from database

if(password_verify($enteredPassword, $hashedPassword)) {
  // Login success!
} else {
  // Nope!
} 

Bcrypt is intentionally slow to compute, making brute force attacks infeasible. Always use unique salt values for each user which get hashed into their password to prevent rainbow table attacks. Consider even stronger schemes like Argon2 for enhanced protection.

Limit Login Attempts to Slow Attacks

To prevent brute force and credential stuffing attacks, implement progressive rate limiting after failed login attempts.

After just 3-5 failed attempts, impose a 30 second delay before the next try. This drastically slows down brute forcing.

After 10 failed attempts, lock out the account or IP address completely for 15 minutes. Legitimate users remember their passwords or can reset via email.

Alert real users via email if there are excessive failures from their account so they can change passwords if compromised.

With protections like this in place, these common attacks simply become ineffective.

Keep Software Up-to-Date and Patch Regularly

Using outdated PHP versions with known vulnerabilities is like leaving the digital door wide open. New exploits are discovered constantly.

Make keeping your entire software stack updated a top priority. Subscribe to mailing lists and feeds for new security advisories. Install patches as soon as possible after release.

Schedule regular version bumps and upgrades of all components:

  • PHP runtime
  • Frameworks and libraries
  • Content management systems
  • Plugins and dependencies

Try to stay within one minor version of the latest for any software you rely on. Outdated software is the easiest attack vector for hackers to breach PHP sites. Don‘t be an easy target!

The journey of a thousand miles begins with a single step

Whew, that was quite an epic saga! While it may seem daunting trying to implement all these layers of security, just focus on taking it step by step.

Start with the most critical and high severity items like input validation, TLS encryption, and password hashing. This will massively increase your protection against common attacks.

Over time incorporate additional controls like rate limiting brute force, minimizing file permissions, enabling HSTS, etc. Think of it as a journey of continuous improvement rather than a single destination.

Prioritize security throughout development, implement defense in depth, and stay vigilant about patching and upgrades. Keep learning and adopting new best practices as they emerge.

Together we can make the PHP ecosystem more secure for everyone! Your users are entrusting you with their data – it‘s our responsibility as developers to earn and uphold that trust.

Let me know if you have any other specific questions! I‘m always happy to help a fellow developer strengthen their web security posture. Here‘s to building robust, hack-resistant PHP applications. Cheers!

AlexisKestler

Written by Alexis Kestler

A female web designer and programmer - Now is a 36-year IT professional with over 15 years of experience living in NorCal. I enjoy keeping my feet wet in the world of technology through reading, working, and researching topics that pique my interest.