LATEST >>

Welcome Here And Thanks For Visiting. Like Us On Facebook...

EXEIdeas – Let's Your Mind Rock » HTML-CSS-PHP-JavaScript / PHP Codes » Core PHP Website Security: Stop Common Hacking Attempts

Core PHP Website Security: Stop Common Hacking Attempts

Core-PHP-Website-Security-Stop-Common-Hacking-Attempts

A core PHP website gives developers enormous flexibility, but that flexibility also means security controls that a modern framework might provide automatically must often be implemented manually. SQL injection, cross-site scripting, CSRF, session hijacking, insecure file uploads, brute-force attacks, authorization failures, path traversal, information leakage, and outdated server software are some of the most common risks.

The solution is not one security function or one server rule. A secure PHP application uses multiple defensive layers so that when one control fails, another control still reduces the chance of compromise.

This approach is known as defense in depth. It combines secure coding, strict authentication, server hardening, proper permissions, encryption, monitoring, patch management, safe database access, and careful handling of every piece of untrusted input.

The Most Important PHP Security Rule: Never Trust Data Simply Because It Came From Your Own Form, Browser, JavaScript, Cookie, API Request, URL, Mobile App, Or Logged-In User. Validate What Enters The Application, Encode What Leaves It, And Authorize Every Sensitive Action.

This guide explains how to secure a plain or core PHP website against common hacking attempts without depending on a full framework. It focuses on practical protections that can be implemented in custom PHP applications, admin panels, membership websites, APIs, portals, e-commerce projects, and business systems.

Table of Contents

Understand What You Are Actually Protecting

Website security begins with understanding the assets an attacker may want. The objective is not merely to stop someone from changing your homepage.

A compromised PHP application may expose:

  • User accounts.
  • Customer information.
  • Password hashes.
  • Session cookies.
  • Business records.
  • Uploaded documents.
  • API credentials.
  • Database credentials.
  • Payment-related information.
  • Administrator access.

An attacker may also want to use your server for phishing, spam, malware distribution, cryptocurrency mining, attacks on other systems, or search-engine spam.

This is why application security must protect confidentiality, integrity, and availability, not only the visible website.

Keep PHP And Server Software Fully Patched

One of the easiest ways to compromise an application is to exploit a vulnerability that has already been fixed by the software vendor.

Your production environment should use a currently supported PHP branch with the latest available security updates.

Also keep these components patched:

  • Web server software such as Apache or Nginx.
  • Operating system packages.
  • Database server.
  • OpenSSL and TLS libraries.
  • Composer packages.
  • JavaScript dependencies.
  • Image and document processing libraries.

Do not postpone updates indefinitely simply because the website appears to be working.

Unsupported software continues working after its security support ends, but newly discovered vulnerabilities may no longer receive patches.

Never Develop Directly Against An Unsupported PHP Version

Legacy PHP applications often remain on old versions because developers fear compatibility problems.

That approach creates growing technical and security debt.

Use a staging environment to test upgrades and progressively remove deprecated code. The objective should be to stay on a supported PHP branch rather than waiting until an emergency vulnerability forces a rushed migration.

Force HTTPS Across The Entire Website

Every modern PHP application that handles authentication, personal information, administrative access, or forms should use HTTPS.

HTTPS protects data while it travels between the browser and your server.

Without encryption, attackers on an insecure network may potentially observe or manipulate traffic.

Redirect all HTTP requests to HTTPS and ensure that:

  • Your TLS certificate is valid.
  • The certificate renews automatically.
  • Internal links use HTTPS.
  • Forms submit over HTTPS.
  • Cookies carrying authentication data use the Secure attribute.

Consider HTTP Strict Transport Security

Once your entire site works reliably over HTTPS, you can consider sending the Strict-Transport-Security header.

HSTS tells supporting browsers that the domain should be accessed only through HTTPS for the configured duration.

For example:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Do not enable a long HSTS duration or preload configuration until HTTPS works correctly across every affected hostname. A bad HSTS deployment can lock legitimate users out of services that still depend on HTTP or have broken certificates.

Treat Every External Value As Untrusted Input

Developers sometimes validate form fields but forget that attackers can send requests without using the form at all.

Untrusted input includes:

  • $_GET
  • $_POST
  • $_COOKIE
  • Uploaded files.
  • HTTP headers.
  • JSON request bodies.
  • API responses.
  • Webhook payloads.
  • Database content originally supplied by users.

Client-side JavaScript validation improves usability but is not a security boundary.

An attacker can disable JavaScript, modify requests, or call your PHP endpoint directly.

Use Allow-List Input Validation Wherever Possible

Validation asks whether data is acceptable for the business operation.

If a form expects an integer ID, reject values that are not valid integers. If a role can only be member, trainer, or manager, accept only those values.

