关于 web-security-audit
web-security-audit 是一项外部黑盒 Web 安全审计技能,面向获授权的网站测试,依据所述 OWASP Top 10 2025 和 PTES 流程开展检查。它涵盖多种 Web 技术,并生成西班牙语 HTML 报告。
主要功能
- 01测试前确认审计授权与限制
- 02检查安全响应头、DNS 和站点路径
- 03扫描 JavaScript 中暴露的密钥
- 04按技术栈执行专项检查
- 05生成西班牙语 HTML 报告
适用场景
- 01审计获授权的网站安全配置
- 02检查前端资源是否暴露密钥
- 03排查 Supabase 或框架相关风险
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ angelapaia/ web-security-audit-skill --skill web-security-audit安装后在 Claude Code 中输入/web-security-audit确认已加载。
详细介绍
Web Security Audit — 专业黑盒渗透测试
基于 OWASP Top 10 2025、PTES 和生产环境实际渗透测试经验(Frontera、Tripath)的外部 Web 安全审计。涵盖 Next.js、React、Vue、WordPress、Django、Rails、Laravel 和任何现代 Web 技术栈。生成专业的西班牙语 HTML 报告。
背景及其重要性
现代网站的攻击面极其庞大:开放的 API、RLS 配置错误的数据库、缺失的安全标头,以及流行框架中的 CVE。具备基础知识的攻击者只需使用 curl,就能在几分钟内利用这些漏洞。此技能将专业渗透测试人员会执行的工作系统化,并以可重复、文档化的形式呈现。
执行流程
阶段 0:授权(必须)
开始任何测试前,向用户确认:
- 是否获得网站所有者授权,可以审计该 URL
- 有哪些限制(不创建账户、不触碰生产环境等)
- 报告语言(默认:西班牙语)
如果没有明确授权,则不要继续。
阶段 1:侦察
使用 Bash 并行执行以下 4 个子阶段:
1A — 安全标头:
curl -sI "https://TARGET"
标头检查清单(每个缺失项都是一个发现):
| 标头 | 预期值 | 缺失时的严重性 |
|---|---|---|
| Strict-Transport-Security | max-age>=31536000; includeSubDomains | 中 |
| Content-Security-Policy | 限制性策略,不含 unsafe-inline | 高 |
| X-Frame-Options | DENY(SAMEORIGIN = 中) | 中 |
| X-Content-Type-Options | nosniff | 低 |
| Referrer-Policy | strict-origin-when-cross-origin 或更严格 | 低 |
| Permissions-Policy | 禁用 camera、microphone、geolocation | 低 |
| X-Powered-By | 不应存在 | 高(信息泄露) |
| Server | 不应泄露确切版本 | 低 |
1B — 技术栈检测:
PAGE=$(curl -s "https://TARGET")
# Detectar frameworks
echo "$PAGE" | grep -oiE '(_next/|__next|nuxt|vue|angular|react|svelte|gatsby|remix|astro|wordpress|wp-content|wp-includes|django|laravel|rails|flask|express)' | sort -u
# Scripts externos (tracking, analytics, APIs)
echo "$PAGE" | grep -oE 'https?://[^"'\''> ]+\.js' | sort -u
# Buscar Supabase, Firebase, Stripe, etc.
echo "$PAGE" | grep -oiE '(supabase|firebase|googleapis|stripe|algolia|sentry|mixpanel|amplitude|segment|intercom|crisp|hubspot)' | sort -u
1C — DNS 和基础设施:
dig TARGET A +short
dig TARGET MX +short
dig TARGET TXT +short
dig TARGET NS +short
# Detectar CDN (Cloudflare, Vercel, etc.)
1D — robots.txt 和 sitemap:
curl -s "https://TARGET/robots.txt"
curl -s "https://TARGET/sitemap.xml" | head -50
阶段 2:搜索 JavaScript 中的机密信息
客户端 JS 包中通常包含 API 密钥、数据库 URL 和令牌。这是攻击者最先检查的内容。
# Descargar todos los chunks JS
CHUNKS=$(curl -s "https://TARGET" | grep -oE '(src="|href=")[^"]*\.js' | sed 's/src="//;s/href="//' | sort -u)
mkdir -p /tmp/audit_js
for chunk in $CHUNKS; do
fname=$(basename "$chunk" | head -c 50)
curl -s "https://TARGET${chunk}" > "/tmp/audit_js/${fname}" 2>&1
done
在下载的文件中搜索以下模式:
# Supabase / Firebase keys
grep -rhoiE 'eyJ[A-Za-z0-9_/+-]{30,}' /tmp/audit_js/ | sort -u
grep -rhoiE 'sb_publishable[A-Za-z0-9_-]+' /tmp/audit_js/
grep -rhoiE 'AIza[A-Za-z0-9_-]{35}' /tmp/audit_js/ # Firebase
grep -rhoiE 'sk_live_[A-Za-z0-9]{20,}' /tmp/audit_js/ # Stripe secret
grep -rhoiE 'pk_live_[A-Za-z0-9]{20,}' /tmp/audit_js/ # Stripe public
# URLs internas
grep -rhoiE 'https?://[a-z0-9.-]+\.(supabase\.co|firebaseio\.com|firebaseapp\.com)' /tmp/audit_js/ | sort -u
# Env vars
grep -rhoiE 'NEXT_PUBLIC_[A-Z_]+' /tmp/audit_js/ | sort -u
如果发现 Supabase JWT,则解码以获取 project ref:
echo "JWT_TOKEN" | cut -d. -f2 | python3 -c "import sys,base64,json; p=sys.stdin.read().strip(); p+='='*(-len(p)%4); print(json.dumps(json.loads(base64.urlsafe_b64decode(p)),indent=2))"
阶段 3:路由枚举
使用外部脚本,避免 shell 转义问题:
cat > /tmp/audit_routes.sh << 'SCRIPT'
#!/bin/bash
TARGET=$1
for path in /admin /login /register /signup /api /api/auth /api/auth/callback /api/users /api/admin /api/admin/users /api/rooms /api/bookings /api/payments /api/webhooks /api/stripe /api/contact /api/newsletter /dashboard /panel /perfil /profile /account /settings /checkout /booking /reserva /mis-reservas /.env /.env.local /.env.production /.git/config /.git/HEAD /graphql /swagger /api-docs /health /status /_next/data /debug /info /wp-admin /wp-login.php /wp-json /xmlrpc.php /phpmyadmin /adminer /server-status /server-info; do
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "https://${TARGET}${path}")
if [ "$code" != "404" ] && [ "$code" != "000" ]; then
echo " ${path} -> HTTP ${code}"
fi
done
SCRIPT
chmod +x /tmp/audit_routes.sh
bash /tmp/audit_routes.sh "TARGET"
对结果分类:
- 应要求身份验证却返回 200 的路由 = 高
- 可访问的管理路由 = 严重
- 可访问的 .env、.git = 严重
- 无需身份验证即可访问的 API 端点 = 高
阶段 4:Supabase 深度审计
仅在阶段 2 检测到 Supabase 时执行。定义变量:
SUPA_URL="https://PROJECT_REF.supabase.co" # o custom domain
ANON_KEY="eyJ..." # del JS
4.1 — 利用错误提示枚举表:
for tbl in users profiles public_users community_profiles rooms listings bookings reservations payments transactions properties apartments tenants landlords owners contracts messages chats notifications reviews verifications documents invoices blog_posts leads contacts waitlist settings admins; do
result=$(curl -s "${SUPA_URL}/rest/v1/${tbl}?limit=1" \
-H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}")
if echo "$result" | grep -q "PGRST"; then
hint=$(echo "$result" | grep -oE '"hint":"[^"]*"' | head -1)
[ -n "$hint" ] && echo " ${tbl}: NO EXISTE ${hint}"
elif echo "$result" | grep -q "permission denied\|row-level security"; then
echo " ${tbl}: PROTEGIDA (RLS activo) ✓"
elif echo "$result" | grep -q "restricted"; then
echo " ${tbl}: PROYECTO SUSPENDIDO/RESTRINGIDO"
elif [ "$result" = "[]" ]; then
echo " ${tbl}: ACCESIBLE (vacia)"
else
echo " ${tbl}: *** ACCESIBLE CON DATOS *** — $(echo "$result" | wc -c) bytes"
fi
done
4.2 — 统计每个可访问表中的记录数:
curl -s "${SUPA_URL}/rest/v1/TABLE?select=id" \
-H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
-H "Prefer: count=exact" -H "Range: 0-0" -I | grep -i content-range
4.3 — 写入测试(INSERT/UPDATE/DELETE):
# INSERT — usar datos dummy
curl -s -X POST "${SUPA_URL}/rest/v1/TABLE" \
-H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
-H "Content-Type: application/json" -H "Prefer: return=minimal" \
-d '{"test_field":"SECURITY_AUDIT_TEST"}'
# Si funciona = CRITICO. Intentar DELETE inmediato para limpiar.
# UPDATE — cambiar campo de un registro conocido
curl -s -X PATCH "${SUPA_URL}/rest/v1/TABLE?id=eq.KNOWN_ID" \
-H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
-H "Content-Type: application/json" -d '{"field":"AUDIT_TEST"}'
# Si funciona = CRITICO. REVERTIR inmediatamente.
4.4 — 公开的身份验证配置:
curl -s "${SUPA_URL}/auth/v1/settings" -H "apikey: ${ANON_KEY}"
检查:
disable_signup: false+ 平台已关闭 = 高(意外开放注册)mailer_autoconfirm: true= 高(账户未经电子邮件验证)- 意外的 OAuth 提供商 = 中
4.5 — 开放注册测试:
curl -s -X POST "${SUPA_URL}/auth/v1/signup" \
-H "apikey: ${ANON_KEY}" -H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"AuditTest2026!"}'
如果返回包含 UUID 的用户对象 = 高(任何人都可以创建账户)
4.6 — Storage 存储桶:
curl -s "${SUPA_URL}/storage/v1/bucket" -H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}"
# Para cada bucket encontrado, intentar listar archivos:
curl -s "${SUPA_URL}/storage/v1/object/list/BUCKET_NAME" \
-H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
-H "Content-Type: application/json" -d '{"prefix":"","limit":20}'
4.7 — RPC 函数:
for fn in get_users get_all_users admin_get_users export_users get_user_emails list_users get_members get_subscribers get_leaderboard get_user_tier; do
result=$(curl -s -X POST "${SUPA_URL}/rest/v1/rpc/${fn}" \
-H "apikey: ${ANON_KEY}" -H "Authorization: Bearer ${ANON_KEY}" \
-H "Content-Type: application/json" -d '{}')
echo " rpc/${fn}: $(echo "$result" | head -c 150)"
done
阶段 5:Next.js 专项检查
仅在检测到 Next.js 时执行。
5.1 — Middleware 绕过 CVE-2025-29927(严重,CVSS 9.1):
# Comparar respuesta normal vs con header de bypass
NORMAL=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET/admin")
BYPASS=$(curl -s -o /dev/null -w "%{http_code}" -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" "https://TARGET/admin")
echo "Normal: $NORMAL | Bypass: $BYPASS"
# Si NORMAL=307/401 pero BYPASS=200 → CRITICO (CVE confirmada)
# Si ambos son 200 → la ruta no tiene middleware (problema diferente)
测试以下变体:middleware、src/middleware、pages/_middleware
5.2 — RSC 数据泄露:
RSC=$(curl -s "https://TARGET" -H "RSC: 1" -H "Next-Router-State-Tree: %5B%22%22%5D")
# Buscar datos sensibles en el payload
echo "$RSC" | grep -oiE '(email|password|secret|token|apikey|phone|dni|iban|credit|ssn)' | sort -u
# Probar en rutas protegidas tambien:
curl -s "https://TARGET/dashboard" -H "RSC: 1" -H "Next-Router-State-Tree: %5B%22%22%2C%7B%22children%22%3A%5B%22dashboard%22%2C%7B%7D%5D%7D%5D"
5.3 — Server Actions:
curl -s -X POST "https://TARGET" -H "Next-Action: 0000000000" -H "Content-Type: text/plain" -d '[]'
# "Server action not found" = endpoint existe (info disclosure menor)
5.4 — Source Maps:
for chunk in $(curl -s "https://TARGET" | grep -oE '/_next/static/chunks/[^"]+\.js' | sort -u); do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${chunk}.map")
[ "$code" = "200" ] && echo " SOURCE MAP EXPUESTO: ${chunk}.map"
done
Source map 可访问 = 中(暴露原始源代码)
阶段 6:WordPress 专项检查
仅在检测到 WordPress 时执行。
# Version de WP
curl -s "https://TARGET" | grep -oE 'content="WordPress [0-9.]+"'
curl -s "https://TARGET/wp-json/" | head -c 200
# xmlrpc activo (bruteforce vector)
curl -s -X POST "https://TARGET/xmlrpc.php" -d '<?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName></methodCall>'
# Users enumeration
curl -s "https://TARGET/wp-json/wp/v2/users"
curl -s "https://TARGET/?author=1" -o /dev/null -w "%{redirect_url}"
# Login bruteforce protection
curl -s -X POST "https://TARGET/wp-login.php" -d "log=admin&pwd=wrongpass"
阶段 7:CORS
# Reflected origin (CRITICO si refleja con credentials)
curl -sI "https://TARGET" -H "Origin: https://evil-attacker.com" | grep -i "access-control"
# Null origin (exploitable via sandboxed iframe)
curl -sI "https://TARGET" -H "Origin: null" | grep -i "access-control"
# Subdomain wildcard
curl -sI "https://TARGET" -H "Origin: https://anything.TARGET" | grep -i "access-control"
# Prefix/suffix bypass
curl -sI "https://TARGET" -H "Origin: https://TARGET.evil.com" | grep -i "access-control"
如果有 API(Supabase 等),也要测试这些端点的 CORS:
- API 中的
access-control-allow-origin: *= 高 - Origin 被反射 +
Access-Control-Allow-Credentials: true= 严重
阶段 8:身份验证与会话
# Cookies
curl -sI "https://TARGET" | grep -i "set-cookie"
# Cada cookie debe tener: Secure, HttpOnly, SameSite=Strict|Lax
# Open redirect
for param in redirect url next return_to redirect_uri callback goto dest; do
loc=$(curl -s -o /dev/null -w "%{redirect_url}" "https://TARGET/login?${param}=https://evil.com")
[ -n "$loc" ] && echo "OPEN REDIRECT: ${param} -> ${loc}"
done
# Password reset host injection
curl -s -X POST "https://TARGET/api/auth/reset-password" \
-H "Host: evil.com" -H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'
阶段 9:HTTP 方法与错误处理
# Metodos HTTP peligrosos
for method in OPTIONS TRACE PUT DELETE PATCH; do
code=$(curl -s -o /dev/null -w "%{http_code}" -X "$method" "https://TARGET/")
echo " ${method}: HTTP ${code}"
done
# TRACE 200 = MEDIO, PUT/DELETE 200 inesperado = ALTO
# Error handling — provocar errores y buscar info leaks
curl -s "https://TARGET/api/users/undefined"
curl -s -X POST "https://TARGET/api/contact" -H "Content-Type: application/json" -d '{"invalid":'
# Stack traces, paths internos, versiones = MEDIO
阶段 10:速率限制
# Test basico — 20 peticiones rapidas
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code} " "https://TARGET/"
done
echo ""
# Si todos 200 = sin rate limiting = ALTO
# Bypass via X-Forwarded-For
for i in $(seq 1 10); do
curl -s -o /dev/null -w "%{http_code} " -H "X-Forwarded-For: 10.0.0.${i}" "https://TARGET/api/users"
done
阶段 11:绕过 Cloudflare/CDN
仅当检测到 Cloudflare 或其他 CDN 时。
# Buscar IP real via registros MX
dig MX TARGET +short
# Subdominios no proxied
for sub in mail ftp cpanel direct api staging dev test; do
ip=$(dig +short ${sub}.TARGET 2>/dev/null)
[ -n "$ip" ] && echo " ${sub}.TARGET: ${ip}"
done
# Certificate Transparency logs
curl -s "https://crt.sh/?q=%25.TARGET&output=json" | python3 -c "import sys,json; [print(x['name_value']) for x in json.load(sys.stdin)]" 2>/dev/null | sort -u
阶段 12:敏感文件
cat > /tmp/audit_files.sh << 'SCRIPT'
#!/bin/bash
TARGET=$1
for path in .env .env.local .env.production .env.development .git/config .git/HEAD .DS_Store wp-config.php server-status server-info debug info package.json composer.json Dockerfile docker-compose.yml .dockerignore backup.sql dump.sql database.sql db.sql; do
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "https://${TARGET}/${path}")
if [ "$code" != "404" ] && [ "$code" != "000" ]; then
echo " ${path}: HTTP ${code}"
fi
done
SCRIPT
bash /tmp/audit_files.sh "TARGET"
生成 HTML 报告
完成所有阶段后,按以下格式生成 HTML 文件。HTML 必须使用纯 CSS,不含 JavaScript(兼容 WhatsApp/Telegram),并采用深色主题。
报告结构:
- 页眉 — 日期、目标(URL)、总体严重程度、审计人员姓名
- 评分卡 — 4 张卡片:严重、高危、中危、正常(带计数)
- 执行摘要 — 用非技术语言写 2-3 句话,背景为红色渐变
- 类比说明 — 用蓝色区块解释发现的问题,把网站比作建筑、酒店等。根据业务类型调整类比
- 技术信息 — 包含技术栈、域名、DB、CDN、发现的密钥的表格
- 严重漏洞 — 带红色边框(#ff4444)的卡片,每项包含:描述性标题、通俗易懂的说明、
<pre>区块中的概念验证、建议的修复方案 - 高危漏洞 — 带橙色边框(#ff8c00)的卡片,结构相同
- 中危漏洞 — 带黄色边框(#f5a623)的卡片
- 做得好的方面 — 表格背景为绿色(#00c853),用于显示通过的项目
- 渗透测试结果 — 摘要表格:攻击 | 结果 | 严重程度(徽标)
- 复现命令 — 可展开的
<details><summary>区块 - 行动计划 — 带彩色圆点的时间线:红色=立即,橙色=今天,黄色=本周,灰色=上线前
- 法律影响 — 适用时说明 RGPD、LOPD、ePrivacy。根据目标所在国家/地区调整
- 页脚 — “机密”、审计人员、日期
基础 CSS 样式:
- 背景:
#0a0a0a,文字:#e0e0e0 - 严重:
#ff4444,高危:#ff8c00,中危:#f5a623,正常:#00c853 - Code/pre:字体
SF Mono, Fira Code, monospace,背景#1a1a1a - 最大宽度:900px,适配移动设备
- 徽标使用 border-radius 20px,颜色根据严重程度设置
保存到:
- 如果有客户项目:
~/Desktop/Empresa/SolutechIA/clientes/[CLIENTE]/ - 如果没有:用户当前目录
- 文件名:
auditoria-seguridad-[dominio]-[YYYY-MM-DD].html
严重程度表
| 发现的问题 | 严重程度 |
|---|---|
| BBDD 未经身份验证即可访问(RLS 已禁用) | 严重 |
| 修改/删除其他用户的数据 | 严重 |
| 付费内容可公开访问 | 严重 |
| 中间件绕过(CVE-2025-29927) | 严重 |
| JavaScript 中暴露 Service role key | 严重 |
| 已确认的 SQL 注入 | 严重 |
.env 或 .git 可公开访问 | 严重 |
| 意外开放注册 | 高危 |
| 无需身份验证即可访问受保护路由(/admin、/dashboard) | 高危 |
| 缺少 Content-Security-Policy | 高危 |
| 缺少速率限制 | 高危 |
| CORS 反射 Origin + credentials | 高危 |
| 暴露 x-powered-by / Server 版本 | 高危 |
| 身份验证配置公开(settings endpoint) | 高危 |
| xmlrpc.php 已启用(WordPress) | 高危 |
| 通过 REST API 枚举 WP 用户 | 高危 |
| CORS 通配符且无 credentials | 中危 |
| 使用 X-Frame-Options SAMEORIGIN 而非 DENY | 中危 |
| 未征得 Cookie 同意即进行跟踪(Facebook Pixel 等) | 中危 |
| Storage bucket 公开且文件可列出 | 中危 |
| 暴露 Source map(可访问 .js.map) | 中危 |
| 错误处理泄露内部信息(stack trace) | 中危 |
| 缺少 HSTS | 中危 |
| 发现 Server Action endpoint | 中危 |
| 意外路由中的 PUT/DELETE 返回 200 | 中危 |
| Server header 泄露确切版本 | 低危 |
| 来自外部服务的占位图片 | 低危 |
审计道德准则
制定这些准则,是因为如果安全审计操作不当,可能会造成实际损害。遵守这些准则,既能保护审计人员,也能保护网站所有者。
- 仅进行最小化概念验证——确认漏洞存在即可,不要进一步利用
- 立即撤销审计过程中所做的任何修改(如果 UPDATE 成功,则恢复原始值)
- 不创建真实账户——使用 @nonexistent-domain.invalid 邮箱
- 不外传或存储用户的真实个人数据
- 不执行拒绝服务攻击,也不进行激进的负载测试
- 记录每个步骤,确保可复现
- 如果发现严重问题,立即通知所有者
- 报告属于机密,仅供获授权的所有者查看
- 完成后清理临时文件:
rm -rf /tmp/audit_*