Software Engineering & Digital Products for Global Enterprises since 2006
CMMi Level 3SOC 2ISO 27001
Menu
View all services
Staff Augmentation
Embed senior engineers in your team within weeks.
Dedicated Teams
A ring-fenced squad with PM, leads, and engineers.
Build-Operate-Transfer
We hire, run, and transfer the team to you.
Contract-to-Hire
Try the talent. Convert when you're ready.
ForceHQ
Skill testing, interviews and ranking — powered by AI.
RoboRingo
Build, deploy and monitor voice agents without code.
MailGovern
Policy, retention and compliance for enterprise email.
Vishing
Test and train staff against AI-driven voice attacks.
CyberForceHQ
Continuous, adaptive security training for every team.
IDS Load Balancer
Built for Multi Instance InDesign Server, to distribute jobs.
AutoVAPT.ai
AI agent for continuous, automated vulnerability and penetration testing.
Salesforce + InDesign Connector
Bridge Salesforce data into InDesign to design print catalogues at scale.
View all solutions
Banking, Financial Services & Insurance
Cloud, digital and legacy modernisation across financial entities.
Healthcare
Clinical platforms, patient engagement, and connected medical devices.
Pharma & Life Sciences
Trial systems, regulatory data, and field-force enablement.
Professional Services & Education
Workflow automation, learning platforms, and consulting tooling.
Media & Entertainment
AI video processing, OTT platforms, and content workflows.
Technology & SaaS
Product engineering, integrations, and scale for tech companies.
Retail & eCommerce
Shopify, print catalogues, web-to-print, and order automation.
View all industries
Blog
Engineering notes, opinions, and field reports.
Case Studies
How clients shipped — outcomes, stack, lessons.
White Papers
Deep-dives on AI, talent models, and platforms.
Portfolio
Selected work across industries.
View all resources
About Us
Who we are, our story, and what drives us.
Co-Innovation
How we partner to build new products together.
Careers
Open roles and what it's like to work here.
News
Press, announcements, and industry updates.
Leadership
The people steering MetaDesign.
Locations
Gurugram, Brisbane, Detroit and beyond.
Contact Us
Talk to sales, hiring, or partnerships.
Request TalentStart a Project
Web Development

How to Write Secure PHP Code: Preventing SQL Injection, XSS, and CSRF

AG
Amit Gupta
Technical Content Lead
January 13, 2025
10 min read
How to Write Secure PHP Code: Preventing SQL Injection, XSS, and CSRF — Web Development | MetaDesign Solutions

The OWASP Top 10 and Why PHP Is a Prime Target

PHP powers over 75% of all websites with a known server-side language, including WordPress, Laravel, and Drupal. This ubiquity makes PHP applications a prime target for attackers. The OWASP Top 10—the industry-standard classification of critical web application vulnerabilities—consistently lists Injection (SQL Injection) and Cross-Site Scripting (XSS) among the most dangerous threats. Understanding these attack vectors and implementing robust defenses is not optional; it is a fundamental professional responsibility for every PHP developer shipping code to production.

Deep Dive: SQL Injection Mechanics and Prevention

SQL Injection (SQLi) occurs when user-supplied input is concatenated directly into an SQL query string. An attacker can input `admin' OR '1'='1` into a login form, transforming a legitimate `WHERE` clause into one that always evaluates to true, bypassing authentication entirely. The definitive prevention is Prepared Statements with Parameterized Queries using PDO: `$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email"); $stmt->execute(['email' => $userInput]);`. This ensures the database engine treats the input as a literal data value, never as executable SQL code, regardless of what the attacker types.

Defending Against Cross-Site Scripting (XSS)

Cross-Site Scripting (XSS) allows attackers to inject malicious JavaScript into pages viewed by other users. Stored XSS persists the payload in the database (e.g., a malicious comment). Reflected XSS embeds the payload in a URL parameter. DOM-based XSS manipulates the client-side JavaScript directly. The primary defense is output encoding: always pass user-generated content through `htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')` before rendering it in HTML. For rendering user-supplied HTML (like a blog editor), use a dedicated sanitization library like HTMLPurifier to strip dangerous tags while preserving safe formatting.

Implementing Cross-Site Request Forgery (CSRF) Tokens