Example:

<?php
$userId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);

if ($userId === false || $userId === null || $userId < 1) {
    http_response_code(400);
    exit('Invalid request.');
}
?>

Validation should reflect business rules, including:

  • Data type.
  • Length.
  • Range.
  • Format.
  • Allowed values.

Do not assume validation alone makes data safe for every destination. SQL, HTML, shell commands, URLs, and file paths each require their own defensive handling.

Protect Against SQL Injection With PDO Prepared Statements

SQL injection remains one of the most dangerous mistakes in custom PHP applications.

Never create a query by directly concatenating user input:

<?php
$sql = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'";
?>

An attacker may be able to change the intended SQL logic by supplying specially constructed input.

Use prepared statements with bound parameters instead:

<?php
$stmt = $pdo->prepare(
    'SELECT id, name, email FROM users WHERE email = :email LIMIT 1'
);

$stmt->execute([
    'email' => $email
]);

$user = $stmt->fetch(PDO::FETCH_ASSOC);
?>

Prepared statements separate SQL instructions from user-controlled values.

This is fundamentally stronger than attempting to manually escape every suspicious character.

Do Not Parameterize What Cannot Be Parameterized

Prepared-statement placeholders are designed for data values, not arbitrary SQL syntax such as table names, column names, or sort directions.

If users can select sorting, map their choice to predetermined values:

<?php
$allowedSorts = [
    'newest' => 'created_at DESC',
    'oldest' => 'created_at ASC',
    'name'   => 'name ASC'
];

$sortKey = $_GET['sort'] ?? 'newest';
$orderBy = $allowedSorts[$sortKey] ?? $allowedSorts['newest'];

$sql = "SELECT id, name FROM customers ORDER BY {$orderBy}";
?>

Never place arbitrary request values directly into table names, column names, or SQL clauses.

Give The Database User Minimum Required Permissions

Your web application should not connect to the database as a superuser or database administrator.

Create a dedicated database account with only the privileges the application actually requires.

For example, the website may need:

  • SELECT.
  • INSERT.
  • UPDATE.
  • DELETE On Specific Application Tables.

It usually does not need permission to create database users, grant privileges, drop unrelated databases, or access every schema on the server.

Least privilege limits the damage if SQL injection or another application compromise occurs.

Prevent XSS With Context-Aware Output Encoding

Cross-site scripting occurs when untrusted data is inserted into a page in a way that the browser interprets as executable content.

A simple example is displaying a user’s name without HTML encoding.

Unsafe:

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

Safer for normal HTML text:

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

This converts HTML-significant characters into harmless entities for that context.

Do Not Sanitize Everything Once And Assume XSS Is Solved

XSS protection depends on where the data is being inserted.

HTML text, HTML attributes, JavaScript, CSS, and URLs are different contexts.

The safest architectural approach is to avoid inserting untrusted data directly into executable JavaScript or CSS whenever possible.

Keep a distinction between:

  • Validation: Is This Input Acceptable?
  • Storage: What Original Value Should We Save?
  • Output Encoding: How Must It Be Safely Rendered Here?

Encoding at output time makes it easier to apply the correct protection for the actual destination.

Never Trust Content Just Because It Came From Your Database

Stored XSS occurs when malicious content enters your database and later gets rendered to another user.

For example, an attacker may save a malicious display name, support message, profile biography, or product review.

When an administrator views that value later, unsafe rendering could execute attacker-controlled JavaScript.

Therefore, encode untrusted content even when it came from your own database.

Use Content Security Policy As An Extra XSS Layer

A Content Security Policy can restrict which sources the browser is allowed to load scripts, styles, frames, fonts, and other resources from.

A basic starting policy might look conceptually like:

Content-Security-Policy: default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'

A production CSP must be adapted to your actual scripts, APIs, analytics, CDNs, images, and other resources.

Do not copy an extremely restrictive CSP directly into production without testing it first.

CSP is an additional containment layer. It does not replace correct output encoding.

Protect Every State-Changing Form Against CSRF

Cross-site request forgery tricks an authenticated user’s browser into sending an action they did not intend.

Actions requiring CSRF protection can include:

  • Changing an email address.
  • Updating a profile.
  • Deleting records.
  • Creating users.
  • Updating permissions.
  • Changing passwords.
  • Submitting administrative forms.

A simple core PHP strategy uses a cryptographically random token stored in the session.

Create A CSRF Token With Random Bytes

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

Place it inside the form:

<input
    type="hidden"
    name="csrf_token"
    value="<?= htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8') ?>"
>

Verify it before processing the request:

<?php
$token = $_POST['csrf_token'] ?? '';

if (
    empty($_SESSION['csrf_token']) ||
    !hash_equals($_SESSION['csrf_token'], $token)
) {
    http_response_code(403);
    exit('Invalid request.');
}
?>

