with one click
sc-lang-php
PHP-specific security deep scan
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
PHP-specific security deep scan
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Comprehensive AI-powered security scanning suite with 48 skills covering OWASP Top 10, 7 language-specific deep scanners (Go, TypeScript, Python, PHP, Rust, Java, C#), supply chain analysis, infrastructure-as-code scanning, and 3000+ checklist items. Use when you need to run a security audit, find vulnerabilities, scan a PR for security issues, or perform a penetration test on a codebase.
C#/.NET-specific security deep scan
Go-specific security deep scan
Java/Kotlin-specific security deep scan
Python-specific security deep scan
Rust-specific security deep scan
| name | sc-lang-php |
| description | PHP-specific security deep scan |
| license | MIT |
| metadata | {"author":"ersinkoc","category":"security","version":"1.0.0"} |
Detects PHP-specific security anti-patterns including deserialization gadgets, type juggling, include-based attacks, and framework-specific vulnerabilities in Laravel and WordPress. Focuses on PHP's unique type system, legacy functions, and common misconfigurations.
Activates when PHP is detected in security-report/architecture.md.
References references/php-security-checklist.md.
// VULNERABLE: Unserializing user input → RCE via magic methods
$data = unserialize($_POST['data']); // __wakeup, __destruct chain
// SAFE: Use JSON or restrict classes
$data = json_decode($_POST['data'], true);
$data = unserialize($input, ['allowed_classes' => false]);
// VULNERABLE: phar:// triggers deserialization without unserialize()
file_exists("phar://" . $_GET['file']); // Triggers metadata deserialization!
// Also: file_get_contents, fopen, is_dir, etc.
// SAFE: Validate file path, never use user input in phar://
// VULNERABLE: User input in include path
include($_GET['page'] . '.php'); // LFI: ?page=../../etc/passwd%00
// SAFE: Allowlist
$allowed = ['home', 'about', 'contact'];
$page = in_array($_GET['page'], $allowed) ? $_GET['page'] : 'home';
include($page . '.php');
// VULNERABLE: Overwrites existing variables including auth vars
extract($_POST); // If POST contains 'isAdmin=1' → $isAdmin = 1
// SAFE: Never use extract() on user input; use specific variables
$name = $_POST['name'];
// VULNERABLE: Loose comparison type juggling
if ($_POST['password'] == $storedHash) { /* ... */ } // "0" == "0e462097..." is TRUE!
if (strcmp($_POST['token'], $expectedToken) == 0) { /* ... */ }
// strcmp([], "string") returns NULL, and NULL == 0 is TRUE!
// SAFE: Strict comparison
if ($_POST['password'] === $storedHash) { /* ... */ }
if (hash_equals($expectedToken, $_POST['token'])) { /* ... */ }
// VULNERABLE: /e flag executes PHP code (deprecated in PHP 7)
preg_replace('/.*/e', $_GET['code'], ''); // Code execution!
// SAFE: Use preg_replace_callback
preg_replace_callback('/pattern/', function($matches) { /* ... */ }, $input);
// VULNERABLE: No $fillable or $guarded
class User extends Model {} // All fields mass-assignable
User::create($request->all()); // role, is_admin controllable
// SAFE
class User extends Model {
protected $fillable = ['name', 'email'];
}
// VULNERABLE: Raw with interpolation
DB::select("SELECT * FROM users WHERE name = '$name'");
User::whereRaw("name = '$name'")->get();
// SAFE: Parameterized
DB::select("SELECT * FROM users WHERE name = ?", [$name]);
User::whereRaw("name = ?", [$name])->get();
// SAFE: Blade {{ }} auto-escapes
{{ $userInput }} // HTML-encoded
// VULNERABLE: {!! !!} renders raw HTML
{!! $userInput !!} // XSS if user-controlled!
// VULNERABLE: Missing nonce verification
function handle_ajax() {
$data = $_POST['data']; // No nonce check → CSRF!
}
// SAFE
function handle_ajax() {
check_ajax_referer('my_nonce', 'security');
$data = sanitize_text_field($_POST['data']);
}
// VULNERABLE: $wpdb without prepare
$wpdb->query("SELECT * FROM users WHERE id = " . $_GET['id']);
// SAFE
$wpdb->prepare("SELECT * FROM users WHERE id = %d", $_GET['id']);
// VULNERABLE: Interpolation in PDO query
$pdo->query("SELECT * FROM users WHERE id = " . $_GET['id']);
// SAFE: Prepared statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]);
// VULNERABLE: User-controlled URL
$content = file_get_contents($_GET['url']); // SSRF!
// Also reads local files: file:///etc/passwd
// SAFE: Validate URL scheme and host
$url = filter_var($_GET['url'], FILTER_VALIDATE_URL);
$parsed = parse_url($url);
if (!in_array($parsed['host'], $allowedHosts)) die('Blocked');
// VULNERABLE: Session fixation
session_start(); // Accepts session ID from URL or cookie without regeneration
// SAFE: Regenerate on auth change
session_start();
session_regenerate_id(true); // After login
// Configuration
ini_set('session.use_only_cookies', 1);
ini_set('session.use_strict_mode', 1);
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Strict');
// VULNERABLE: assert() can execute code (PHP < 8.0)
assert($_GET['expr']); // Executes as PHP code!
// PHP 8.0+ treats assert() as a language construct, not eval
composer.jsonscripts section for dangerous post-install hookscomposer.lock is committed and reviewed// VULNERABLE: SSRF + SSL bypass
$ch = curl_init($_GET['url']);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disables SSL!
$result = curl_exec($ch);
// SAFE: Validate URL, enable SSL
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); // HTTPS only
// VULNERABLE: MD5 for passwords
$hash = md5($password);
$hash = md5($password . $salt); // Still weak!
// SAFE: password_hash (bcrypt by default)
$hash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($input, $hash)) { /* ... */ }
// VULNERABLE: strcmp returns NULL for non-string args
if (strcmp($_POST['token'], $secret) == 0) { /* ... */ }
// POST token[]=1 → strcmp([], "secret") returns NULL → NULL == 0 is TRUE
// SAFE: Type check first or use hash_equals
if (!is_string($_POST['token'])) die('Invalid');
if (hash_equals($secret, $_POST['token'])) { /* ... */ }
// VULNERABLE: Display errors in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
// SAFE: Log only
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php/error.log');