CSRF attacks trick an authenticated user's browser into submitting a forged request (like changing their email address or transferring funds) to your application. Because the browser automatically attaches cookies, the server cannot distinguish the forged request from a legitimate one. The defense is a Synchronizer Token Pattern: on every form render, generate a unique, random token using `bin2hex(random_bytes(32))`, store it in `$_SESSION['csrf_token']`, and embed it as a hidden `` field. On form submission, compare the submitted token against the session token. If they don't match, reject the request immediately.

Secure Password Storage with bcrypt and Argon2

Never store passwords in plaintext or using weak hashing algorithms like MD5 or SHA-1. PHP provides the `password_hash()` function, which defaults to the bcrypt algorithm with automatic salting. Use `password_hash($password, PASSWORD_BCRYPT)` for hashing and `password_verify($userInput, $storedHash)` for verification. For maximum security, PHP 7.2+ supports Argon2id via `PASSWORD_ARGON2ID`, which is resistant to both GPU-based brute-force attacks and side-channel timing attacks, making it the recommended choice for new applications handling sensitive financial or healthcare data.

Transform Your Publishing Workflow

Our experts can help you build scalable, API-driven publishing systems tailored to your business.

Book a free consultation

Hardening Session Management

Sessions are the backbone of PHP authentication, and mismanaging them creates critical vulnerabilities. After a successful login, always call `session_regenerate_id(true)` to prevent Session Fixation attacks. Configure session cookies with the `HttpOnly` flag (preventing JavaScript access), the `Secure` flag (HTTPS only), and the `SameSite=Strict` attribute (preventing CSRF via cookies). Set aggressive session timeouts using `session.gc_maxlifetime`. For high-security applications, bind the session to the user's IP address and User-Agent string, invalidating the session if either changes unexpectedly.

Input Validation, Sanitization, and Content Security Policy

Defense in depth requires validating all user input. Use PHP's `filter_var()` with specific filters like `FILTER_VALIDATE_EMAIL`, `FILTER_VALIDATE_URL`, and `FILTER_VALIDATE_INT` to reject malformed data at the application boundary. Beyond server-side defenses, implement a Content Security Policy (CSP) HTTP header. A strict CSP like `Content-Security-Policy: default-src 'self'; script-src 'self'` instructs the browser to only execute JavaScript loaded from your own domain, providing a powerful last line of defense against XSS even if your server-side sanitization has a gap.

Automated Security Testing and Penetration Testing

No amount of careful coding replaces systematic testing. Integrate Static Application Security Testing (SAST) tools like PHPStan (with the `phpstan-strict-rules` extension) and SonarQube into your CI/CD pipeline to scan for vulnerabilities on every commit without executing code. For runtime testing, use Dynamic Application Security Testing (DAST) tools like OWASP ZAP or Burp Suite, which simulate real-world attacks against your running application. Schedule periodic third-party penetration tests using gray-box methodology for the most comprehensive security assessment.

FAQ

Frequently Asked Questions

Common questions about this topic, answered by our engineering team.

Prepared statements separate the SQL query structure from the user-supplied data. The database engine parses the query template first, then binds the user input as literal values. This makes it physically impossible for injected input to alter the query's logic, regardless of what the attacker types.

htmlspecialchars() escapes ALL HTML, converting tags into harmless display characters. It is ideal for contexts where no HTML should be rendered. HTMLPurifier is a whitelist-based sanitizer that allows safe HTML tags (like <b>, <i>, <a>) while stripping dangerous ones (like <script>), ideal for rich-text editors.

The CSRF token is a secret, random value tied to the user's session. An attacker forging a request from a different website cannot know or guess this token. When the server receives the form, it validates the token against the session; a mismatch means the request is forged and is rejected.

Both are strong choices. Bcrypt (PASSWORD_BCRYPT) is the battle-tested default in PHP's password_hash(). Argon2id (PASSWORD_ARGON2ID), available in PHP 7.2+, is the newer standard that provides stronger resistance to GPU attacks and side-channel exploits, recommended for new high-security applications.

Use SAST tools like PHPStan and SonarQube for static code analysis in your CI/CD pipeline. Use DAST tools like OWASP ZAP and Burp Suite for runtime attack simulation. Combining both approaches provides the most comprehensive security coverage.

Discussion

Join the Conversation

Ready when you are

Let's build something great together.

A 30-minute call with a principal engineer. We'll listen, sketch, and tell you whether we're the right partner — even if the answer is no.

Talk to a strategist
Need help with your project? Let's talk.
Book a call