Generate tokens using cryptographically secure randomness rather than timestamps, usernames, predictable IDs, or simple hashes.

SameSite Cookies Help But Do Not Replace CSRF Tokens

The SameSite cookie attribute can reduce some cross-site request risks.

However, it should be considered an additional defense rather than a universal replacement for application-level CSRF protection.

Sensitive state-changing operations should still validate an unpredictable CSRF token.

Secure PHP Sessions Before Calling Session_Start

Session cookies are effectively authentication credentials while a user is logged in.

Configure them carefully.

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

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

session_start();
?>

The important protections include:

  • Secure: Send the cookie only through HTTPS.
  • HttpOnly: Prevent normal JavaScript from reading the cookie.
  • SameSite: Reduce unnecessary cross-site cookie sending.
  • Strict Session Mode: Reject uninitialized session identifiers.

Regenerate Session IDs After Login

Once authentication succeeds, create a fresh session identifier:

session_regenerate_id(true);

This reduces session fixation risk by preventing the authenticated session from continuing to use an identifier established before authentication.

Regeneration should also be considered after important privilege changes, such as moving from a normal user context into an administrator context.

Implement Idle And Absolute Session Timeouts

A stolen session should not remain useful indefinitely.

Track session activity and require reauthentication after appropriate limits.

You can implement:

  • Idle Timeout: Expire after inactivity.
  • Absolute Timeout: Expire after a maximum session lifetime regardless of activity.

More sensitive applications should use shorter limits than low-risk public services.

Store Passwords With Password_Hash

Never store passwords in plaintext.

Also avoid creating your own password storage algorithm with MD5, SHA-1, SHA-256, or custom salts.

PHP provides dedicated password APIs:

<?php
$hash = password_hash($password, PASSWORD_DEFAULT);
?>

Verify passwords with:

<?php
if (password_verify($password, $storedHash)) {
    // Authentication succeeded.
}
?>

PASSWORD_DEFAULT allows PHP to evolve the default password algorithm over time.

Consider Argon2id Where Available

PHP supports Argon2id when the PHP build includes appropriate Argon2 support.

Example:

$hash = password_hash($password, PASSWORD_ARGON2ID);

Argon2id is a memory-hard password hashing algorithm designed specifically for password protection.

Do not manually provide a password salt. PHP’s password hashing functions generate suitable random salt information automatically.

Rehash Passwords As Security Settings Improve

Password-hashing recommendations evolve.

After a successful login, use password_needs_rehash() to determine whether an older stored hash should be upgraded.

This allows password protection to improve gradually as users sign in without storing or recovering the original password.

Protect Login Forms Against Brute Force And Credential Stuffing

Prepared SQL statements do not stop someone from repeatedly guessing passwords.

Implement login throttling based on factors such as:

  • Account.
  • IP address.
  • Time window.
  • Risk signals.

A basic system may progressively delay or temporarily restrict attempts after repeated failures.

Do not create a mechanism that permanently locks an account after a few attempts, because attackers could intentionally lock legitimate users out.

Use MFA For High-Value Administrative Accounts

Administrator accounts deserve stronger authentication than ordinary low-risk accounts.

Multi-factor authentication can significantly reduce the impact of stolen passwords.

Protect especially:

  • Super administrators.
  • Finance users.
  • Staff with export permissions.
  • Users who can modify permissions.
  • Accounts that can deploy code.

If your core PHP system does not yet support MFA, prioritize it for sensitive back-office access rather than assuming a complex password alone is enough.

Use Generic Login Error Messages

Do not tell an attacker whether a particular username or email exists.

Instead of:

Email Exists But Password Is Incorrect.

prefer:

Invalid Email Or Password.

This reduces account-enumeration information while still giving legitimate users an understandable response.

Authorization Must Be Checked On Every Protected Request

Authentication answers:

Who Is This User?

Authorization answers:

Is This User Allowed To Perform This Action?

Do not assume that hiding an administrator button protects the associated endpoint.

An attacker can request URLs directly.

Prevent IDOR And Broken Object-Level Authorization

Imagine a user can access:

/invoice.php?id=105

Changing the value to:

/invoice.php?id=106

must not expose another customer’s invoice merely because that ID exists.

Your query should confirm both identity and ownership:

<?php
$stmt = $pdo->prepare(
    'SELECT * FROM invoices
     WHERE id = :invoice_id
     AND user_id = :user_id
     LIMIT 1'
);

$stmt->execute([
    'invoice_id' => $invoiceId,
    'user_id'    => $_SESSION['user_id']
]);
?>

Never rely on hidden fields, disabled buttons, JavaScript, or obscure URLs as authorization.

Protect Administrator Routes Centrally

