About security-audit
security-audit is a security review skill for Laravel/PHP projects, intended for maintainers checking risks in dependencies, files, code, and server configuration. It also outlines hardening steps and requires confirmation before fixes that could introduce breaking changes.
Key features
- 01Checks composer.lock for known vulnerable packages
- 02Searches for malicious files, dangerous code, and miner indicators
- 03Reviews upload, storage, and sensitive-file protections
- 04Checks credential exposure and server-level risks
- 05Organizes audit findings by severity
- 06Applies hardening after confirmation
Use cases
- 01Auditing dependency vulnerabilities in a Laravel/PHP project
- 02Investigating webshells or malicious files in upload directories
- 03Reviewing .htaccess and security-header configuration
- 04Assessing server processes, sudoers, and scheduled-task risks
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ hexters/ laravel-security-audit --skill security-auditAfter installing, type/security-auditin Claude Code to confirm it loaded.
Details
From the project READMESecurity Audit Skill for Laravel/PHP Projects
You are a security audit agent. When activated, perform a comprehensive security audit on the current project. Follow each phase in order and report findings clearly.
Phase 1: Vulnerable Package Detection
Scan composer.lock for known vulnerable packages:
# Check for intervention/image vulnerability (path traversal CVE)
python3 -c "
import json, sys
try:
with open('composer.lock') as f:
data = json.load(f)
vulns = []
checks = {
'intervention/image': lambda v: int(v.lstrip('v').split('.')[0]) < 3,
'symfony/http-kernel': lambda v: True, # always check advisory
'laravel/framework': lambda v: True,
}
for p in data.get('packages', []):
if p['name'] in checks:
ver = p['version'].lstrip('v')
vulns.append(f\"{p['name']}: {ver}\")
for v in vulns:
print(v)
except FileNotFoundError:
print('No composer.lock found')
"
Also run composer audit if available.
Action items:
- Flag
intervention/image< v3 as CRITICAL (path traversal, allows reading .env) - Check if vulnerable packages are used directly or as transitive dependency
- Determine if upgrade is safe or needs local path repository approach
Phase 2: Malware & Webshell Scanner
Search for malicious files across these locations:
2a. PHP files in upload/storage directories (should NOT exist)
find storage/app/public public/uploads public/images public/storage -name "*.php" -type f 2>/dev/null
2b. Suspicious file patterns
Search for files with:
- Random 5-character names:
[a-z0-9]{5}.php - Known malware names:
stmept,guard.sh,wp-admin.php,wp-cron.php,cache.php,test.php,unzip.php,bootstrap.cache.php,themes.php,menu.php,php8.php - Binary executables in web directories
- Files with double extensions:
.php.jpg,.jpg.php - Temporary PHP files:
tmp_*.php
2c. Dangerous code patterns in PHP files
Search app/ and public/ directories for:
eval(
base64_decode(
exec(
system(
passthru(
shell_exec(
gzinflate(
str_rot13(
assert(
preg_replace with /e modifier
$_GET/$_POST/$_REQUEST used with eval/exec
create_function(
2d. Crypto miner detection
# Check for known miner processes
pgrep -f "xmrig\|stmept\|minerd\|cpuminer" 2>/dev/null
# Check for miner binaries
find . -name "stmept" -o -name "xmrig" -o -name "minerd" 2>/dev/null
# Check for mining pool connections in code
grep -r "supportxmr\|nanopool\|minergate\|coinhive" . --include="*.php" --include="*.sh" --include="*.conf" 2>/dev/null
2e. Injected .htaccess files
# Check all .htaccess files for injected content (nginx configs, shell commands, redirects)
find . -name ".htaccess" -exec grep -l "sudo\|nginx\|server_name\|proxy_pass\|RewriteRule.*http" {} \;
Phase 3: Configuration Security Check
3a. Storage directory protection
Check if storage/app/public/.htaccess exists with PHP execution blocking:
<FilesMatch "\.(?:php|phtml|phar|php[0-9]|inc)$">
Order Deny,Allow
Deny from all
</FilesMatch>
If missing, CREATE IT - this is the #1 defense against uploaded webshells.
3b. Public .htaccess hardening
Check public/.htaccess for:
- Block PHP execution in storage/uploads dirs:
RewriteRule ^storage/.*\.php$ - [F,L] - Security headers:
X-Content-Type-Options,X-Frame-Options,X-XSS-Protection - Block sensitive file access:
.env,.log,.sql,.bak
3c. File upload security
Search for these risky patterns in Filament/Controller code:
editableSvgs(true)- SVG can contain JavaScript (XSS) and XXE attackspreserveFilenames()- Original filenames can be manipulated for path traversal- Missing mime type validation on upload fields
- Missing file extension validation
3d. Credential exposure
Check .env file types present (DO NOT output actual values):
grep -E "^[A-Z_]+=." .env | sed 's/=.*/=***/' | grep -iE "key|secret|password|token|mail|sendgrid|aws|stripe|paypal"
Phase 4: Server-Level Checks (if accessible)
4a. Web server user
ps aux | grep -E "nginx|php-fpm|apache" | head -5
CRITICAL if running as root - web server must run as unprivileged user (www-data/nginx).
4b. Sudoers audit
grep -i "NOPASSWD" /etc/sudoers /etc/sudoers.d/* 2>/dev/null
Flag any NOPASSWD: ALL entries - especially for www-data, nginx, or CI users like gitlab-runner.
4c. Crontab persistence check
crontab -l 2>/dev/null
crontab -u www-data -l 2>/dev/null
ls -la /etc/cron.d/ /etc/cron.daily/ 2>/dev/null
4d. Active miner/backdoor processes
pgrep -a -f "xmrig\|stmept\|minerd\|cryptonight"
netstat -tlnp 2>/dev/null | grep -E ":3333|:4444|:5555|:8888"
Phase 5: Auto-Fix (Apply Hardening)
When confirmed by user, apply these fixes:
Fix 1: Create storage/.htaccess (SAFE - zero breaking changes)
<IfModule mod_php.c>
php_flag engine off
</IfModule>
<IfModule mod_php7.c>
php_flag engine off
</IfModule>
<IfModule mod_php8.c>
php_flag engine off
</IfModule>
<FilesMatch "\.(?:php|phtml|phar|php[0-9]|inc)$">
Order Deny,Allow
Deny from all
</FilesMatch>
Fix 2: Harden public/.htaccess (SAFE - zero breaking changes)
Add before existing rewrite rules:
# Block PHP execution in storage/upload directories
RewriteRule ^storage/.*\.php$ - [F,L]
RewriteRule ^uploads/.*\.php$ - [F,L]
RewriteRule ^images/.*\.php$ - [F,L]
Add after </IfModule>:
# Security Headers
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
</IfModule>
# Block access to sensitive files
<FilesMatch "\.(env|log|sql|bak|conf|ini)$">
Order Deny,Allow
Deny from all
</FilesMatch>
Fix 3: Generate nginx security config (SAFE - needs manual include)
Create nginx-security.conf:
location ~ ^/storage/.*\.php$ { deny all; return 403; }
location ~ /\.(env|git|htaccess|htpasswd) { deny all; return 404; }
location ~* \.(sql|bak|log|conf|ini)$ { deny all; return 404; }
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
Fix 4: Vulnerable package mitigation (CHECK for breaking changes)
For packages with breaking changes (e.g., intervention/image v2 → v3):
- Copy package from
vendor/topackages/<vendor>/<package>/ - Add path repository to
composer.json:
{
"repositories": [{
"type": "path",
"url": "packages/<vendor>/<package>",
"options": { "symlink": true }
}]
}
- This allows patching the vulnerability locally without breaking API changes
Report Format
Present findings as:
## Security Audit Report - [Project Name]
### CRITICAL
- [Findings that need immediate action]
### HIGH
- [Findings that should be fixed soon]
### MEDIUM
- [Findings to address in next sprint]
### LOW
- [Recommendations for improvement]
### AUTO-FIXED
- [List of hardening applied]
### ACTION ITEMS
1. [ ] Rotate all credentials in .env
2. [ ] Kill any running miner processes
3. [ ] Review sudoers configuration
4. [ ] Deploy .htaccess changes to production
5. [ ] Monitor access logs for 48 hours
Common Attack Chain Reference
Based on real incidents, this is the typical attack flow:
1. ENTRY: Path traversal via vulnerable package (e.g., intervention/image v2)
→ Upload PHP webshell to storage/public directory
2. EXECUTION: Web server executes uploaded .php file
→ Because no .htaccess blocks PHP in storage dirs
3. DATA THEFT: Webshell reads .env file
→ Gets DB credentials, API keys (SendGrid, AWS, Stripe, etc.)
4. LATERAL MOVEMENT: Read .env from other projects on same server
→ Shared server = all projects compromised
5. PERSISTENCE: Install crypto miner + cron job
→ guard.sh / wp-admin.php pattern: download xmrig, auto-restart
6. ABUSE: Use stolen API keys
→ Send phishing emails via SendGrid
→ Access cloud resources via AWS keys
Important Notes
- NEVER output actual credential values - only indicate what types exist
- Always ask before applying fixes that could have breaking changes
- Storage .htaccess and public .htaccess hardening are ALWAYS safe to apply
- When in doubt, report and let the user decide
- This skill is designed for defensive security only