php-framework/security-checks.yaml

537 lines
16 KiB
YAML
Raw Permalink Normal View History

# PHP Security Checks Specification
# For `core php security` command implementation in Go
#
# Usage: core php security [--fix] [--json] [--severity=high]
#
# This file defines security checks that can be run without PHP runtime
# by parsing files directly or shelling out to existing tools.
name: PHP Security Checks
version: 1.0.0
# Severity levels (exit codes)
severity_levels:
critical: 1 # Must fix before deploy
high: 2 # Should fix soon
medium: 3 # Recommended fix
low: 4 # Nice to have
info: 0 # Informational only
# =============================================================================
# ENVIRONMENT CHECKS
# Parse .env file directly - no PHP needed
# =============================================================================
env_checks:
- id: debug_mode
name: Debug Mode Disabled
description: APP_DEBUG must be false in production
severity: critical
key: APP_DEBUG
condition: "!= true"
when_env: [production, prod, live, staging]
message: "Debug mode exposes sensitive information to users"
fix: "Set APP_DEBUG=false in .env"
cwe: CWE-215
- id: app_key_set
name: Application Key Set
description: APP_KEY must be set and valid
severity: critical
key: APP_KEY
condition: "exists && length >= 32"
message: "Missing or weak encryption key"
fix: "Run: php artisan key:generate"
cwe: CWE-321
- id: app_key_not_default
name: Application Key Not Default
description: APP_KEY must not be a known default value
severity: critical
key: APP_KEY
condition: "not_in"
bad_values:
- "base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- "SomeRandomString"
message: "Using default or example APP_KEY"
cwe: CWE-798
- id: secure_cookies
name: Secure Cookies Enabled
description: SESSION_SECURE_COOKIE should be true for HTTPS
severity: high
key: SESSION_SECURE_COOKIE
condition: "== true"
when_env: [production, prod, live]
message: "Cookies can be intercepted over HTTP"
fix: "Set SESSION_SECURE_COOKIE=true"
cwe: CWE-614
- id: session_http_only
name: HTTP-Only Cookies
description: Cookies should not be accessible via JavaScript
severity: high
key: SESSION_HTTP_ONLY
condition: "== true"
default_good: true # Laravel default is true
message: "Cookies accessible to JavaScript (XSS risk)"
cwe: CWE-1004
- id: session_same_site
name: SameSite Cookie Attribute
description: SESSION_SAME_SITE should be 'lax' or 'strict'
severity: medium
key: SESSION_SAME_SITE
condition: "in"
good_values: [lax, strict]
message: "Missing CSRF protection via SameSite attribute"
cwe: CWE-1275
- id: https_only
name: HTTPS Enforced
description: APP_URL should use HTTPS in production
severity: high
key: APP_URL
condition: "starts_with https://"
when_env: [production, prod, live]
message: "Application not enforcing HTTPS"
cwe: CWE-319
- id: mail_encryption
name: Mail Encryption Enabled
description: MAIL_ENCRYPTION should be set for secure email
severity: medium
key: MAIL_ENCRYPTION
condition: "in"
good_values: [tls, ssl, starttls]
when_key_exists: MAIL_HOST
message: "Email sent without encryption"
cwe: CWE-319
- id: db_password_set
name: Database Password Set
description: DB_PASSWORD should not be empty in production
severity: critical
key: DB_PASSWORD
condition: "exists && not_empty"
when_env: [production, prod, live]
message: "Database has no password"
cwe: CWE-521
- id: redis_password
name: Redis Password Set
description: REDIS_PASSWORD should be set if Redis is used
severity: high
key: REDIS_PASSWORD
condition: "exists && not_empty"
when_key_exists: REDIS_HOST
when_env: [production, prod, live]
message: "Redis accessible without authentication"
cwe: CWE-306
- id: log_level_production
name: Log Level Appropriate
description: LOG_LEVEL should not be 'debug' in production
severity: medium
key: LOG_LEVEL
condition: "not_in"
bad_values: [debug]
when_env: [production, prod, live]
message: "Verbose logging may expose sensitive data"
cwe: CWE-532
- id: telescope_disabled
name: Telescope Disabled in Production
description: TELESCOPE_ENABLED should be false in production
severity: high
key: TELESCOPE_ENABLED
condition: "!= true"
when_env: [production, prod, live]
message: "Telescope exposes application internals"
cwe: CWE-215
- id: debugbar_disabled
name: Debugbar Disabled in Production
description: DEBUGBAR_ENABLED should be false in production
severity: high
key: DEBUGBAR_ENABLED
condition: "!= true"
when_env: [production, prod, live]
message: "Debugbar exposes sensitive debug information"
cwe: CWE-215
# =============================================================================
# FILE SYSTEM CHECKS
# Use Go's os package - no PHP needed
# =============================================================================
filesystem_checks:
- id: env_not_public
name: .env Not Publicly Accessible
description: .env file should not be in public directory
severity: critical
check: file_not_exists
paths:
- public/.env
- public_html/.env
- www/.env
message: "Environment file exposed to web"
cwe: CWE-538
- id: env_permissions
name: .env File Permissions
description: .env should not be world-readable
severity: high
check: file_permissions
path: .env
max_mode: "0640" # rw-r----- or stricter
message: ".env file is world-readable"
cwe: CWE-732
- id: storage_permissions
name: Storage Directory Writable
description: storage/ must be writable but not world-writable
severity: medium
check: dir_permissions
path: storage
min_mode: "0755"
max_mode: "0775"
message: "Storage directory has insecure permissions"
cwe: CWE-732
- id: no_git_public
name: .git Not Publicly Accessible
description: .git directory should not be in public
severity: critical
check: dir_not_exists
paths:
- public/.git
- public_html/.git
message: "Git repository exposed to web (source code leak)"
cwe: CWE-538
- id: no_sensitive_files_public
name: No Sensitive Files in Public
description: Sensitive files should not be in public directory
severity: critical
check: files_not_exist
patterns:
- "public/*.sql"
- "public/*.sqlite"
- "public/*.log"
- "public/*.bak"
- "public/*.env*"
- "public/composer.json"
- "public/composer.lock"
- "public/package.json"
message: "Sensitive files exposed to web"
cwe: CWE-538
- id: bootstrap_cache_writable
name: Bootstrap Cache Writable
description: bootstrap/cache must be writable
severity: medium
check: dir_writable
path: bootstrap/cache
message: "Bootstrap cache not writable (deployment issues)"
# =============================================================================
# CONFIG FILE CHECKS
# Parse PHP config files with regex - no PHP runtime needed
# =============================================================================
config_checks:
- id: csrf_middleware
name: CSRF Middleware Enabled
description: VerifyCsrfToken middleware must be active
severity: critical
check: pattern_exists
files:
- app/Http/Kernel.php
- bootstrap/app.php
patterns:
- "VerifyCsrfToken"
- "ValidateCsrfToken"
message: "CSRF protection not enabled"
cwe: CWE-352
- id: auth_throttle
name: Login Throttling Enabled
description: Rate limiting should be applied to auth routes
severity: high
check: pattern_exists
files:
- routes/auth.php
- routes/web.php
- app/Http/Kernel.php
patterns:
- "throttle:"
- "RateLimiter"
- "ThrottleRequests"
message: "No rate limiting on authentication routes"
cwe: CWE-307
- id: bcrypt_or_argon
name: Strong Password Hashing
description: Password hashing should use bcrypt or argon2
severity: high
check: config_value
file: config/hashing.php
key: driver
good_values: [bcrypt, argon, argon2id]
message: "Weak password hashing algorithm"
cwe: CWE-916
- id: session_driver_secure
name: Secure Session Driver
description: Session driver should not be 'array' in production
severity: high
check: env_or_config
env_key: SESSION_DRIVER
config_file: config/session.php
config_key: driver
bad_values: [array]
when_env: [production, prod, live]
message: "Session driver 'array' loses sessions on restart"
cwe: CWE-384
# =============================================================================
# STATIC PATTERN CHECKS
# Grep/regex through source files - no PHP needed
# =============================================================================
pattern_checks:
- id: blade_unescaped
name: Unescaped Blade Output
description: "{!! !!}" can lead to XSS if used with user input
severity: high
check: pattern_warning
paths:
- "resources/views/**/*.blade.php"
pattern: '\{!!\s*\$(?!__env|app|config|errors)'
exclude_patterns:
- '\{!!\s*\$slot' # Slots are safe
- '\{!!\s*html_entity_decode' # Intentional
message: "Unescaped output may cause XSS vulnerability"
cwe: CWE-79
- id: raw_sql_input
name: Raw SQL with User Input
description: DB::raw() with user input is SQL injection risk
severity: critical
check: pattern_search
paths:
- "app/**/*.php"
- "src/**/*.php"
patterns:
- 'DB::raw\s*\(\s*["\'].*\$(?:request|_GET|_POST|input)'
- 'whereRaw\s*\(\s*["\'].*\$(?:request|_GET|_POST|input)'
- 'selectRaw\s*\(\s*["\'].*\$(?:request|_GET|_POST|input)'
message: "Possible SQL injection with raw query"
cwe: CWE-89
- id: dangerous_functions
name: No Dangerous Function Usage
description: Certain PHP functions should never be used
severity: critical
check: pattern_forbidden
paths:
- "app/**/*.php"
- "src/**/*.php"
patterns:
- '\b(create_function|assert)\s*\('
message: "Dangerous function allows arbitrary code execution"
cwe: CWE-94
- id: shell_exec_input
name: Shell Execution with User Input
description: shell_exec/exec with user input is command injection
severity: critical
check: pattern_search
paths:
- "app/**/*.php"
- "src/**/*.php"
patterns:
- '(?:shell_exec|exec|system|passthru|popen)\s*\([^)]*\$(?:request|_GET|_POST|input)'
message: "Possible command injection vulnerability"
cwe: CWE-78
- id: unserialize_usage
name: Unsafe unserialize()
description: unserialize() with user input leads to object injection
severity: critical
check: pattern_search
paths:
- "app/**/*.php"
- "src/**/*.php"
patterns:
- '\bunserialize\s*\(\s*\$(?:request|_GET|_POST|input)'
message: "Possible PHP object injection via unserialize()"
cwe: CWE-502
- id: mass_assignment_unguarded
name: Unguarded Models
description: Models should have $fillable or $guarded defined
severity: high
check: model_guard
paths:
- "app/Models/**/*.php"
- "src/**/Models/**/*.php"
must_have_one_of:
- 'protected\s+\$fillable\s*='
- 'protected\s+\$guarded\s*='
base_class: "extends Model"
message: "Model has no mass assignment protection"
cwe: CWE-915
- id: hardcoded_credentials
name: No Hardcoded Credentials
description: Passwords and secrets should not be in code
severity: critical
check: pattern_forbidden
paths:
- "app/**/*.php"
- "src/**/*.php"
- "config/**/*.php"
patterns:
- '(?:password|secret|api_key|apikey|token)\s*[=:]\s*["\'][^"\']{8,}["\']'
exclude_patterns:
- 'env\s*\(' # Using env() is fine
- 'config\s*\(' # Using config() is fine
- '@param|@var|@return' # PHPDoc
message: "Hardcoded credentials found in source code"
cwe: CWE-798
- id: debug_functions
name: No Debug Functions in Production Code
description: dd(), dump(), var_dump() should not be in production
severity: medium
check: pattern_forbidden
paths:
- "app/**/*.php"
- "src/**/*.php"
exclude_paths:
- "**/Tests/**"
- "**/test/**"
patterns:
- '\b(?:dd|dump|var_dump|print_r|var_export)\s*\('
message: "Debug function found in production code"
cwe: CWE-489
- id: error_display
name: No Direct Error Display
description: Errors should not be displayed directly to users
severity: medium
check: pattern_forbidden
paths:
- "app/**/*.php"
- "src/**/*.php"
patterns:
- 'ini_set\s*\(\s*["\']display_errors["\']\s*,\s*["\']?(?:1|on|true)'
- 'error_reporting\s*\(\s*E_ALL\s*\)'
message: "Direct error display exposes sensitive information"
cwe: CWE-209
# =============================================================================
# EXTERNAL TOOL CHECKS
# Shell out to existing tools
# =============================================================================
tool_checks:
- id: composer_audit
name: Composer Security Audit
description: Check PHP dependencies for known vulnerabilities
severity: critical
command: composer audit --format=json
success_exit_code: 0
parse: json
error_path: advisories
message: "Vulnerable PHP dependencies found"
cwe: CWE-1395
- id: npm_audit
name: NPM Security Audit
description: Check JS dependencies for known vulnerabilities
severity: high
command: npm audit --json
success_exit_code: 0
parse: json
error_path: vulnerabilities
when_file_exists: package.json
message: "Vulnerable JavaScript dependencies found"
cwe: CWE-1395
- id: phpstan_security
name: PHPStan Security Analysis
description: Run PHPStan for security-related issues
severity: high
command: ./vendor/bin/phpstan analyse --error-format=json --no-progress
success_exit_code: 0
parse: json
error_path: totals.file_errors
message: "Static analysis found potential issues"
# =============================================================================
# HEADER CHECKS (for deployed apps)
# Requires HTTP access - optional check
# =============================================================================
header_checks:
- id: hsts_header
name: HSTS Header Present
description: Strict-Transport-Security header should be set
severity: high
header: Strict-Transport-Security
condition: exists
when: url_provided
message: "Missing HSTS header (HTTPS downgrade attacks possible)"
cwe: CWE-319
- id: content_type_options
name: X-Content-Type-Options Header
description: Prevent MIME type sniffing
severity: medium
header: X-Content-Type-Options
expected: nosniff
when: url_provided
message: "Missing X-Content-Type-Options header"
cwe: CWE-693
- id: frame_options
name: X-Frame-Options Header
description: Prevent clickjacking attacks
severity: medium
header: X-Frame-Options
condition: "in"
good_values: [DENY, SAMEORIGIN]
when: url_provided
message: "Missing clickjacking protection"
cwe: CWE-1021
- id: csp_header
name: Content-Security-Policy Header
description: CSP helps prevent XSS attacks
severity: medium
header: Content-Security-Policy
condition: exists
when: url_provided
message: "Missing Content-Security-Policy header"
cwe: CWE-693
# =============================================================================
# OUTPUT FORMAT
# =============================================================================
output:
formats:
- text # Human readable (default)
- json # Machine readable
- sarif # GitHub/GitLab security format
- markdown # For PR comments
# =============================================================================
# CI INTEGRATION
# =============================================================================
ci:
# Fail CI if any of these severities found
fail_on: [critical, high]
# GitHub Actions annotation format
github_annotations: true
# GitLab code quality report
gitlab_codequality: true