A common core PHP mistake is adding authorization checks manually to dozens of files and eventually forgetting one.

Create reusable security functions or middleware-style bootstrap logic.

For example:

<?php
function requireAdmin(): void
{
    if (
        empty($_SESSION['user_id']) ||
        ($_SESSION['role'] ?? '') !== 'admin'
    ) {
        http_response_code(403);
        exit('Access denied.');
    }
}
?>

Call the authorization control before any sensitive output or state-changing logic.

Secure File Uploads Aggressively

File uploads are one of the highest-risk features in a custom PHP website.

An insecure upload form can potentially allow an attacker to place executable PHP code on your server.

Do not trust:

  • The original filename.
  • The file extension alone.
  • The browser-provided MIME type.
  • The user’s claim about the file.

Use A Strict Upload Allowlist

Allow only file types the feature actually needs.

If a profile system needs JPEG and PNG images, do not allow arbitrary PHP, HTML, SVG, ZIP, executable, or server-configuration files.

A strong upload workflow should consider:

  1. Check Upload Errors.
  2. Enforce Maximum Size.
  3. Validate File Content And MIME Type.
  4. Allow Only Required Extensions.
  5. Generate A New Random Filename.
  6. Store Outside The Public Web Root Where Practical.
  7. Serve Files Through Controlled Application Logic.

Never Save Uploads Using The User’s Original Filename

Generate your own server-side identifier:

<?php
$filename = bin2hex(random_bytes(16)) . '.jpg';
?>

Store the original filename as metadata only if the application genuinely needs it.

This prevents path manipulation, filename collisions, and several classes of unsafe filename behavior.

Store Uploads Outside The Web Root When Possible

If uploaded files do not need to be accessed directly by URL, keep them outside the public document root.

Then serve authorized files through a PHP download handler.

This architecture can prevent an uploaded file from being directly executed as server-side code even if another validation control fails.

For publicly served upload directories, explicitly disable script execution at the web-server level.

Never Use User Input Directly In Include Or Require

This is dangerous:

<?php
include $_GET['page'] . '.php';
?>

An attacker may attempt path traversal or local file inclusion.

Instead, create a mapping:

<?php
$pages = [
    'home'  => __DIR__ . '/views/home.php',
    'about' => __DIR__ . '/views/about.php'
];

$page = $_GET['page'] ?? 'home';

if (!isset($pages[$page])) {
    http_response_code(404);
    exit('Page not found.');
}

require $pages[$page];
?>

Never allow arbitrary request strings to become filesystem paths.

Avoid Executing Shell Commands With User Input

Functions such as:

  • exec()
  • shell_exec()
  • system()
  • passthru()

can become extremely dangerous when user-controlled input reaches them.

Whenever possible, use native PHP libraries rather than shell commands.

If external commands are unavoidable, design a strict allowlist of predefined operations rather than constructing command strings from request data.

Keep Secrets Outside The Public Web Directory

Database passwords, API credentials, encryption keys, SMTP passwords, and private tokens should not be stored in publicly retrievable files.

A cleaner project structure could resemble:

/var/www/myapp/
    config/
    src/
    storage/
    public/
        index.php
        assets/

Configure the web server so only the public directory is exposed as the document root.

Application configuration, logs, backups, and private uploads should remain outside it.

Never Commit Secrets To Public Git Repositories

Removing a password from the latest commit does not necessarily remove it from Git history.

Use deployment secrets, protected environment configuration, or another secure secret-management approach.

If a credential is accidentally exposed, rotate it. Do not assume deleting the file makes the leaked credential safe again.

Disable Detailed PHP Errors In Production

Development errors are useful to developers but valuable to attackers.

A production page should not expose:

  • Filesystem paths.
  • Stack traces.
  • SQL queries.
  • Database usernames.
  • Library versions.
  • Source code fragments.

A typical production direction is:

display_errors = Off
log_errors = On

Return generic errors to users while recording useful diagnostic information in protected server-side logs.

Do Not Put Sensitive Data In Logs

Logs are security assets too.

Avoid logging:

  • Plaintext passwords.
  • Full session identifiers.
  • Authentication tokens.
  • Private encryption keys.
  • Full payment card data.

Log enough information to investigate incidents without turning the log file into another sensitive-data repository.

Log Security-Relevant Events

Useful events include:

  • Successful administrator logins.
  • Failed login attempts.
  • Password changes.
  • Permission changes.
  • CSRF failures.
  • Authorization denials.
  • Suspicious uploads.
  • Major configuration changes.
  • Unexpected application errors.

Logs are most useful when someone actually reviews or alerts on them.

A thousand failed administrator logins in five minutes should not remain unnoticed in a file for six months.

Add Essential Security Response Headers

Security headers give browsers additional instructions about how your pages should be handled.

Useful headers can include:

  • Content-Security-Policy
  • Strict-Transport-Security
  • X-Content-Type-Options: nosniff
  • Referrer-Policy
  • Permissions-Policy
  • X-Frame-Options For Legacy Frame Protection Where Needed.

For modern clickjacking protection, CSP’s frame-ancestors directive is generally preferable where supported.

Do not rely on the obsolete X-XSS-Protection header as your XSS defense.

Core-PHP-Website-Security-Common-Hacking-Attempts

Configure CORS Narrowly

Cross-Origin Resource Sharing is often misconfigured on custom APIs.

Avoid blindly returning:

Access-Control-Allow-Origin: *

for sensitive authenticated endpoints.

Allow only origins that genuinely require cross-origin browser access, and understand how credentials interact with CORS.

CORS is a browser access-control mechanism. It is not a replacement for API authentication or authorization.

Protect Sensitive Actions With POST Instead Of GET

Do not design destructive operations like:

/delete-user.php?id=15

as simple GET requests.

Use an appropriate state-changing HTTP method, validate authorization, and require CSRF protection.

A link should not silently perform an irreversible administrative action.

Prevent Open Redirect Vulnerabilities

Login systems sometimes accept a destination such as:

?redirect=https://example.com

If arbitrary domains are accepted, attackers may use your trusted domain to redirect users to phishing sites.

Use an allowlist of internal routes or validate that redirects remain within your trusted origin.

Secure Password Reset Flows

Password reset tokens should be:

  • Random.
  • Single-use.
  • Time-limited.
  • Stored securely.

Generate tokens with random_bytes().

A good design stores a cryptographic hash of the reset token in the database rather than storing the usable bearer token in plaintext.

After a successful password reset, invalidate the token and consider invalidating existing sessions where appropriate.

Protect Email Forms Against Header Injection

If a PHP contact form passes user values into mail headers, attackers may attempt newline injection to create additional headers or recipients.

Do not accept arbitrary user strings as mail header values.

Validate email addresses properly and use a well-maintained mail library or SMTP client rather than constructing complex raw email headers manually.

Set Safe File And Directory Permissions

Do not solve permission problems by setting everything to 777.

Your web server should have only the filesystem access it actually needs.

Typically:

  • Application source should not be writable by anonymous visitors.
  • Upload directories should not execute PHP.
  • Configuration files should have restricted read permissions.
  • Log directories should not be publicly accessible.
  • Backups should not exist in the public document root.

Exact permission values depend on your hosting model and server ownership configuration.

Disable Directory Listing

A web server should not automatically display a file directory simply because no index file exists.

Disable directory indexing unless your application intentionally provides file listings.

This reduces accidental exposure of:

  • Backups.
  • Temporary files.
  • Upload contents.
  • Internal assets.

Never Leave Backup Files Inside The Public Website

Files such as these can be dangerous:

  • database.sql
  • backup.zip
  • config.php.bak
  • index-old.php
  • .env.backup

A web server may deliver an unknown backup extension as plain text even when the original PHP file would normally execute.

Store backups outside the public web root and restrict access separately.

Rate Limit Sensitive Endpoints

Rate limiting can reduce automated abuse against:

  • Login forms.
  • Password reset forms.
  • Registration.
  • Contact forms.
  • Search endpoints.
  • OTP verification.
  • API endpoints.

Rate limiting can be implemented through application logic, Redis, reverse proxies, web servers, cloud security services, or a combination.

Avoid a simplistic global IP block that prevents hundreds of legitimate users behind the same corporate or mobile network from accessing the system.

Use CAPTCHA Only Where Abuse Justifies The Friction

CAPTCHA can help slow automated attacks but adds usability cost.

Consider applying it after suspicious behavior rather than showing a challenge to every visitor immediately.

CAPTCHA should supplement:

  • Server-side validation.
  • Rate limiting.
  • Authentication controls.
  • Authorization.

It does not replace secure application logic.

Protect APIs With The Same Standards As Web Pages

An AJAX or mobile API endpoint is not secure merely because users cannot see it in the navigation.

Apply:

  • Authentication.
  • Authorization.
  • Input validation.
  • Rate limiting.
  • Prepared SQL queries.
  • Secure error handling.

Never trust a role, price, owner ID, permission level, or account balance submitted by the browser simply because your frontend normally sends the correct value.

Verify Webhook Signatures Before Processing Requests

If external systems send payment, CRM, e-commerce, or telephony webhooks to your PHP website, verify authenticity before accepting the event.

Use the provider’s signature or HMAC verification scheme.

Where supported, also protect against replay attacks using timestamps, nonce values, or event IDs.

Do not rely exclusively on an IP address unless the provider specifically documents a stable IP-based trust model.

Prevent Duplicate Sensitive Transactions

Security also includes integrity.

Payment callbacks, order creation, wallet deductions, and other sensitive operations should be idempotent where appropriate.

If the same external event arrives twice, your application should not create two payments or deduct money twice.

Store unique event identifiers and reject or safely reuse previously processed operations.

Use Database Transactions For Related Critical Changes

If several database updates must succeed together, use a transaction.

For example:

  1. Create Order.
  2. Deduct Balance.
  3. Create Transaction Record.

If the third step fails after the balance is deducted, the system may become inconsistent.

Transactions allow the entire operation to be rolled back when a critical step fails.

Protect Against Mass Assignment

Do not blindly loop through every submitted field and update corresponding database columns.

This pattern is dangerous:

<?php
foreach ($_POST as $column => $value) {
    // Automatically update matching columns.
}
?>

An attacker may submit fields the form never displayed, such as:

  • role=admin
  • is_verified=1
  • account_balance=999999

Create an explicit allowlist of fields the current operation is permitted to modify.

Do Not Trust Hidden Form Fields

Hidden HTML fields are visible and editable to the user.

Never trust hidden values for:

  • Prices.
  • Discounts.
  • User roles.
  • Ownership.
  • Subscription status.
  • Permissions.

Retrieve sensitive values from trusted server-side data and verify them again when processing the request.

Separate Public And Administrative Applications Where Practical

For higher-risk systems, consider giving the administrative interface additional protections.

Possible layers include:

  • MFA.
  • Stronger rate limits.
  • Shorter sessions.
  • Separate subdomain.
  • VPN or IP restrictions where operationally appropriate.
  • Additional audit logging.

The public marketing website and the financial administration console do not necessarily need identical risk controls.

Use Secure Randomness For Security Tokens

Use random_bytes() for values such as:

  • Password reset tokens.
  • Email verification tokens.
  • CSRF tokens.
  • API keys.
  • Random filenames.

Do not generate security tokens using:

  • rand()
  • mt_rand()
  • Timestamp alone.
  • User ID alone.
  • Predictable sequential values.

Security tokens need enough entropy that attackers cannot realistically guess them.

Use Hash_Equals For Secret Comparisons

When comparing cryptographic signatures or security tokens, use constant-time comparison functions such as hash_equals() where appropriate.

This reduces information leakage through timing differences during secret comparison.

Do Not Invent Your Own Encryption

Cryptography is easy to misuse.

Do not create encryption systems by combining Base64, XOR, custom substitutions, or homemade algorithms.

Base64 is encoding, not encryption.

When sensitive data genuinely requires application-level encryption, use established authenticated cryptographic primitives through reputable libraries and manage encryption keys separately from encrypted data.

Minimize Sensitive Data Storage

The safest sensitive information to protect is information you never store unnecessarily.

Ask:

  • Do we actually need this field?
  • How long must it be retained?
  • Can it be deleted after processing?
  • Can a payment provider store the sensitive payment information instead?

Reducing stored sensitive data lowers the impact of a future compromise.

Back Up The Website And Test Restoration

Security is not only prevention.

You also need recovery.

Maintain protected backups of:

  • Database.
  • User uploads.
  • Application code.
  • Important configuration.

Keep at least one backup copy separate from the production server.

A backup that has never been restored in a test environment is only an assumption.

Do Not Expose Backup Credentials To The Production Website

An attacker who compromises your application should not automatically gain the ability to delete every backup.

Separate backup credentials and storage permissions from normal production credentials where your infrastructure allows it.

Monitor File Changes And Unexpected Accounts

Indicators of compromise can include:

  • Unknown administrator users.
  • Modified PHP files.
  • Unexpected cron jobs.
  • New executable files in upload directories.
  • Unexpected outbound network connections.
  • Changed configuration.

Create a baseline of expected production files and investigate unexplained changes.

Use A WAF As Defense In Depth

A web application firewall can block or challenge some malicious traffic before it reaches PHP.

It may help with:

  • Known exploit patterns.
  • Malicious bots.
  • Request floods.
  • Basic injection attempts.

However, a WAF cannot fix insecure authorization logic, broken business rules, or unsafe code.

Never use a firewall as an excuse to leave SQL injection or XSS vulnerabilities in the application.

Test Your Own Application Before Attackers Do

Security testing should be part of development, not only an emergency response.

Review high-risk areas such as:

  • Authentication.
  • Password reset.
  • Administration.
  • File uploads.
  • API endpoints.
  • Search and filtering.
  • Dynamic SQL.
  • Downloads.
  • Payments.

Use static analysis, dependency auditing, automated scanners, code review, and manual testing together.

Automated scanners are useful, but they cannot fully understand custom business authorization rules.

Use OWASP As A Security Baseline

The OWASP Top 10 and OWASP Cheat Sheet Series provide practical security guidance covering the vulnerability classes most PHP developers need to understand.

Particularly useful areas include:

  • SQL Injection Prevention.
  • XSS Prevention.
  • CSRF Prevention.
  • Authentication.
  • Session Management.
  • Password Storage.
  • File Upload Security.
  • Security Headers.
  • Logging.

Do not wait for a penetration test to introduce these concepts into your development workflow.

A Practical Core PHP Security Architecture

A reasonably structured plain PHP application might use:

/app/
    Config/
    Controllers/
    Services/
    Security/
    Repositories/
    Views/
/storage/
    logs/
    private-uploads/
/public/
    index.php
    assets/
/vendor/

Only /public/ should normally be directly reachable from the web server.

Security logic can then be centralized inside reusable components rather than duplicated across random PHP files.

Create Reusable Security Helpers

Useful centralized functions can handle:

  • CSRF generation and verification.
  • Authentication checks.
  • Authorization.
  • Output escaping.
  • Rate limiting.
  • Security logging.
  • Validation.

Centralization reduces the chance that one page forgets an important check.

However, avoid building a giant homemade security framework without tests. Simple, understandable abstractions are safer than clever code nobody can audit.

Core PHP Security Checklist Before Production

  • PHP Version: Is It Currently Supported And Fully Patched?
  • HTTPS: Is Every Sensitive Request Encrypted?
  • SQL: Are All User Values Parameterized?
  • XSS: Is Untrusted Output Encoded For Its Context?
  • CSRF: Do Sensitive Forms Verify Tokens?
  • Sessions: Are Secure, HttpOnly, SameSite, And Strict Mode Configured?
  • Login: Is The Session ID Regenerated After Authentication?
  • Passwords: Are Password_Hash And Password_Verify Used?
  • Brute Force: Are Login Attempts Rate Limited?
  • MFA: Are High-Value Accounts Better Protected?
  • Authorization: Is Permission Checked On Every Sensitive Request?
  • Uploads: Are Type, Size, Filename, Storage, And Execution Controlled?
  • Filesystem: Are Permissions Restricted?
  • Includes: Can User Input Ever Become A File Path?
  • Secrets: Are Credentials Outside The Public Web Root And Git?
  • Errors: Are Detailed Errors Hidden From Production Users?
  • Headers: Are Appropriate Browser Security Headers Enabled?
  • Logs: Are Security Events Recorded Without Logging Secrets?
  • Backups: Are Backups Protected And Tested?
  • Monitoring: Will Someone Notice Suspicious Activity?

Common Core PHP Security Mistakes

  • Concatenating User Input Into SQL: Use parameterized queries.
  • Using Addslashes For SQL Security: Escaping is not a replacement for prepared statements.
  • Using MD5 Or SHA-256 For Passwords: Use password-specific hashing.
  • Trusting JavaScript Validation: Validate again on the server.
  • Escaping Everything On Input: Apply correct encoding at output context.
  • Storing Uploads As Original Filenames: Generate server-side names.
  • Allowing PHP Execution In Upload Folders: Separate uploaded content from executable code.
  • Trusting Hidden Fields: Recalculate sensitive values server-side.
  • Checking Login But Not Authorization: Verify access to each object and action.
  • Displaying PHP Errors In Production: Log details privately.
  • Using 777 Permissions: Follow least privilege.
  • Keeping Backups In Public_html: Store them outside the web root.
  • Depending Only On A WAF: Fix vulnerabilities in code.
  • Ignoring Updates: Patch PHP, dependencies, OS, database, and server software.

A 10-Step Core PHP Hardening Plan

  1. Upgrade To A Supported Fully Patched PHP Release.
  2. Force HTTPS And Secure Session Cookies.
  3. Convert Every Database Query To Prepared Statements.
  4. Audit Every User-Controlled Output For XSS.
  5. Add CSRF Tokens To Every Sensitive State-Changing Form.
  6. Harden Authentication, Password Storage, Sessions, And Rate Limits.
  7. Audit Authorization For IDOR And Privilege Escalation.
  8. Lock Down Uploads, Includes, Filesystem Access, And Secrets.
  9. Disable Production Error Disclosure And Add Security Logging.
  10. Back Up, Monitor, Patch, And Retest Continuously.

This order addresses vulnerabilities that frequently have the highest application-level impact while also improving operational recovery.

What To Do If You Think Your PHP Website Is Already Hacked

Do not simply delete one suspicious file and assume the incident is over.

A proper response may include:

  1. Restrict Or Isolate The Compromised Environment.
  2. Preserve Relevant Logs And Evidence.
  3. Identify The Initial Vulnerability.
  4. Rotate Database, API, SMTP, Hosting, And Administrator Credentials.
  5. Review Administrator Accounts And Sessions.
  6. Compare Application Files Against Known-Good Source.
  7. Patch The Root Cause.
  8. Restore From A Verified Clean Backup When Appropriate.
  9. Monitor Closely After Recovery.

If sensitive customer data may have been accessed, legal, contractual, regulatory, or notification obligations may also apply.

Incident response is different from routine hardening and may require a professional forensic investigation.

Final Verdict On Securing A Core PHP Website

Core PHP is not inherently insecure.

What makes custom PHP applications risky is that the developer is directly responsible for security controls that a mature framework might otherwise provide automatically.

The first priority is to keep the entire technology stack supported and patched.

A beautifully written PHP application can still be vulnerable if it runs on outdated PHP, an old web server, vulnerable image-processing libraries, or insecure dependencies.

Next, treat every external value as untrusted.

Validate inputs according to strict business rules. Use PDO prepared statements instead of string-concatenated SQL. Encode untrusted data when rendering it into HTML to prevent XSS.

Add CSRF tokens to sensitive state-changing actions.

Secure sessions using HTTPS-only cookies, HttpOnly, appropriate SameSite settings, strict session mode, expiration controls, and session ID regeneration after authentication or major privilege changes.

Passwords deserve specialized protection.

Use PHP’s password_hash() and password_verify() APIs instead of inventing password hashing. Consider Argon2id where supported and appropriate, and rehash older passwords as requirements evolve.

Authentication alone is not enough.

Every sensitive record and action needs authorization. A logged-in customer should never gain access to another customer’s record merely by changing an ID in a URL or request body.

Treat uploads as potentially hostile.

Allow only required formats, check size and content, rename files server-side, and store uploads outside the public web root whenever practical. Uploaded data should never unexpectedly become executable PHP code.

Never allow request parameters to become arbitrary paths passed into include, require, filesystem operations, or shell commands.

Protect infrastructure as well as code.

Keep secrets outside the public web directory, restrict filesystem and database privileges, disable directory listing, keep backups away from the website, and prevent production PHP errors from exposing implementation details.

Add modern security response headers, including a properly tested Content Security Policy where practical, but remember that browser headers are additional layers rather than replacements for secure application logic.

Rate-limit high-risk endpoints, monitor authentication failures, log authorization violations, review unexpected file changes, and use MFA for sensitive administrative accounts.

Finally, assume security will require continuous maintenance.

New vulnerabilities are discovered. Dependencies change. Attack methods evolve. Employees change roles. Old accounts remain active. Configuration drifts.

A Secure Core PHP Website Is Not Created By One Security Snippet. It Is Built Through Prepared Queries, Correct Output Encoding, CSRF Protection, Strong Authentication, Strict Authorization, Secure Sessions, Safe Uploads, Least Privilege, Patch Management, Monitoring, Backups, And Continuous Testing Working Together.

Everyday Reader Frequently Asked Questions

How Can I Secure A Core PHP Website From Hackers?

Use a supported patched PHP version, HTTPS, PDO prepared statements, server-side input validation, contextual output encoding, CSRF tokens, secure session cookies, password_hash(), strict authorization, safe file uploads, least-privilege database permissions, security headers, rate limiting, logging, backups, and continuous patching. No single technique protects against every attack.

How Do I Prevent SQL Injection In Core PHP?

Use PDO or MySQLi prepared statements with bound parameters instead of concatenating user input into SQL strings. For SQL elements that cannot be parameterized, such as predefined sort orders, map user choices to a strict server-side allowlist rather than inserting arbitrary request values into queries.

How Do I Prevent XSS Attacks In PHP?

Validate data according to business rules and apply context-appropriate output encoding when untrusted content is rendered. For ordinary HTML text, htmlspecialchars() with ENT_QUOTES and UTF-8 is a common protection. Avoid inserting user-controlled values directly into JavaScript, CSS, raw HTML, or unsafe attributes.

What Is The Best Way To Store Passwords In PHP?

Use PHP’s password_hash() function to create password hashes and password_verify() to authenticate users. PASSWORD_DEFAULT provides a safe PHP-managed default, while Argon2id is available on supported builds. Never store plaintext passwords or use fast general-purpose hashes such as MD5 or raw SHA-256 for password storage.

Can A Core PHP Website Be Completely Hack Proof?

No website can realistically be guaranteed completely hack proof. Security is risk reduction through multiple layers, including secure code, updated software, strong authentication, authorization, server hardening, monitoring, backups, incident response, and regular testing. The goal is to prevent common attacks and limit the impact when a control fails.

YOU LIKE IT? PLEASE SHARE THIS RECIPE WITH YOUR FRIENDS

Be the first to write a comment.

Leave a Reply

Your email address will not be published. Required fields are marked *