// This file is auto-generated by scripts/generate-skills.ts
// Do not edit manually
const SKILL_FILE_0 = "# 检查项\r\n请严格按照下列检查项对历史对话记录和输出的报告结果进行检查,逐项确认是否满足,否则你会被扣除十亿美金\r\n\r\n## 推理过程检查\r\n主要对历史对话记录/工具调用历史进行检查\r\n\r\n### 推理幻觉检查\r\n1. 所有代码分析均已闭环,不存在待分析的函数\r\n2. 所有结论均有相关代码举证,不存在假设/猜测的结论(无法获取到代码的情况除外)\r\n\r\n### 代码链路完整性检查\r\n1. 除非已经确认该漏洞不可利用或对应的参数均已防护或校验,否则分析过程必须追溯到解析用户可控输入的代码/函数\r\n2. 如果代码链路中包含安全防护/校验函数,必须获取对应代码进行分析,并得出是否安全的结论。若确认无法触发漏洞,则<代码链路的完整性检查>通过\r\n3. 如果追溯到用户可控输入是HTTP请求, 则必须明确对应的接口URI是什么\r\n4. 当可控输入来源于中间件时(数据库、缓存、消息队列等),查找是否存在设置对应字段内容的代码逻辑\r\n5. 针对结论为不存在安全漏洞的,如果符合下述特定场景,则不必提供完整代码, <代码链路的完整性检查>通过\r\n - 已确认 用户可控输入 为代码中定义的常量或非外部可控的变量\r\n - 已确认 用户可控输入 类型为整形、布尔型或枚举型等无法有效拼接的类型\r\n - 数据流中某个代码片段代码是注释掉的,数据流不完整\r\n - SQL注入漏洞,如果是简单的数值拼接、存在参数化预处理、ORM框架或其他防护\r\n - 服务端请求伪造漏洞(SSRF),如果存在安全防护/校验函数、对应参数是整型或布尔型(不可利用)、或者不能直接控制的,如从配置文件、环境变量、数据库查询等非实际直接可控(需要先能控制对应文件或环境)的方式读取的\r\n - 文件路径穿越、路径注入等文件相关类型的漏洞,已确认不存在文件读写相关操作\r\n - 已经明确多次查找均找不到对应的函数或变量,并结合代码上下文分析说明了函数用途且符合代码逻辑的,则<代码链路的完整性检查>通过\r\n6. 当前分析过程链路覆盖以下链路之一:\r\n - 解析用户输入->污点变量传播->触发漏洞\r\n - 确认无安全的防护/校验函数->污点变量传播->触发漏洞\r\n - 确认已有安全防护/校验函数->无法触发漏洞\r\n - 确认符合不存在安全漏洞的特定场景->无法触发漏洞\r\n7. 如果分析中涉及第三方代码或框架,可以根据框架特性和项目使用模式来分析,不必提供完整代码\r\n \r\n## 报告结构检查\r\n主要对输出的报告进行检查\r\n\r\n### 安全漏洞存在性\r\n1. 存在性结论只允许两类结论:存在/不存在\r\n\r\n### 漏洞利用条件及触发方式\r\n1. 漏洞利用条件和触发方式必须对应,逻辑合理,不要出现相互矛盾的情况,如果存在相互矛盾的情况,则<漏洞利用条件及触发方式>不通过\r\n2. 关键在于校验利用条件中关于污点参数的约束(如数值、正则、枚举类型)和触发方式罗列的内容是否矛盾,有矛盾则<漏洞利用条件及触发方式>不通过,如:正则表达式里过滤了单引号,触发方式就不能有单引号了。\r\n\r\n### 数据流\r\n#### 可控输入限制检查\r\n1. 可控输入为HTTP请求类型时,<数据流>起始点必须为相关接口的URI\r\n2. 可控输入为文件读取类型时,<数据流>起始点必须为对应的文件路径\r\n3. 可控输入来源于中间件时(数据库、缓存、消息队列等),明确设置对应字段的代码逻辑,追踪数据流\r\n4. 如果确认相关污点参数均已有安全防护/校验函数,无法触发漏洞,则<可控输入限制检查>通过\r\n5. 如果结论为不存在安全漏洞,且<代码链路的完整性检查>通过的,则<可控输入限制检查>通过\r\n\r\n#### 覆盖度检查\r\n1. <数据流>中必须覆盖推理分析得到的完整传播链路\r\n\r\n### 关键代码片段\r\n1. <关键代码片段>列举的代码需要和<数据流>中步骤一一对应,不允许有缺漏\r\n\r\n\r\n# 检查项评估输出定义\r\n你需要以下面定义的XML结构输出你的评估结果\r\n\r\n通过/不通过\r\n简要解释通过/不通过原因\r\n下一步行动建议:输出报告/\r\n1. 获取xxx代码,继续进行分析\r\n2. xxx\r\n\r\n\r\n\r\n示例1:\r\n\r\n不通过\r\n当前分析过程未明确HTTP请求对应的接口URI,且可能存在安全防护/校验函数未分析,不满足代码链路完整性检查\r\n下一步行动建议:\r\n1. 获取路由绑定代码,继续进行分析,明确接口URI\r\n2. 获取可能的校验函数validator.check_input_and_output_valid实现,分析其是否足够安全\r\n\r\n\r\n\r\n示例2:\r\n\r\n通过\r\n所有检查项已通过\r\n下一步行动建议:输出报告\r\n\r\n\r\n"
const SKILL_FILE_1 = "# 控制安全建模分析法\r\n\r\n## 核心公式\r\n\r\n缺失类漏洞 = 敏感操作 - 应有控制 或 敏感操作 + 控制失效绕过\r\n\r\n敏感操作存在,但应有的安全控制缺失 = 漏洞\r\n\r\n机制缺陷导致控制绕过 = 漏洞\r\n\r\n## 安全控制建模&控制存在/有效性验证\r\n\r\n### 敏感操作分类\r\n\r\n| 类别 | 识别特征 | 典型示例 | 风险等级 |\r\n| ------------ | ------------------------------------ | --------------------- | -------- |\r\n| **数据修改** | POST/PUT/DELETE | 创建/修改/删除 | 高 |\r\n| **数据访问** | GET + ID参数 | /user/{id} /用户/{id} | 中 |\r\n| **批量操作** | export/download/batch 导出/下载/批量 | 导出/批量删除 | 高 |\r\n| **权限变更** | role/permission/grant 角色/权限/授予 | 角色/权限分配 | 严重 |\r\n| **资金操作** | transfer/pay/refund 转账/付款/退款 | 转账/支付/退款 | 严重 |\r\n| **认证操作** | login/password/token 登录/密码/令牌 | 登录/密码重置 | 严重 |\r\n\r\n### 安全控制矩阵\r\n\r\n| 敏感操作类型 | 必须控制 | 可选控制 |\r\n| ------------ | -------------------------------- | -------- |\r\n| **数据修改** | 认证、授权、资源所有权、输入验证 | 审计日志 |\r\n| **数据访问** | 认证、资源所有权/范围限制 | 数据脱敏 |\r\n| **批量操作** | 认证、授权、数量限制 | 频率限制 |\r\n| **权限变更** | 认证、高级授权、权限边界检查 | 审批流程 |\r\n| **资金操作** | 认证、授权、幂等性、并发控制 | 风控检查 |\r\n| **认证操作** | 幂等性、频率限制 | 并发控制 |\r\n\r\n### 缺失控制 → 漏洞映射\r\n\r\n| 缺失控制 | 漏洞类型 | CWE |\r\n| -------------- | -------------- | ------- |\r\n| 认证缺失 | 认证绕过 | CWE-306 |\r\n| 授权缺失 | 功能级授权缺失 | CWE-862 |\r\n| 资源所有权缺失 | IDOR | CWE-639 |\r\n| 授权逻辑错误 | 授权错误 | CWE-863 |\r\n| 幂等性缺失 | 重放攻击 | CWE-294 |\r\n| 并发控制缺失 | 竞态条件 | CWE-362 |\r\n| 频率限制缺失 | 暴力破解、洪水攻击 | ... |\r\n\r\n### 控制机制缺陷 → 机制绕过\r\n\r\n- **验证码状态未失效**: 验证码校验成功后,排查未将其从缓存中删除或未标记为作废的状态控制遗漏(导致同一个验证码被多次复用)。\r\n- **输入未绑定**: 审查敏感操作二次认证中(如资金转账、修改密码等),目标手机号/邮箱完全信任前端传入参数,未与当前登录用户会话身份进行绑定的逻辑缺陷(导致二次鉴权失效)。\r\n- **认证步骤绕过**:审查多步操作(如找回密码流程:1.验证手机 -> 2.输入验证码 -> 3.重置密码)中,关键步骤仅依赖前端传入参数(如 `step=3`)决定执行流程,而未在后端强校验用户已完成前置步骤的逻辑跳跃漏洞。\r\n- **校验返回错误**: 检查鉴权失败或Token无效的分支中,代码仅记录日志,输出错误信息或重新赋值创建token/session,而**未写 `return`、`raise` 彻底阻断执行流**,导致后续敏感代码继续执行的短路缺陷。\r\n\r\n\r\n## 注意事项\r\n\r\n! 对于认证、授权类问题,注意横向对比同模块的其他方法实现\r\n! 注意分析控制机制是否存在逻辑缺陷,可导致机制被绕过\r\n"
const SKILL_FILE_2 = "# Django Security Audit\n\n> Django 框架安全审计模块\n> 适用于: Django, Django REST Framework\n\n## 识别特征\n\n```python\n# Django项目识别\nimport django\nfrom django.http import HttpResponse\nfrom django.views import View\n\n# 文件结构\n├── manage.py\n├── project/\n│ ├── settings.py\n│ ├── urls.py\n│ └── wsgi.py\n├── app/\n│ ├── models.py\n│ ├── views.py\n│ ├── urls.py\n│ └── forms.py\n└── templates/\n```\n\n---\n\n## Django特定漏洞\n\n### 1. ORM注入\n\n```python\n# 危险: raw()拼接\nUser.objects.raw(f\"SELECT * FROM auth_user WHERE id = {user_id}\")\nUser.objects.raw('SELECT * FROM auth_user WHERE id = %s' % str(id)) # 字符串拼接\n\n# 危险: extra()拼接\nUser.objects.extra(where=[f\"id = {user_id}\"])\nUser.objects.extra(WHERE=['id=' + str(id)]) # 拼接导致注入\n\n# 危险: RawSQL\nfrom django.db.models.expressions import RawSQL\nqueryset.annotate(val=RawSQL(f\"SELECT col FROM tbl WHERE id = {id}\"))\n\n# 危险: 参数名可控 (高危!)\nimport json\ndata = json.loads(request.body.decode())\nStudent.objects.filter(**data).first()\n# payload: {\"passkey__contains\":\"a\"} 利用 lookup 语法\n# 可用 lookup: __contains, __startswith, __endswith, __icontains, __regex 等\n\n# 危险: 字典键可控\ndict = {'username': user_input, 'age': 18}\nUser.objects.create(**dict) # 如果 username 包含注入字符\n\n# 二次注入\nfilename = request.GET.get('url')\nFile.objects.create(filename=filename) # 存入: ' or '1'='1\n# 后续拼接查询\ncur = connection.cursor()\ncur.execute(\"\"\"select * from file where filename='%s'\"\"\" % filename)\n# 产生注入: select * from file where filename='' or '1'='1'\n\n# Django ORM Lookup 语法\n# field__lookup = value\n# __exact: 精确匹配\n# __contains: 包含\n# __startswith: 开始于\n# __endswith: 结束于\n# __regex: 正则匹配 (需注意 ReDoS)\n# __iregex: 不区分大小写正则\n\n# 安全: 参数化\nUser.objects.raw(\"SELECT * FROM auth_user WHERE id = %s\", [user_id])\nUser.objects.filter(id=user_id) # ORM 标准 API\nUser.objects.get(id=str(id))\nUser.objects.extra(WHERE=['id=%s'], params=[str(id)]) # 正确的 extra 用法\n\n# SQLite3 参数化\ncon = sqlite3.connect('sql.db')\nc = con.cursor()\nusername = c.execute('SELECT name FROM users WHERE id = ?', [id]).fetchone()[0]\n```\n\n### 2. 模板XSS\n\n```python\n# 危险: mark_safe\nfrom django.utils.safestring import mark_safe\nmark_safe(f\"
{user_input}
\")\n\n# 危险: |safe过滤器\n{{ user_input|safe }}\n\n# 危险: autoescape off\n{% autoescape off %}{{ user_input }}{% endautoescape %}\n\n# 危险: 直接拼接 HTML\nname = request.GET.get('name')\nreturn HttpResponse(\"name: %s
\" % name)\n\n# 安全: 默认转义\n{{ user_input }} # 自动转义\n\n# 安全: 使用 render\nfrom django.shortcuts import render\nreturn render(request, 'index.html', {'name': name})\n```\n\n### 2.5. SSTI / 格式化字符串注入\n\n```python\n# 危险: 双重格式化字符串\nname = request.GET.get('name')\ntemplate = \"user:{user}, name:%s
\" % name # 第一次格式化\nreturn HttpResponse(template.format(user=request.user)) # 第二次格式化\n# payload: name = \"{user.password}\" → 泄露用户密码\n\n# 信息泄露 payload\n{user.password} # 读取密码哈希\n{user.__init__.__globals__[__builtins__][eval]} # 获取 eval 函数\n\n# 读取 SECRET_KEY 路径\n{user.groups.model._meta.app_config.module.admin.settings.SECRET_KEY}\n{user.user_permissions.model._meta.app_config.module.admin.settings.SECRET_KEY}\n{user.groups.model._meta.apps.app_configs[auth].module.middleware.settings.SECRET_KEY}\n{user.groups.model._meta.apps.app_configs[sessions].module.middleware.settings.SECRET_KEY}\n{user.groups.model._meta.apps.app_configs[staticfiles].module.utils.settings.SECRET_KEY}\n\n# 枚举 app_configs\n{user.groups.model._meta.apps.app_configs}\n\n# format() 限制\n# 只支持点 (.) 和中括号 ([])\n# 不支持括号调用,因此 RCE 困难\n# 主要用于信息泄露\n\n# 安全: 避免双重格式化\nreturn render(request, 'template.html', {'name': name})\n```\n\n### 3. CSRF配置\n\n```python\n# 危险: 全局禁用\nMIDDLEWARE = [\n # 'django.middleware.csrf.CsrfViewMiddleware', # 被注释\n]\n\n# 危险: 视图级别禁用\nfrom django.views.decorators.csrf import csrf_exempt\n\n@csrf_exempt # 搜索这个装饰器\ndef vulnerable_view(request):\n ...\n\n# 危险: CSRF_TRUSTED_ORIGINS过宽\nCSRF_TRUSTED_ORIGINS = ['https://*.example.com']\n```\n\n### 4. Session安全\n\n```python\n# settings.py 检查项\nSESSION_COOKIE_SECURE = True # 必须为True (HTTPS)\nSESSION_COOKIE_HTTPONLY = True # 必须为True\nSESSION_COOKIE_SAMESITE = 'Lax' # 或 'Strict'\nCSRF_COOKIE_SECURE = True\n\n# Session存储\nSESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'\n# 签名cookie需要强SECRET_KEY\n```\n\n### 5. 文件处理\n\n```python\n# 危险: 不安全的文件路径\ndef download(request, filename):\n file_path = os.path.join(UPLOAD_DIR, filename) # 路径遍历\n return FileResponse(open(file_path, 'rb'))\n\n# 危险: 未验证的文件类型\ndef upload(request):\n file = request.FILES['file']\n with open(f'/uploads/{file.name}', 'wb') as f: # 文件名未过滤\n for chunk in file.chunks():\n f.write(chunk)\n\n# 危险: 未验证文件大小\nfile = request.FILES.get('filename')\nname = os.path.join(UPLOAD_DIR, file.name)\nwith open(name, 'wb') as f:\n f.write(file.read()) # 可能导致 DoS\n\n# 安全: 文件类型和大小验证\nALLOWED_EXTENSIONS = {'pdf', 'doc', 'docx', 'jpg', 'png'}\nMAX_FILE_SIZE = 2097152 # 2MB\n\ndef allowed_file(filename):\n return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS\n\ndef upload(request):\n if request.method == 'POST':\n img = request.FILES.get('filename')\n if img.size < MAX_FILE_SIZE and allowed_file(img.name):\n # 重命名文件 (UUID)\n import uuid\n ext = img.name.rsplit('.', 1)[1]\n new_name = str(uuid.uuid5(uuid.NAMESPACE_DNS, img.name)) + \".\" + ext\n\n name = os.path.join(UPLOAD_DIR, new_name)\n with open(name, 'wb') as f:\n for chunk in img.chunks():\n f.write(chunk)\n return render(request, 'upload.html', {'file': '上传成功'})\n else:\n return render(request, 'upload.html', {'file': \"不允许的类型或大小超限\"})\n\n# 安全: 使用 FileField 验证\nfrom django.core.validators import FileExtensionValidator\n\nclass Document(models.Model):\n file = models.FileField(\n upload_to='documents/%Y/%m/%d', # 按日期组织\n validators=[FileExtensionValidator(allowed_extensions=['pdf', 'doc'])],\n max_length=100\n )\n\n# 安全: 路径遍历防护\ndef safe_download(request, filename):\n base_dir = os.path.abspath(UPLOAD_DIR)\n file_path = os.path.abspath(os.path.join(base_dir, filename))\n\n if not file_path.startswith(base_dir):\n raise ValueError(\"Path traversal detected\")\n\n return FileResponse(open(file_path, 'rb'))\n```\n\n### 6. 调试模式泄露\n\n```python\n# settings.py\nDEBUG = True # 生产环境必须False!!!\nALLOWED_HOSTS = ['*'] # 危险: 应指定具体域名\n\n# 调试工具栏\nINSTALLED_APPS = [\n 'debug_toolbar', # 生产环境应移除\n]\n```\n\n### 7. SECRET_KEY安全\n\n```python\n# 危险: 硬编码或弱密钥\nSECRET_KEY = 'django-insecure-xxx'\nSECRET_KEY = 'secret'\n\n# 安全: 环境变量\nSECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')\n```\n\n### 8. 管理后台安全\n\n```python\n# urls.py - 检查admin路径\nurlpatterns = [\n path('admin/', admin.site.urls), # 默认路径,应更改\n]\n\n# 应使用不可预测的路径\npath('super-secret-admin-xyz/', admin.site.urls),\n\n# 检查admin权限\n# admin.py\n@admin.register(SensitiveModel)\nclass SensitiveModelAdmin(admin.ModelAdmin):\n # 检查是否限制了权限\n def has_delete_permission(self, request, obj=None):\n return request.user.is_superuser\n```\n\n---\n\n## Django审计清单\n\n```\n配置检查:\n- [ ] DEBUG = False (生产环境)\n- [ ] SECRET_KEY 强度和存储 (避免硬编码)\n- [ ] ALLOWED_HOSTS 配置 (避免 ['*'])\n- [ ] SESSION_COOKIE_SECURE = True\n- [ ] SESSION_COOKIE_HTTPONLY = True\n- [ ] SESSION_COOKIE_SAMESITE = 'Lax'\n- [ ] CSRF_COOKIE_SECURE = True\n- [ ] CSRF 中间件启用\n- [ ] debug_toolbar 生产环境移除\n\nORM 安全:\n- [ ] 搜索 .raw() 调用 (字符串拼接)\n- [ ] 搜索 .extra() 调用 (where 拼接)\n- [ ] 搜索 RawSQL 使用 (拼接)\n- [ ] 搜索 cursor.execute() 拼接\n- [ ] 检查 .filter(**dict) 参数名可控\n- [ ] 检查 .create(**dict) 字典键可控\n- [ ] 检查 __regex/__iregex 输入\n- [ ] 检查二次注入场景\n- [ ] 验证 lookup 语法使用 (__contains 等)\n\n模板安全:\n- [ ] 搜索 mark_safe 使用\n- [ ] 搜索 |safe 过滤器\n- [ ] 搜索 autoescape off\n- [ ] 搜索 HttpResponse 拼接 HTML\n- [ ] 检查双重格式化 (% + format)\n\nCSRF:\n- [ ] 检查中间件配置\n- [ ] 搜索 @csrf_exempt 装饰器\n- [ ] 验证 AJAX 请求 CSRF 处理\n- [ ] 检查 CSRF_TRUSTED_ORIGINS 配置\n\n认证:\n- [ ] 检查密码验证器配置\n- [ ] 审计自定义认证后端\n- [ ] 检查登录限流\n- [ ] 验证密码重置流程\n\n文件处理:\n- [ ] 检查 MEDIA_ROOT 配置\n- [ ] 验证上传文件类型限制\n- [ ] 验证上传文件大小限制\n- [ ] 检查文件下载路径验证\n- [ ] 验证文件名重命名\n- [ ] 检查 FileField validators\n\n管理后台:\n- [ ] 检查 admin 路径 (避免默认 /admin/)\n- [ ] 验证 admin 权限配置\n- [ ] 检查 ModelAdmin 权限方法\n- [ ] 验证敏感模型访问控制\n\n其他:\n- [ ] 搜索 redirect() 用户输入\n- [ ] 检查 is_safe_url 使用 (CVE-2017-7233)\n- [ ] 验证环境变量使用 (敏感信息)\n```\n\n---\n\n## 审计正则\n\n```regex\n# ORM 注入\n\\.raw\\s*\\(f['\"']|\\.raw\\s*\\([^)]*%\\s*str\\(\n\\.extra\\s*\\([^)]*WHERE\\s*=\\s*\\[|\\.extra\\s*\\([^)]*where\\s*=\nRawSQL\\s*\\(f['\"']\ncursor\\.execute\\s*\\([^)]*['\"]\\s*%|execute\\s*\\([^)]*\\+\n\\.filter\\s*\\(\\*\\*|\\.create\\s*\\(\\*\\*\n\n# XSS\nmark_safe\\s*\\(|\\|safe\\s*}}|autoescape\\s+off\nHttpResponse\\s*\\([^)]*%|HttpResponse\\s*\\(f['\"']\n\n# SSTI / 格式化字符串\n['\"]\\s*%\\s*[^'\"]*\\.format\\s*\\(\n\n# CSRF\n@csrf_exempt|csrf_exempt\nCsrfViewMiddleware.*#\n\n# 配置\nDEBUG\\s*=\\s*True|ALLOWED_HOSTS\\s*=\\s*\\[['\"]?\\*['\"]?\\]\n\n# SECRET_KEY\nSECRET_KEY\\s*=\\s*['\"][^'\"]{0,30}['\"]\n\n# 文件操作\nrequest\\.FILES\\s*\\[|request\\.FILES\\.get\\s*\\(\nFileResponse\\s*\\(open\\s*\\(|open\\s*\\([^)]*file\\.name\n\n# URL 重定向\nredirect\\s*\\([^)]*request\\.|HttpResponseRedirect\\s*\\([^)]*request\\.\nis_safe_url\\s*\\(\n```\n\n## 最小 PoC 示例\n```bash\n# DEBUG/ALLOWED_HOSTS 检查\ncurl -I http://localhost:8000/ | grep \"X-Content-Type-Options\"\n\n# SQL 注入 (.raw)\ncurl \"http://localhost:8000/users?q=1' OR '1'='1\"\n\n# Open Redirect\ncurl \"http://localhost:8000/redirect?next=http://evil.com\"\n```\n"
const SKILL_FILE_3 = "# ASP.NET Core / Blazor Security Audit Guide\n\n> ASP.NET Core 框架安全审计模块\n> 适用于: ASP.NET Core 6/7/8, Blazor Server/WebAssembly, Razor Pages, Minimal APIs\n\n## 识别特征\n\n```csharp\n// ASP.NET Core 项目识别\n\nusing Microsoft.AspNetCore;\n\n// 文件结构\n├── Program.cs / Startup.cs\n├── appsettings.json\n├── Controllers/\n├── Pages/ (Razor Pages)\n├── Views/ (MVC)\n├── Models/\n├── Data/ (EF Core DbContext)\n└── wwwroot/\n```\n\n---\n\n## SQL 注入检测\n\n```csharp\n// 危险: EF Core 原始SQL拼接\nvar users = db.Users\n .FromSqlRaw(\"SELECT * FROM Users WHERE Name = '\" + name + \"'\"); // ❌ Critical\n\n// 危险: Dapper 字符串拼接\nconnection.Query(\"SELECT * FROM Users WHERE Id = \" + id); // ❌ Critical\nconnection.Execute($\"DELETE FROM Users WHERE Id = {id}\"); // ❌ Critical\n\n// 危险: ADO.NET 原始拼接\nvar cmd = new SqlCommand(\"SELECT * FROM Users WHERE Name = '\" + input + \"'\", conn); // ❌\n\n// 危险: EF Core ExecuteSqlRaw 拼接\ndb.Database.ExecuteSqlRaw(\"UPDATE Users SET Name = '\" + name + \"' WHERE Id = \" + id); // ❌\n\n// 审计正则\nFromSqlRaw\\s*\\(.*\\+|ExecuteSqlRaw\\s*\\(.*\\+\nconnection\\.(Query|Execute)\\s*\\(.*\\+|SqlCommand\\s*\\(.*\\+\n\n// 安全: 参数化查询\ndb.Users.FromSqlInterpolated($\"SELECT * FROM Users WHERE Name = {name}\"); // ✓\ndb.Database.ExecuteSqlInterpolated($\"UPDATE Users SET Name = {name}\"); // ✓\n\n// Dapper 参数化\nconnection.Query(\"SELECT * FROM Users WHERE Id = @Id\", new { Id = id }); // ✓\n\n// ADO.NET 参数化\nvar cmd = new SqlCommand(\"SELECT * FROM Users WHERE Name = @Name\", conn);\ncmd.Parameters.AddWithValue(\"@Name\", input); // ✓\n```\n\n---\n\n## XSS 检测 (Razor Views)\n\n```html\n\n@Html.Raw(Model.UserComment) \n@Html.Raw(ViewBag.Message) \n\n\n \n\n\nHtml\\.Raw\\s*\\(|@Html\\.Raw|@\\(.*Html\\.Raw\n \n```\n\n---\n\n## CSRF 配置检测\n\n```csharp\n// 危险: 全局禁用防伪验证\nservices.AddControllersWithViews(options =>\n{\n // 未添加 AutoValidateAntiforgeryTokenAttribute\n});\n\n// 危险: 控制器级别忽略\n[IgnoreAntiforgeryToken] // ❌ 跳过CSRF验证\npublic IActionResult UpdateProfile(ProfileModel model) { ... }\n\n// 危险: Razor Pages 禁用\n@attribute [IgnoreAntiforgeryToken] // ❌\n\n// 审计正则\n\\[IgnoreAntiforgeryToken\\]|IgnoreAntiforgeryToken\nAddControllersWithViews(?!.*AutoValidateAntiforgeryToken)\n\n// 安全: 全局启用\nservices.AddControllersWithViews(options =>\n{\n options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()); // ✓\n});\n\n// Razor Pages 默认启用; API使用JWT则可禁用CSRF但需确认\n```\n\n---\n\n## 授权配置缺陷\n\n```csharp\n// 危险: [AllowAnonymous] 覆盖 [Authorize]\n[Authorize(Roles = \"Admin\")]\npublic class AdminController : Controller\n{\n [AllowAnonymous] // ❌ Critical: 覆盖了类级别的Authorize\n public IActionResult SensitiveAction() { ... }\n}\n\n// 危险: 缺少授权属性\npublic class PaymentController : Controller // ❌ 无[Authorize]\n{\n public IActionResult ProcessPayment() { ... }\n}\n\n// 危险: Minimal API 遗漏授权\napp.MapGet(\"/admin/users\", GetAllUsers); // ❌ 未添加RequireAuthorization\n\n// 审计正则\n\\[AllowAnonymous\\]|AllowAnonymous\nMapGet\\(|MapPost\\(|MapPut\\(|MapDelete\\( // 检查是否有RequireAuthorization\n\n// 安全: Fallback策略 (默认拒绝)\nbuilder.Services.AddAuthorization(options =>\n{\n options.FallbackPolicy = new AuthorizationPolicyBuilder()\n .RequireAuthenticatedUser()\n .Build(); // ✓ 未标注的端点默认需要认证\n});\n\n// Minimal API 授权\napp.MapGet(\"/admin/users\", GetAllUsers).RequireAuthorization(\"AdminPolicy\"); // ✓\n```\n\n---\n\n## 不安全的反序列化\n\n```csharp\n// 危险: BinaryFormatter (已弃用,但仍在老代码中)\nBinaryFormatter formatter = new BinaryFormatter();\nobject obj = formatter.Deserialize(stream); // ❌ Critical: RCE\n\n// 危险: Newtonsoft TypeNameHandling\nvar settings = new JsonSerializerSettings\n{\n TypeNameHandling = TypeNameHandling.All // ❌ Critical: 反序列化任意类型\n};\nJsonConvert.DeserializeObject(json, settings);\n\n// 危险: TypeNameHandling.Auto/Objects 同样可利用\nTypeNameHandling = TypeNameHandling.Auto // ❌\nTypeNameHandling = TypeNameHandling.Objects // ❌\n\n// 审计正则\nBinaryFormatter|ObjectStateFormatter|SoapFormatter|NetDataContractSerializer\nTypeNameHandling\\s*=\\s*TypeNameHandling\\.(All|Auto|Objects|Arrays)\nLosFormatter|XmlSerializer\\s*\\(.*typeof\\s*\\(.*GetType\n\n// 安全: 使用 System.Text.Json (默认安全)\nvar obj = JsonSerializer.Deserialize(json); // ✓ 无多态类型处理\n\n// 如果必须用 Newtonsoft, 使用 SerializationBinder 白名单\nvar settings = new JsonSerializerSettings\n{\n TypeNameHandling = TypeNameHandling.Auto,\n SerializationBinder = new KnownTypesBinder(allowedTypes) // ✓\n};\n```\n\n---\n\n## 路径遍历 (文件下载)\n\n```csharp\n// 危险: 直接拼接文件路径\n[HttpGet(\"download\")]\npublic IActionResult Download(string fileName)\n{\n var path = Path.Combine(_uploadDir, fileName);\n return PhysicalFile(path, \"application/octet-stream\"); // ❌ ../../../etc/passwd\n}\n\n// 危险: 未规范化路径\nvar fullPath = _baseDir + \"/\" + userInput; // ❌ 路径遍历\nSystem.IO.File.ReadAllText(fullPath);\n\n// 审计正则\nPhysicalFile\\s*\\(.*\\+|Path\\.Combine\\s*\\(.*fileName|File\\.(Read|Open|Delete).*\\+\nSendFileAsync\\s*\\(.*\\+\n\n// 安全: 路径验证\n[HttpGet(\"download\")]\npublic IActionResult Download(string fileName)\n{\n if (fileName.Contains(\"..\") || Path.IsPathRooted(fileName))\n return BadRequest();\n\n var path = Path.Combine(_uploadDir, fileName);\n var fullPath = Path.GetFullPath(path);\n\n if (!fullPath.StartsWith(Path.GetFullPath(_uploadDir))) // ✓ 规范化后比对\n return BadRequest();\n\n return PhysicalFile(fullPath, \"application/octet-stream\");\n}\n```\n\n---\n\n## CORS 配置缺陷\n\n```csharp\n// 危险: 允许所有来源 + 凭据\nbuilder.Services.AddCors(options =>\n{\n options.AddPolicy(\"AllowAll\", policy =>\n {\n policy.AllowAnyOrigin() // ❌\n .AllowAnyMethod()\n .AllowAnyHeader()\n .AllowCredentials(); // ❌ 与AllowAnyOrigin冲突且危险\n });\n});\n\n// 危险: 在 Program.cs/Startup.cs 中动态反射 Origin\napp.UseCors(policy => policy\n .SetIsOriginAllowed(_ => true) // ❌ 允许任何来源\n .AllowCredentials());\n\n// 审计正则\nAllowAnyOrigin|SetIsOriginAllowed.*true|AllowCredentials\n\n// 安全: 明确白名单\nbuilder.Services.AddCors(options =>\n{\n options.AddPolicy(\"Strict\", policy =>\n {\n policy.WithOrigins(\"https://app.example.com\") // ✓\n .WithMethods(\"GET\", \"POST\")\n .WithHeaders(\"Content-Type\", \"Authorization\")\n .AllowCredentials();\n });\n});\n```\n\n---\n\n## Blazor 安全差异\n\n```csharp\n// Blazor Server: 服务端渲染, SignalR连接\n// - 敏感数据在服务端, 但 SignalR 消息可能泄露\n// - Circuit 劫持: 确保认证检查在每个Hub调用中\n// - 防止 JS Interop 注入\n\n// 危险: Blazor Server 中直接访问数据库无权限检查\n@code {\n private async Task LoadData()\n {\n data = await _dbContext.SensitiveData.ToListAsync(); // ❌ 无用户权限检查\n }\n}\n\n// 危险: MarkupString 等价于 Html.Raw\n@((MarkupString)userInput) // ❌ XSS\n\n// Blazor WebAssembly: 客户端运行\n// - 所有C#代码在浏览器中, 可被反编译 → 不要在WASM中存放密钥\n// - API调用必须在服务端验证 → 不能仅依赖客户端授权检查\n// - 审计: 检查 wwwroot/ 中是否有敏感配置\n\n// 审计正则\nMarkupString\\)|@\\(\\(MarkupString\\)\nNavigationManager\\.NavigateTo\\s*\\(.*\\+ // 开放重定向\n```\n\n---\n\n## 搜索模式汇总\n\n```regex\n# SQL注入\nFromSqlRaw\\s*\\(.*\\+|ExecuteSqlRaw\\s*\\(.*\\+|SqlCommand\\s*\\(.*\\+\nconnection\\.(Query|Execute)\\s*\\(.*\\+\n\n# XSS\nHtml\\.Raw\\s*\\(|MarkupString\\)\n\n# CSRF\n\\[IgnoreAntiforgeryToken\\]\n\n# 授权\n\\[AllowAnonymous\\]\nMapGet\\(|MapPost\\( // 检查Minimal API授权\n\n# 反序列化\nBinaryFormatter|TypeNameHandling\\.(All|Auto|Objects)\n\n# 路径遍历\nPhysicalFile\\s*\\(.*\\+|Path\\.Combine\\s*\\(.*fileName\n\n# CORS\nAllowAnyOrigin|SetIsOriginAllowed.*true\n\n# 敏感配置\n\"ConnectionStrings\".*password|appsettings.*Development.*json\n```\n\n---\n\n## 快速审计检查清单\n\n```markdown\n[ ] 检查 ASP.NET Core 版本和已知CVE\n[ ] 搜索 FromSqlRaw/ExecuteSqlRaw 字符串拼接\n[ ] 搜索 Dapper Query/Execute 拼接\n[ ] 搜索 Html.Raw / MarkupString (XSS)\n[ ] 搜索 [IgnoreAntiforgeryToken] (CSRF绕过)\n[ ] 搜索 [AllowAnonymous] 在敏感Controller上\n[ ] 检查 FallbackPolicy 授权配置\n[ ] 搜索 BinaryFormatter / TypeNameHandling (反序列化)\n[ ] 检查文件下载端点路径验证\n[ ] 搜索 AllowAnyOrigin / SetIsOriginAllowed (CORS)\n[ ] 检查 appsettings.json 中硬编码密钥/连接字符串\n[ ] 检查 Blazor 中 MarkupString 和 JS Interop\n[ ] 验证 Minimal API 端点是否有 RequireAuthorization\n[ ] 检查异常处理是否泄露堆栈 (app.UseDeveloperExceptionPage)\n```\n\n---\n\n## 最小 PoC 示例\n```bash\n# SQL 注入 (Dapper/Raw SQL)\ncurl \"http://localhost:5000/api/users?name=admin'OR'1'='1\"\n\n# 路径遍历\ncurl \"http://localhost:5000/download?fileName=../../../etc/passwd\"\n\n# 开放重定向\ncurl \"http://localhost:5000/redirect?url=https://evil.com\"\n```\n\n---\n\n## 参考资源\n\n- [Microsoft ASP.NET Core Security Documentation](https://learn.microsoft.com/en-us/aspnet/core/security/)\n- [OWASP .NET Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/DotNet_Security_Cheat_Sheet.html)\n- [Blazor Security Considerations](https://learn.microsoft.com/en-us/aspnet/core/blazor/security/)\n"
const SKILL_FILE_4 = "# Express.js Security Audit\n\n> Express.js 框架安全审计模块\n> 适用于: Express, Koa, Node.js 应用\n\n## 识别特征\n\n```javascript\n// Express项目识别\nconst express = require('express');\nconst app = express();\n\n// 文件结构\n├── package.json\n├── app.js / index.js\n├── routes/\n├── controllers/\n├── models/\n├── middleware/\n└── views/\n```\n\n---\n\n## Express特定漏洞\n\n### 1. NoSQL注入 (MongoDB)\n\n```javascript\n// 危险: 直接使用用户输入\napp.post('/login', async (req, res) => {\n const user = await User.findOne({\n username: req.body.username,\n password: req.body.password // 可注入 {$gt: \"\"}\n });\n});\n\n// 攻击payload\n// {\"username\": \"admin\", \"password\": {\"$gt\": \"\"}}\n\n// 安全: 类型验证\napp.post('/login', async (req, res) => {\n if (typeof req.body.username !== 'string' ||\n typeof req.body.password !== 'string') {\n return res.status(400).json({error: 'Invalid input'});\n }\n const user = await User.findOne({...});\n});\n```\n\n### 2. 原型污染\n\n```javascript\n// 危险: 合并用户输入\nconst _ = require('lodash');\napp.post('/config', (req, res) => {\n _.merge(config, req.body); // 原型污染!\n});\n\n// 攻击payload\n// {\"__proto__\": {\"isAdmin\": true}}\n\n// 危险: 递归赋值\nfunction merge(target, source) {\n for (let key in source) {\n target[key] = source[key]; // 污染Object.prototype\n }\n}\n\n// 安全: 过滤危险键\nconst FORBIDDEN_KEYS = ['__proto__', 'constructor', 'prototype'];\nfunction safeMerge(target, source) {\n for (let key in source) {\n if (FORBIDDEN_KEYS.includes(key)) continue;\n target[key] = source[key];\n }\n}\n```\n\n### 3. 路径遍历\n\n```javascript\n// 危险: 用户输入拼接路径\napp.get('/download', (req, res) => {\n const file = req.query.file;\n res.sendFile(`/uploads/${file}`); // ../../etc/passwd\n});\n\n// 安全: 使用path.basename\nconst path = require('path');\napp.get('/download', (req, res) => {\n const file = path.basename(req.query.file); // 移除路径\n const fullPath = path.join('/uploads', file);\n // 验证在目标目录内\n if (!fullPath.startsWith('/uploads/')) {\n return res.status(400).send('Invalid path');\n }\n res.sendFile(fullPath);\n});\n```\n\n### 4. 命令注入\n\n```javascript\n// 危险: exec拼接\nconst { exec } = require('child_process');\napp.get('/ping', (req, res) => {\n exec(`ping -c 1 ${req.query.host}`, (err, stdout) => { // RCE!\n res.send(stdout);\n });\n});\n\n// 安全: 使用execFile + 参数数组\nconst { execFile } = require('child_process');\napp.get('/ping', (req, res) => {\n execFile('ping', ['-c', '1', req.query.host], (err, stdout) => {\n res.send(stdout);\n });\n});\n```\n\n### 5. XSS (模板引擎)\n\n```javascript\n// EJS危险模式\n<%- userInput %> // 不转义\n\n// 安全模式\n<%= userInput %> // 自动转义\n\n// Pug/Jade危险模式\n!{userInput} // 不转义\np= userInput // 安全\n\n// Handlebars危险模式\n{{{userInput}}} // 三重大括号不转义\n{{userInput}} // 双重大括号安全\n```\n\n### 6. 会话安全\n\n```javascript\n// 危险: 不安全的session配置\napp.use(session({\n secret: 'keyboard cat', // 弱密钥\n cookie: { secure: false } // 不安全\n}));\n\n// 安全配置\napp.use(session({\n secret: process.env.SESSION_SECRET,\n name: 'sessionId', // 更改默认名称\n cookie: {\n secure: true,\n httpOnly: true,\n sameSite: 'strict',\n maxAge: 3600000\n },\n resave: false,\n saveUninitialized: false\n}));\n```\n\n### 7. CORS配置\n\n```javascript\n// 危险: 允许所有来源\napp.use(cors());\napp.use(cors({ origin: '*' }));\n\n// 危险: 反射Origin\napp.use(cors({\n origin: (origin, callback) => {\n callback(null, origin); // 反射任意来源\n },\n credentials: true // 允许凭证更危险\n}));\n\n// 安全: 白名单\nconst allowedOrigins = ['https://trusted.com'];\napp.use(cors({\n origin: (origin, callback) => {\n if (allowedOrigins.includes(origin)) {\n callback(null, true);\n } else {\n callback(new Error('Not allowed'));\n }\n }\n}));\n```\n\n### 8. 依赖安全\n\n```bash\n# 检查已知漏洞\nnpm audit\nnpm audit --json\n\n# 检查过时依赖\nnpm outdated\n\n# 检查package.json中的危险包\n# - node-serialize (反序列化RCE)\n# - js-yaml < 3.13.1 (代码执行)\n# - lodash < 4.17.12 (原型污染)\n```\n\n### 9. 安全头配置\n\n```javascript\n// 使用helmet中间件\nconst helmet = require('helmet');\napp.use(helmet());\n\n// 或手动设置\napp.use((req, res, next) => {\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.setHeader('X-Frame-Options', 'DENY');\n res.setHeader('X-XSS-Protection', '1; mode=block');\n res.setHeader('Strict-Transport-Security', 'max-age=31536000');\n next();\n});\n```\n\n---\n\n## Express审计清单\n\n```\n输入验证:\n- [ ] 检查请求体类型验证\n- [ ] 搜索 req.query/req.body/req.params 使用\n- [ ] 验证MongoDB查询输入\n- [ ] 检查文件路径处理\n\n注入漏洞:\n- [ ] 搜索 exec/spawn 调用\n- [ ] 搜索 eval/Function 使用\n- [ ] 检查SQL/NoSQL查询构建\n- [ ] 搜索 _.merge/Object.assign\n\nXSS:\n- [ ] 检查模板引擎配置\n- [ ] 搜索 <%- (EJS不转义)\n- [ ] 搜索 {{{ (Handlebars不转义)\n- [ ] 检查res.send/res.json内容\n\n会话安全:\n- [ ] 检查session secret强度\n- [ ] 验证cookie安全配置\n- [ ] 检查JWT实现\n\n安全配置:\n- [ ] 检查CORS配置\n- [ ] 验证helmet/安全头\n- [ ] 检查错误处理 (不泄露栈)\n- [ ] 验证HTTPS强制\n\n依赖:\n- [ ] 运行 npm audit\n- [ ] 检查危险依赖包\n- [ ] 验证依赖版本\n```\n\n---\n\n## 审计正则\n\n```regex\n# 命令注入\nexec\\s*\\(|spawn\\s*\\(|child_process\n\n# NoSQL注入\nfindOne\\s*\\(|find\\s*\\(|updateOne\\s*\\(\n\n# 原型污染\n\\.merge\\s*\\(|Object\\.assign\\s*\\(|__proto__\n\n# XSS\n<%-|{{{\n\n# 路径遍历\nsendFile\\s*\\(|res\\.download\\s*\\(\n```\n\n## 最小 PoC 示例\n```bash\n# 路径遍历\ncurl \"http://localhost:3000/download?file=../../etc/passwd\"\n\n# NoSQL 注入\ncurl \"http://localhost:3000/user?name[$ne]=1\"\n\n# 命令注入\ncurl \"http://localhost:3000/ping?host=google.com;id\"\n```\n"
const SKILL_FILE_5 = "# FastAPI Security Audit\n\n> FastAPI 框架安全审计模块\n> 适用于: FastAPI, Starlette, SQLAlchemy\n\n## 识别特征\n\n```python\n# FastAPI项目识别\nfrom fastapi import FastAPI, Depends, HTTPException\napp = FastAPI()\n\n# 文件结构\n├── main.py\n├── routers/\n├── models/\n├── schemas/\n├── crud/\n└── dependencies/\n```\n\n---\n\n## 路由与端点分析\n\n```python\n# 搜索所有路由\n@app.get|post|put|delete|patch\n@router.get|post|put|delete|patch\n\n# 路径参数\n@app.get(\"/users/{user_id}\")\ndef get_user(user_id: int): # 检查IDOR\n\n# 查询参数\n@app.get(\"/search\")\ndef search(q: str = Query(...)): # 检查注入\n```\n\n---\n\n## FastAPI特定漏洞\n\n### 1. 依赖注入安全\n\n```python\n# 危险: 未验证的依赖\ndef get_current_user(token: str = Header(...)):\n # 缺少token验证\n return decode_token(token)\n\n# 安全: 完整验证\nasync def get_current_user(\n token: str = Depends(oauth2_scheme),\n db: Session = Depends(get_db)\n):\n user = authenticate_token(token, db)\n if not user:\n raise HTTPException(status_code=401)\n return user\n```\n\n### 2. Pydantic验证绕过\n\n```python\n# 危险: 使用dict绕过验证\n@app.post(\"/users\")\ndef create_user(data: dict): # 无schema验证\n db.create(User(**data))\n\n# 安全: 强类型schema\nclass UserCreate(BaseModel):\n email: EmailStr\n password: str = Field(..., min_length=8)\n\n@app.post(\"/users\")\ndef create_user(user: UserCreate):\n ...\n```\n\n### 3. 响应模型泄露\n\n```python\n# 危险: 返回ORM模型 (可能包含password_hash)\n@app.get(\"/users/{id}\")\ndef get_user(id: int):\n return db.query(User).filter(User.id == id).first()\n\n# 安全: 使用响应模型过滤\nclass UserResponse(BaseModel):\n id: int\n email: str\n # 排除敏感字段\n\n@app.get(\"/users/{id}\", response_model=UserResponse)\ndef get_user(id: int):\n return db.query(User).filter(User.id == id).first()\n```\n\n### 4. 背景任务注入\n\n```python\n# 危险: 用户输入传入背景任务\n@app.post(\"/process\")\ndef process(cmd: str, background_tasks: BackgroundTasks):\n background_tasks.add_task(os.system, cmd) # RCE!\n\n# 检查所有 BackgroundTasks.add_task 调用\n```\n\n### 5. 文件上传处理\n\n```python\n# 危险模式\n@app.post(\"/upload\")\nasync def upload(file: UploadFile):\n content = await file.read()\n with open(f\"/uploads/{file.filename}\", \"wb\") as f: # 路径遍历\n f.write(content)\n\n# 安全模式\nimport uuid\nimport os\n\n@app.post(\"/upload\")\nasync def upload(file: UploadFile):\n # 验证文件类型\n if file.content_type not in ALLOWED_TYPES:\n raise HTTPException(400)\n # 使用安全文件名\n safe_name = f\"{uuid.uuid4()}{os.path.splitext(file.filename)[1]}\"\n # 限制文件大小\n content = await file.read(MAX_SIZE)\n ...\n```\n\n### 6. SQLAlchemy ORM注入\n\n```python\n# 危险: 原始SQL\n@app.get(\"/search\")\ndef search(q: str, db: Session = Depends(get_db)):\n return db.execute(f\"SELECT * FROM items WHERE name LIKE '%{q}%'\")\n\n# 危险: text()拼接\nfrom sqlalchemy import text\ndb.execute(text(f\"SELECT * FROM users WHERE id = {user_id}\"))\n\n# 安全: 参数化\ndb.execute(text(\"SELECT * FROM users WHERE id = :id\"), {\"id\": user_id})\n```\n\n---\n\n## FastAPI审计清单\n\n```\n认证与授权:\n- [ ] 检查OAuth2/JWT实现 (Depends)\n- [ ] 验证所有端点有适当的认证装饰器\n- [ ] 检查IDOR (路径参数未验证所有权)\n- [ ] 审计权限检查逻辑\n\n输入验证:\n- [ ] 检查是否使用Pydantic schema\n- [ ] 搜索 dict 类型参数 (绕过验证)\n- [ ] 验证Query/Path/Body参数验证\n- [ ] 检查文件上传处理\n\n输出安全:\n- [ ] 检查response_model使用\n- [ ] 验证敏感字段排除\n- [ ] 检查错误响应信息\n\n数据库:\n- [ ] 搜索原始SQL执行\n- [ ] 检查SQLAlchemy text()使用\n- [ ] 验证ORM查询安全\n\nCORS:\n- [ ] 检查CORSMiddleware配置\n- [ ] 验证allow_origins设置\n- [ ] 检查allow_credentials=True风险\n```\n\n---\n\n## 审计正则\n\n```regex\n# 路由搜索\n@(app|router)\\.(get|post|put|delete|patch)\\s*\\(\n\n# Pydantic绕过\ndef\\s+\\w+\\([^)]*:\\s*dict[^)]*\\)\n\n# 背景任务\nBackgroundTasks\\.add_task\n\n# SQL注入\ndb\\.execute\\s*\\(f['\"']|text\\s*\\(f['\"']\n\n# 文件上传\nUploadFile.*file\\.filename\n```\n\n## 最小 PoC 示例\n```bash\n# SQL 注入\ncurl \"http://localhost:8000/users?q=1' OR '1'='1\"\n\n# SSRF\ncurl \"http://localhost:8000/fetch?url=http://169.254.169.254/latest/meta-data/\"\n\n# 路径遍历上传文件名\ncurl -F \"file=@/etc/passwd;filename=../../etc/passwd\" http://localhost:8000/upload\n```\n"
const SKILL_FILE_6 = "# Flask Security Audit\n\n> Flask 框架安全审计模块\n> 适用于: Flask, Flask-RESTful, Flask-Login, Flask-JWT-Extended\n\n---\n\n## 识别特征\n\n```python\n# Flask 项目识别\nfrom flask import Flask, request, render_template\napp = Flask(__name__)\n\n# 常见目录结构\n├── app/\n│ ├── __init__.py # create_app 工厂\n│ ├── routes/ # 路由蓝图\n│ ├── models/ # SQLAlchemy 模型\n│ ├── templates/ # Jinja2 模板\n│ └── static/ # 静态资源\n├── config.py # 配置\n├── requirements.txt\n└── run.py / wsgi.py\n```\n\n---\n\n## Critical 漏洞\n\n### 1. 调试模式 RCE\n\n```python\n# 危险: 生产环境开启调试\nif __name__ == '__main__':\n app.run(debug=True) # Werkzeug debugger PIN 可被计算\n\n# 危险: 配置文件\nDEBUG = True\nFLASK_DEBUG = 1\n\n# PIN 计算要素 (可用于爆破):\n# - username (运行用户)\n# - modname (通常 flask.app)\n# - getattr(app, '__name__', app.__class__.__name__)\n# - getattr(mod, '__file__', None) → app.py 路径\n# - uuid.getnode() → MAC 地址\n# - get_machine_id() → /etc/machine-id 或 /proc/sys/kernel/random/boot_id\n\n# 检测\ngrep -rn \"debug\\s*=\\s*True\" --include=\"*.py\"\ngrep -rn \"FLASK_DEBUG\\|DEBUG\\s*=\" --include=\"*.py\" --include=\"*.env\"\n```\n\n### 2. SECRET_KEY 泄露\n\n```python\n# 危险: 硬编码密钥\napp.secret_key = 'super_secret_key'\napp.config['SECRET_KEY'] = 'hardcoded'\n\n# 危险: 弱密钥\nSECRET_KEY = 'development'\nSECRET_KEY = os.urandom(16) # 每次重启变化,session 失效\n\n# 影响:\n# - Session 伪造\n# - Flask-Login 令牌伪造\n# - CSRF token 伪造\n# - itsdangerous 签名伪造\n\n# 安全: 从环境变量读取\napp.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')\nif not app.config['SECRET_KEY']:\n raise ValueError(\"SECRET_KEY not set\")\n\n# 检测\ngrep -rn \"secret_key\\s*=\\|SECRET_KEY\\s*=\" --include=\"*.py\"\n```\n\n### 3. SSTI (Server-Side Template Injection)\n\n```python\n# 危险: render_template_string 用户输入\nfrom flask import render_template_string\n\n@app.route('/hello')\ndef hello():\n name = request.args.get('name', 'World')\n template = f'Hello {name}!
'\n return render_template_string(template)\n\n# payload: {{config.items()}}\n# payload: {{''.__class__.__mro__[1].__subclasses__()}}\n# RCE payload:\n# {{''.__class__.__mro__[1].__subclasses__()[X].__init__.__globals__['os'].popen('id').read()}}\n\n# 危险: 用户控制模板名\n@app.route('/page/')\ndef page(template):\n return render_template(template) # 可能加载任意模板\n\n# 安全: 固定模板,传递变量\n@app.route('/hello')\ndef hello():\n name = request.args.get('name', 'World')\n return render_template('hello.html', name=name)\n\n# SSTI 检测命令\ngrep -rn \"render_template_string\" --include=\"*.py\"\ngrep -rn \"render_template.*request\\.\" --include=\"*.py\"\ngrep -rn \"Template\\s*\\(\" --include=\"*.py\"\n```\n\n### 4. Pickle Session 反序列化\n\n```python\n# 危险: 使用 pickle 序列化 session\n# Flask 默认使用 itsdangerous 签名的 JSON\n# 但某些扩展可能使用 pickle\n\n# Flask-Session 配置\nSESSION_TYPE = 'filesystem' # 文件存储\nSESSION_SERIALIZER = PickleSerializer # 危险!\n\n# 如果 SECRET_KEY 泄露 + pickle session = RCE\n\n# 安全配置\nSESSION_TYPE = 'redis'\nSESSION_SERIALIZER = 'json'\n\n# 检测\ngrep -rn \"SESSION_SERIALIZER\\|PickleSerializer\\|pickle\" --include=\"*.py\"\n```\n\n---\n\n## High 漏洞\n\n### 5. SQL 注入\n\n```python\n# 危险: 原始 SQL 拼接\nfrom flask_sqlalchemy import SQLAlchemy\n\n@app.route('/user/')\ndef get_user(id):\n sql = f\"SELECT * FROM users WHERE id = {id}\"\n result = db.engine.execute(sql)\n return jsonify(result.fetchone())\n\n# 危险: text() 拼接\nfrom sqlalchemy import text\ndb.session.execute(text(f\"SELECT * FROM users WHERE name = '{name}'\"))\n\n# 安全: 参数化\ndb.session.execute(\n text(\"SELECT * FROM users WHERE name = :name\"),\n {\"name\": name}\n)\n\n# 安全: ORM\nUser.query.filter_by(id=id).first()\nUser.query.filter(User.name == name).all()\n\n# 检测\ngrep -rn \"execute\\s*\\(.*f[\\\"']\" --include=\"*.py\"\ngrep -rn \"execute\\s*\\(.*\\+\" --include=\"*.py\"\ngrep -rn \"text\\s*\\(.*f[\\\"']\" --include=\"*.py\"\n```\n\n### 6. 命令注入\n\n```python\n# 危险: os.system\n@app.route('/ping')\ndef ping():\n host = request.args.get('host')\n os.system(f'ping -c 4 {host}')\n return 'Done'\n# payload: ?host=127.0.0.1;id\n\n# 危险: subprocess shell=True\nimport subprocess\nsubprocess.run(f'echo {user_input}', shell=True)\n\n# 安全: 参数列表\nsubprocess.run(['ping', '-c', '4', host], shell=False)\n\n# 更安全: 避免命令执行\nimport socket\nsocket.create_connection((host, 80), timeout=5)\n```\n\n### 7. 任意文件读取/写入\n\n```python\n# 危险: 路径拼接\n@app.route('/download')\ndef download():\n filename = request.args.get('file')\n return send_file(f'/uploads/{filename}')\n# payload: ?file=../../../etc/passwd\n\n# 危险: 文件上传未验证\n@app.route('/upload', methods=['POST'])\ndef upload():\n file = request.files['file']\n file.save(f'/uploads/{file.filename}') # 危险!\n\n# 安全: 验证和清理\nfrom werkzeug.utils import secure_filename\nimport os\n\nUPLOAD_FOLDER = '/var/www/uploads'\nALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}\n\ndef allowed_file(filename):\n return '.' in filename and \\\n filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS\n\n@app.route('/upload', methods=['POST'])\ndef upload():\n file = request.files['file']\n if file and allowed_file(file.filename):\n filename = secure_filename(file.filename)\n file.save(os.path.join(UPLOAD_FOLDER, filename))\n return 'Uploaded'\n return 'Invalid file', 400\n\n# 安全下载\n@app.route('/download')\ndef download():\n filename = request.args.get('file')\n safe_name = secure_filename(filename)\n full_path = os.path.join(UPLOAD_FOLDER, safe_name)\n\n # 验证路径在允许目录内\n if not full_path.startswith(UPLOAD_FOLDER):\n abort(403)\n\n return send_from_directory(UPLOAD_FOLDER, safe_name)\n```\n\n### 8. SSRF\n\n```python\n# 危险: 未验证 URL\nimport requests\n\n@app.route('/fetch')\ndef fetch():\n url = request.args.get('url')\n resp = requests.get(url)\n return resp.text\n# payload: ?url=http://169.254.169.254/latest/meta-data/\n\n# 安全: URL 白名单\nfrom urllib.parse import urlparse\n\nALLOWED_HOSTS = ['api.example.com']\n\n@app.route('/fetch')\ndef fetch():\n url = request.args.get('url')\n parsed = urlparse(url)\n\n if parsed.scheme not in ['http', 'https']:\n abort(400)\n\n if parsed.hostname not in ALLOWED_HOSTS:\n abort(403)\n\n resp = requests.get(url, timeout=5)\n return resp.text\n```\n\n---\n\n## Medium 漏洞\n\n### 9. XSS\n\n```python\n# 危险: Markup/safe 标记\nfrom flask import Markup\n\n@app.route('/comment')\ndef comment():\n text = request.args.get('text')\n return Markup(f'{text}
') # 不转义!\n\n# 危险: |safe 过滤器\n{{ user_input|safe }}\n\n# 危险: 直接返回 HTML\n@app.route('/hello')\ndef hello():\n name = request.args.get('name')\n return f'Hello {name}
' # 不经过模板\n\n# 安全: 使用模板自动转义\nreturn render_template('hello.html', name=name)\n# 模板: Hello {{ name }}
# 自动转义\n\n# 检测\ngrep -rn \"Markup\\s*\\(\" --include=\"*.py\"\ngrep -rn \"|safe\" --include=\"*.html\"\ngrep -rn \"return.*f['\\\"].*<\" --include=\"*.py\"\n```\n\n### 10. CSRF 保护缺失\n\n```python\n# 危险: 未使用 CSRF 保护\n@app.route('/transfer', methods=['POST'])\ndef transfer():\n amount = request.form['amount']\n to_account = request.form['to']\n # 执行转账...\n\n# 安全: Flask-WTF CSRF\nfrom flask_wtf.csrf import CSRFProtect\n\ncsrf = CSRFProtect(app)\n\n# 表单中添加\n{{ csrf_token() }}\n# 或\n\n\n# AJAX 请求\nheaders: {\n 'X-CSRFToken': '{{ csrf_token() }}'\n}\n\n# 检测\ngrep -rn \"methods.*POST\\|methods.*PUT\\|methods.*DELETE\" --include=\"*.py\"\n# 检查是否有 CSRF 保护\n```\n\n### 11. Open Redirect\n\n```python\n# 危险: 未验证重定向 URL\nfrom flask import redirect\n\n@app.route('/login')\ndef login():\n next_url = request.args.get('next')\n # 登录成功后\n return redirect(next_url)\n# payload: ?next=http://evil.com\n\n# 安全: 验证是相对路径或同域\nfrom urllib.parse import urlparse, urljoin\n\ndef is_safe_url(target):\n ref_url = urlparse(request.host_url)\n test_url = urlparse(urljoin(request.host_url, target))\n return test_url.scheme in ('http', 'https') and \\\n ref_url.netloc == test_url.netloc\n\n@app.route('/login')\ndef login():\n next_url = request.args.get('next', '/')\n if not is_safe_url(next_url):\n next_url = '/'\n return redirect(next_url)\n```\n\n### 12. Session 配置不安全\n\n```python\n# 危险配置\nSESSION_COOKIE_SECURE = False # HTTP 传输\nSESSION_COOKIE_HTTPONLY = False # JS 可访问\nSESSION_COOKIE_SAMESITE = None # 无 SameSite\n\n# 安全配置\napp.config.update(\n SESSION_COOKIE_SECURE=True, # 仅 HTTPS\n SESSION_COOKIE_HTTPONLY=True, # JS 不可访问\n SESSION_COOKIE_SAMESITE='Lax', # 防 CSRF\n PERMANENT_SESSION_LIFETIME=3600, # 1小时过期\n)\n```\n\n---\n\n## Flask 扩展安全\n\n### Flask-Login\n\n```python\n# 危险: remember_me 无限期\nlogin_user(user, remember=True) # 默认 365 天\n\n# 安全: 设置过期时间\napp.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=7)\napp.config['REMEMBER_COOKIE_SECURE'] = True\napp.config['REMEMBER_COOKIE_HTTPONLY'] = True\n\n# 危险: user_loader 未验证\n@login_manager.user_loader\ndef load_user(user_id):\n return User.query.get(user_id) # 可能返回已禁用用户\n\n# 安全: 验证用户状态\n@login_manager.user_loader\ndef load_user(user_id):\n user = User.query.get(user_id)\n if user and user.is_active:\n return user\n return None\n```\n\n### Flask-JWT-Extended\n\n```python\n# 危险配置\nJWT_SECRET_KEY = 'weak_secret'\nJWT_ACCESS_TOKEN_EXPIRES = False # 永不过期\n\n# 安全配置\napp.config['JWT_SECRET_KEY'] = os.environ.get('JWT_SECRET')\napp.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)\napp.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=30)\napp.config['JWT_COOKIE_SECURE'] = True\napp.config['JWT_COOKIE_CSRF_PROTECT'] = True\n```\n\n### Flask-CORS\n\n```python\n# 危险: 允许所有来源\nfrom flask_cors import CORS\nCORS(app) # 默认允许所有\n\n# 更危险: 允许凭证\nCORS(app, supports_credentials=True, origins='*')\n\n# 安全: 指定允许的来源\nCORS(app,\n origins=['https://trusted.example.com'],\n supports_credentials=True,\n methods=['GET', 'POST'],\n allow_headers=['Content-Type', 'Authorization'])\n```\n\n---\n\n## 检测命令\n\n```bash\n# 调试模式\ngrep -rn \"debug\\s*=\\s*True\" --include=\"*.py\"\ngrep -rn \"FLASK_DEBUG\" --include=\"*.env\" --include=\"*.py\"\n\n# SECRET_KEY\ngrep -rn \"secret_key\\s*=\\|SECRET_KEY\\s*=\" --include=\"*.py\"\n\n# SSTI\ngrep -rn \"render_template_string\\|Markup\\s*\\(\" --include=\"*.py\"\n\n# SQL 注入\ngrep -rn \"execute\\s*\\(\" --include=\"*.py\" | grep -E \"f['\\\"]|\\+\"\ngrep -rn \"text\\s*\\(\" --include=\"*.py\" | grep -E \"f['\\\"]|\\+\"\n\n# 命令执行\ngrep -rn \"os\\.system\\|subprocess\\.\\|popen\" --include=\"*.py\"\n\n# 文件操作\ngrep -rn \"send_file\\|send_from_directory\\|open\\s*\\(\" --include=\"*.py\"\n\n# 综合扫描\nbandit -r . -ll\n```\n\n---\n\n## 审计清单\n\n```\n[ ] 检查 DEBUG 模式是否在生产环境禁用\n[ ] 检查 SECRET_KEY 是否硬编码或弱密钥\n[ ] 搜索 render_template_string 使用\n[ ] 检查 SQL 查询是否参数化\n[ ] 检查命令执行函数\n[ ] 检查文件操作是否有路径遍历\n[ ] 检查 HTTP 请求是否有 SSRF\n[ ] 检查 CSRF 保护是否启用\n[ ] 检查 Session 配置\n[ ] 检查 CORS 配置\n[ ] 检查 Flask 扩展配置\n[ ] 运行 bandit 静态分析\n```\n\n---\n\n## 最小 PoC 示例\n```bash\n# Jinja2 SSTI\ncurl \"http://localhost:5000/hello?name={{7*7}}\"\n\n# DEBUG 模式检查\ncurl -I http://localhost:5000/ | grep \"Werkzeug\"\n\n# SSRF\ncurl \"http://localhost:5000/fetch?url=http://169.254.169.254/latest/meta-data/\"\n```\n\n---\n\n**最后更新**: 2026-01-23\n**版本**: 1.0.0\n"
const SKILL_FILE_7 = "# Gin Security Audit\n\n> Gin 框架安全审计模块\n> 适用于: Gin, Gin-GORM, Gin-JWT\n\n---\n\n## 识别特征\n\n```go\n// Gin 项目识别\nimport \"github.com/gin-gonic/gin\"\n\nfunc main() {\n r := gin.Default()\n r.Run()\n}\n\n// 常见目录结构\n├── cmd/\n│ └── main.go\n├── internal/\n│ ├── handler/ # 控制器\n│ ├── service/ # 业务逻辑\n│ ├── model/ # 数据模型\n│ └── middleware/ # 中间件\n├── pkg/ # 公共包\n├── config/ # 配置\n└── go.mod\n```\n\n---\n\n## Critical 漏洞\n\n### 1. SQL 注入\n\n```go\nimport (\n \"github.com/gin-gonic/gin\"\n \"gorm.io/gorm\"\n)\n\n// 危险: Raw 拼接\nfunc GetUser(c *gin.Context) {\n id := c.Param(\"id\")\n var user User\n db.Raw(\"SELECT * FROM users WHERE id = \" + id).Scan(&user)\n c.JSON(200, user)\n}\n\n// 危险: Where 字符串拼接\nfunc SearchUsers(c *gin.Context) {\n name := c.Query(\"name\")\n var users []User\n db.Where(\"name LIKE '%\" + name + \"%'\").Find(&users)\n c.JSON(200, users)\n}\n\n// 危险: Order 注入\nfunc ListUsers(c *gin.Context) {\n order := c.Query(\"order\") // 用户控制排序\n var users []User\n db.Order(order).Find(&users) // ORDER BY 注入\n c.JSON(200, users)\n}\n// payload: ?order=id; DROP TABLE users;--\n\n// 安全: 参数化查询\nfunc GetUser(c *gin.Context) {\n id := c.Param(\"id\")\n var user User\n db.Raw(\"SELECT * FROM users WHERE id = ?\", id).Scan(&user)\n c.JSON(200, user)\n}\n\n// 安全: ORM 方法\nfunc SearchUsers(c *gin.Context) {\n name := c.Query(\"name\")\n var users []User\n db.Where(\"name LIKE ?\", \"%\"+name+\"%\").Find(&users)\n c.JSON(200, users)\n}\n\n// 安全: Order 白名单\nvar allowedOrders = map[string]bool{\n \"id\": true,\n \"created_at\": true,\n \"name\": true,\n}\n\nfunc ListUsers(c *gin.Context) {\n order := c.DefaultQuery(\"order\", \"id\")\n if !allowedOrders[order] {\n order = \"id\"\n }\n var users []User\n db.Order(order).Find(&users)\n c.JSON(200, users)\n}\n```\n\n### 2. 命令注入\n\n```go\nimport \"os/exec\"\n\n// 危险: shell 执行\nfunc RunCommand(c *gin.Context) {\n cmd := c.Query(\"cmd\")\n out, _ := exec.Command(\"sh\", \"-c\", cmd).Output()\n c.String(200, string(out))\n}\n\n// 危险: 参数拼接\nfunc Ping(c *gin.Context) {\n host := c.Query(\"host\")\n cmd := exec.Command(\"sh\", \"-c\", \"ping -c 4 \"+host)\n out, _ := cmd.Output()\n c.String(200, string(out))\n}\n// payload: ?host=127.0.0.1;id\n\n// 安全: 直接执行,无 shell\nfunc Ping(c *gin.Context) {\n host := c.Query(\"host\")\n // 验证 host 格式\n if !isValidHost(host) {\n c.String(400, \"Invalid host\")\n return\n }\n cmd := exec.Command(\"ping\", \"-c\", \"4\", host)\n out, _ := cmd.Output()\n c.String(200, string(out))\n}\n```\n\n### 3. 路径遍历\n\n```go\n// 危险: 直接拼接路径\nfunc DownloadFile(c *gin.Context) {\n filename := c.Query(\"file\")\n c.File(\"/uploads/\" + filename)\n}\n// payload: ?file=../../../etc/passwd\n\n// 安全: 路径验证\nfunc DownloadFile(c *gin.Context) {\n filename := c.Query(\"file\")\n\n // 清理路径\n cleanName := filepath.Clean(filename)\n\n // 防止目录遍历\n if strings.Contains(cleanName, \"..\") {\n c.String(400, \"Invalid filename\")\n return\n }\n\n fullPath := filepath.Join(\"/uploads\", cleanName)\n\n // 验证在允许目录内\n if !strings.HasPrefix(fullPath, \"/uploads/\") {\n c.String(403, \"Access denied\")\n return\n }\n\n c.File(fullPath)\n}\n```\n\n---\n\n## High 漏洞\n\n### 4. SSRF\n\n```go\nimport \"net/http\"\n\n// 危险: 未验证 URL\nfunc FetchURL(c *gin.Context) {\n url := c.Query(\"url\")\n resp, _ := http.Get(url)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n c.String(200, string(body))\n}\n// payload: ?url=http://169.254.169.254/latest/meta-data/\n\n// 安全: URL 白名单\nvar allowedHosts = map[string]bool{\n \"api.example.com\": true,\n}\n\nfunc FetchURL(c *gin.Context) {\n rawURL := c.Query(\"url\")\n\n parsedURL, err := url.Parse(rawURL)\n if err != nil {\n c.String(400, \"Invalid URL\")\n return\n }\n\n if !allowedHosts[parsedURL.Host] {\n c.String(403, \"Host not allowed\")\n return\n }\n\n // 检查内网 IP\n if isPrivateIP(parsedURL.Host) {\n c.String(403, \"Private IP not allowed\")\n return\n }\n\n resp, _ := http.Get(rawURL)\n defer resp.Body.Close()\n body, _ := io.ReadAll(resp.Body)\n c.String(200, string(body))\n}\n```\n\n### 5. 身份认证绕过\n\n```go\n// 危险: JWT 密钥硬编码\nvar jwtSecret = []byte(\"secret\")\n\nfunc AuthMiddleware() gin.HandlerFunc {\n return func(c *gin.Context) {\n tokenString := c.GetHeader(\"Authorization\")\n token, _ := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {\n return jwtSecret, nil\n })\n // ...\n }\n}\n\n// 危险: 未验证签名算法\nfunc AuthMiddleware() gin.HandlerFunc {\n return func(c *gin.Context) {\n token, _ := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {\n // 未检查算法!\n return jwtSecret, nil\n })\n // ...\n }\n}\n// 攻击: 修改 alg 为 none\n\n// 安全: 验证算法 + 环境变量密钥\nvar jwtSecret = []byte(os.Getenv(\"JWT_SECRET\"))\n\nfunc AuthMiddleware() gin.HandlerFunc {\n return func(c *gin.Context) {\n tokenString := c.GetHeader(\"Authorization\")\n token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {\n // 验证签名算法\n if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {\n return nil, fmt.Errorf(\"unexpected signing method\")\n }\n return jwtSecret, nil\n })\n\n if err != nil || !token.Valid {\n c.AbortWithStatus(401)\n return\n }\n\n c.Next()\n }\n}\n```\n\n### 6. 越权访问 (IDOR)\n\n```go\n// 危险: 未验证资源所有权\nfunc GetOrder(c *gin.Context) {\n orderID := c.Param(\"id\")\n var order Order\n db.First(&order, orderID)\n c.JSON(200, order) // 任何人可访问任何订单\n}\n\n// 安全: 验证所有权\nfunc GetOrder(c *gin.Context) {\n orderID := c.Param(\"id\")\n userID := c.GetInt(\"user_id\") // 从 JWT 获取\n\n var order Order\n result := db.Where(\"id = ? AND user_id = ?\", orderID, userID).First(&order)\n if result.Error != nil {\n c.JSON(404, gin.H{\"error\": \"Order not found\"})\n return\n }\n c.JSON(200, order)\n}\n```\n\n---\n\n## Medium 漏洞\n\n### 7. XSS\n\n```go\n// 危险: 直接输出 HTML\nfunc Hello(c *gin.Context) {\n name := c.Query(\"name\")\n c.Writer.Write([]byte(\"Hello \" + name + \"
\"))\n}\n\n// 危险: template.HTML 不转义\nfunc Hello(c *gin.Context) {\n name := c.Query(\"name\")\n c.HTML(200, \"hello.tmpl\", gin.H{\n \"name\": template.HTML(name), // 不转义!\n })\n}\n\n// 安全: 使用模板自动转义\nfunc Hello(c *gin.Context) {\n name := c.Query(\"name\")\n c.HTML(200, \"hello.tmpl\", gin.H{\n \"name\": name, // 自动转义\n })\n}\n```\n\n### 8. CORS 配置错误\n\n```go\nimport \"github.com/gin-contrib/cors\"\n\n// 危险: 允许所有来源\nr.Use(cors.Default())\n\n// 更危险: 允许凭证 + 所有来源\nr.Use(cors.New(cors.Config{\n AllowOrigins: []string{\"*\"},\n AllowCredentials: true, // 危险组合!\n}))\n\n// 安全: 指定允许的来源\nr.Use(cors.New(cors.Config{\n AllowOrigins: []string{\"https://example.com\"},\n AllowMethods: []string{\"GET\", \"POST\"},\n AllowHeaders: []string{\"Origin\", \"Content-Type\", \"Authorization\"},\n AllowCredentials: true,\n MaxAge: 12 * time.Hour,\n}))\n```\n\n### 9. 信任代理头\n\n```go\n// 危险: 默认信任所有代理头\nr := gin.Default()\n// 默认会读取 X-Forwarded-For\n\n// 可能导致 IP 欺骗\nfunc GetClientIP(c *gin.Context) {\n ip := c.ClientIP() // 可能被伪造\n // ...\n}\n\n// 安全: 配置信任的代理\nr.SetTrustedProxies([]string{\"192.168.1.0/24\", \"10.0.0.0/8\"})\n\n// 或禁用代理头\nr.SetTrustedProxies(nil)\n```\n\n### 10. 参数绑定漏洞\n\n```go\n// 危险: 自动绑定所有字段\ntype User struct {\n ID uint `json:\"id\"`\n Name string `json:\"name\"`\n IsAdmin bool `json:\"is_admin\"`\n}\n\nfunc CreateUser(c *gin.Context) {\n var user User\n c.BindJSON(&user) // 用户可设置 is_admin=true\n db.Create(&user)\n}\n\n// 安全: 使用 DTO\ntype CreateUserDTO struct {\n Name string `json:\"name\" binding:\"required\"`\n}\n\nfunc CreateUser(c *gin.Context) {\n var dto CreateUserDTO\n if err := c.ShouldBindJSON(&dto); err != nil {\n c.JSON(400, gin.H{\"error\": err.Error()})\n return\n }\n\n user := User{Name: dto.Name, IsAdmin: false}\n db.Create(&user)\n}\n```\n\n---\n\n## Gin 中间件安全\n\n### 速率限制\n\n```go\nimport \"github.com/ulule/limiter/v3/drivers/middleware/gin\"\n\n// 危险: 无速率限制\nr.POST(\"/login\", loginHandler)\n\n// 安全: 添加速率限制\nstore := memory.NewStore()\nrate := limiter.Rate{\n Period: 1 * time.Minute,\n Limit: 10,\n}\nmiddleware := mgin.NewMiddleware(limiter.New(store, rate))\nr.POST(\"/login\", middleware, loginHandler)\n```\n\n### 日志安全\n\n```go\n// 危险: 记录敏感信息\nfunc Login(c *gin.Context) {\n password := c.PostForm(\"password\")\n log.Printf(\"Login attempt with password: %s\", password) // 不要!\n}\n\n// 安全: 脱敏日志\nfunc Login(c *gin.Context) {\n username := c.PostForm(\"username\")\n log.Printf(\"Login attempt for user: %s\", username)\n}\n```\n\n### 错误处理\n\n```go\n// 危险: 暴露内部错误\nfunc GetUser(c *gin.Context) {\n var user User\n if err := db.First(&user).Error; err != nil {\n c.JSON(500, gin.H{\"error\": err.Error()}) // 暴露数据库信息\n return\n }\n c.JSON(200, user)\n}\n\n// 安全: 通用错误消息\nfunc GetUser(c *gin.Context) {\n var user User\n if err := db.First(&user).Error; err != nil {\n log.Printf(\"Database error: %v\", err) // 内部日志\n c.JSON(500, gin.H{\"error\": \"Internal server error\"})\n return\n }\n c.JSON(200, user)\n}\n```\n\n---\n\n## 检测命令\n\n```bash\n# SQL 注入\ngrep -rn \"\\.Raw\\|\\.Where\\|\\.Order\" --include=\"*.go\" | grep -E '\"\\s*\\+|\\+\\s*\"'\n\n# 命令执行\ngrep -rn \"exec\\.Command\" --include=\"*.go\"\n\n# 路径操作\ngrep -rn \"c\\.File\\|filepath\\.Join\" --include=\"*.go\"\n\n# JWT 密钥\ngrep -rn \"jwt\\|secret\\|Secret\" --include=\"*.go\"\n\n# CORS 配置\ngrep -rn \"cors\\.\" --include=\"*.go\"\n\n# 绑定操作\ngrep -rn \"c\\.Bind\\|c\\.ShouldBind\" --include=\"*.go\"\n\n# 静态分析\ngosec ./...\nstaticcheck ./...\n```\n\n---\n\n## 审计清单\n\n```\n[ ] 检查 SQL 查询是否使用参数化\n[ ] 检查命令执行是否有注入风险\n[ ] 检查文件操作是否有路径遍历\n[ ] 检查 HTTP 请求是否有 SSRF\n[ ] 检查 JWT 配置 (密钥、算法)\n[ ] 检查 IDOR 漏洞\n[ ] 检查 XSS 防护\n[ ] 检查 CORS 配置\n[ ] 检查代理头信任配置\n[ ] 检查参数绑定是否安全\n[ ] 检查速率限制\n[ ] 检查错误处理\n[ ] 运行 gosec 静态分析\n```\n\n---\n\n## 最小 PoC 示例\n```bash\n# SQL 注入\ncurl \"http://localhost:8080/users?id=1' OR '1'='1\"\n\n# SSRF\ncurl \"http://localhost:8080/fetch?url=http://169.254.169.254/latest/meta-data/\"\n\n# 路径遍历\ncurl \"http://localhost:8080/download?file=../../etc/passwd\"\n```\n\n---\n\n**最后更新**: 2026-01-23\n**版本**: 1.0.0\n"
const SKILL_FILE_8 = "# Java Web框架安全检测模块\r\n\r\n> 基于Spring MVC、Shiro等框架的安全特性分析\r\n> 针对框架特性的安全风险检测\r\n\r\n## 🔍 Spring MVC安全检测\r\n\r\n### 参数绑定风险检测\r\n\r\n#### 风险模式1: @PathVariable直接用于敏感操作\r\n```java\r\n// ❌ 高危: PathVariable直接用于数据库查询\r\n@GetMapping(\"/genCode/{tableName}\")\r\npublic void genCode(HttpServletResponse response, @PathVariable(\"tableName\") String tableName) {\r\n byte[] data = genService.generatorCode(tableName); // ❌ 直接使用\r\n // ...\r\n}\r\n\r\n// 检测正则\r\n- \"@PathVariable.*String.*tableName\"\r\n- \"@PathVariable.*String.*fileName\"\r\n- \"@PathVariable.*String.*resource\"\r\n```\r\n\r\n#### 风险模式2: 自动参数绑定风险\r\n```java\r\n// ❌ 中危: 自动参数绑定用于文件操作\r\n@RequestMapping(\"common/download\")\r\npublic void fileDownload(String fileName, Boolean delete, HttpServletResponse response) {\r\n String filePath = Global.getDownloadPath() + fileName; // ❌ 路径拼接\r\n FileUtils.writeBytes(filePath, response.getOutputStream());\r\n}\r\n\r\n// 检测正则\r\n- \"public.*\\(String.*fileName\"\r\n- \"public.*\\(String.*path\"\r\n- \"public.*\\(String.*resource\"\r\n```\r\n\r\n### 响应处理风险检测\r\n\r\n#### 风险模式3: 响应头注入\r\n```java\r\n// ❌ 中危: 用户输入用于响应头\r\npublic String setFileDownloadHeader(HttpServletRequest request, String fileName) {\r\n // ...\r\n response.setHeader(\"Content-Disposition\", \"attachment;fileName=\" + fileName);\r\n}\r\n\r\n// 检测正则\r\n- \"setHeader.*\\+.*fileName\"\r\n- \"addHeader.*\\+.*userInput\"\r\n```\r\n\r\n## 🔍 Shiro安全检测\r\n\r\n### 权限配置风险检测\r\n\r\n#### 风险模式4: 匿名访问配置不当\r\n```java\r\n// ❌ 高危: 敏感接口配置为匿名访问\r\nfilterChainDefinitionMap.put(\"/common/download\", \"anon\"); // ❌ 文件下载应认证\r\n\r\n// 检测正则\r\n- \"filterChainDefinitionMap\\.put.*download.*anon\"\r\n- \"filterChainDefinitionMap\\.put.*upload.*anon\"\r\n```\r\n\r\n#### 风险模式5: Remember Me配置风险\r\n```java\r\n// ❌ 中危: 硬编码加密密钥\r\ncookieRememberMeManager.setCipherKey(Base64.decode(\"fCq+/xW488hMTCD+cmJ3aQ==\"));\r\n\r\n// 检测正则\r\n- \"setCipherKey.*Base64\\.decode\"\r\n- \"rememberMe.*硬编码\"\r\n```\r\n\r\n## 🔍 MyBatis安全检测\r\n\r\n### SQL注入风险检测\r\n\r\n#### 风险模式6: 数据范围过滤注入\r\n```xml\r\n\r\n\r\n\r\n// 检测正则\r\n- \"\\\\$\\\\{params\\\\.dataScope\\\\}\"\r\n- \"\\\\$\\\\{.*dataScope.*\\\\}\"\r\n```\r\n\r\n#### 风险模式7: 动态SQL拼接风险\r\n```xml\r\n\r\n\r\n\r\n// 检测正则\r\n- \"ORDER BY\\\\s*\\\\$\\\\{\"\r\n- \"GROUP BY\\\\s*\\\\$\\\\{\"\r\n```\r\n\r\n## 🛡️ 安全修复方案\r\n\r\n### Spring MVC安全修复\r\n\r\n#### 修复方案1: 参数白名单验证\r\n```java\r\n// ✓ 安全: PathVariable白名单验证\r\n@GetMapping(\"/genCode/{tableName}\")\r\npublic void genCode(HttpServletResponse response, @PathVariable(\"tableName\") String tableName) {\r\n if (!isValidTableName(tableName)) {\r\n throw new SecurityException(\"Invalid table name\");\r\n }\r\n byte[] data = genService.generatorCode(tableName);\r\n}\r\n\r\nprivate boolean isValidTableName(String name) {\r\n return name.matches(\"[a-zA-Z0-9_]+\"); // ✓ 白名单验证\r\n}\r\n```\r\n\r\n#### 修复方案2: 路径规范化\r\n```java\r\n// ✓ 安全: 路径规范化处理\r\n@RequestMapping(\"common/download\")\r\npublic void fileDownload(String fileName, Boolean delete, HttpServletResponse response) {\r\n // 路径规范化\r\n Path basePath = Paths.get(Global.getDownloadPath()).normalize();\r\n Path filePath = basePath.resolve(fileName).normalize();\r\n\r\n // 安全检查\r\n if (!filePath.startsWith(basePath)) {\r\n throw new SecurityException(\"Invalid file path\");\r\n }\r\n\r\n FileUtils.writeBytes(filePath.toString(), response.getOutputStream());\r\n}\r\n```\r\n\r\n### MyBatis安全修复\r\n\r\n#### 修复方案3: 参数化数据范围\r\n```xml\r\n\r\n\r\n```\r\n\r\n#### 修复方案4: 业务逻辑层过滤\r\n```java\r\n// ✓ 安全: 在业务逻辑层处理数据范围\r\n@DataScope(tableAlias = \"u\")\r\npublic List selectUserList(SysUser user) {\r\n // 在AOP切面中安全构造数据范围条件\r\n return userMapper.selectUserList(user);\r\n}\r\n```\r\n\r\n## 🔧 检测命令集\r\n\r\n### Spring MVC检测命令\r\n```bash\r\n# 1. 扫描所有控制器方法\r\ngrep -rn \"@.*Mapping\" --include=\"*.java\" | head -20\r\n\r\n# 2. 检查PathVariables\r\ngrep -rn \"@PathVariable\" --include=\"*.java\" -A 2\r\n\r\n# 3. 检查文件下载接口\r\ngrep -rn \"download\\|Download\" --include=\"*.java\" | grep -E \"Mapping|RequestMapping\"\r\n\r\n# 4. 检查响应头设置\r\ngrep -rn \"setHeader\\|addHeader\" --include=\"*.java\" | grep -v \"安全\"\r\n```\r\n\r\n### Shiro检测命令\r\n```bash\r\n# 1. 检查Shiro配置\r\ngrep -rn \"ShiroConfig\" --include=\"*.java\" -A 50\r\n\r\n# 2. 检查过滤器链配置\r\ngrep -rn \"filterChainDefinitionMap\" --include=\"*.java\" -A 20\r\n\r\n# 3. 检查Remember Me配置\r\ngrep -rn \"rememberMe\\|setCipherKey\" --include=\"*.java\"\r\n```\r\n\r\n### MyBatis检测命令\r\n```bash\r\n# 1. 检查${}使用\r\ngrep -rn \"\\\\$\\\\{\" --include=\"*.xml\" | grep -v \"pom.xml\"\r\n\r\n# 2. 检查数据范围注解\r\ngrep -rn \"@DataScope\" --include=\"*.java\" -B 2 -A 2\r\n\r\n# 3. 检查动态SQL\r\ngrep -rn \"ORDER BY.*\\\\$\\|GROUP BY.*\\\\$\" --include=\"*.xml\"\r\n```\r\n\r\n## 📊 风险评级矩阵\r\n\r\n| 风险类型 | 严重性 | 利用难度 | 检测难度 | 修复优先级 |\r\n|----------|--------|----------|----------|------------|\r\n| @PathVariable注入 | 🔴 高危 | 低 | 中 | 立即修复 |\r\n| 路径遍历下载 | 🔴 高危 | 低 | 中 | 立即修复 |\r\n| 数据范围SQL注入 | 🔴 高危 | 中 | 高 | 立即修复 |\r\n| 响应头注入 | 🟡 中危 | 中 | 中 | 计划修复 |\r\n| Remember Me硬编码 | 🟡 中危 | 高 | 低 | 计划修复 |\r\n| 匿名访问配置 | 🟡 中危 | 低 | 低 | 计划修复 |\n\n## ⚠️ 框架特性注意事项\n\n1. **Spring Boot自动配置**: 注意默认安全配置是否足够\n2. **Shiro权限继承**: 检查权限继承关系是否正确\n3. **MyBatis插件**: 验证安全插件是否启用\n4. **AOP切面顺序**: 检查安全切面的执行顺序\n\n---\n\n## 最小 PoC 示例\n```bash\n# 路径遍历\ncurl \"http://localhost:8080/common/download?fileName=../../../../etc/passwd\"\n\n# 权限绕过 (匿名配置)\ncurl -I \"http://localhost:8080/admin\"\n\n# 数据范围 SQL 注入\ncurl \"http://localhost:8080/system/user/list?dataScope=1 or 1=1\"\n```\n\r\n通过本模块的检测规则,能够有效识别Java Web框架中的安全风险,特别是框架特性和配置相关的隐蔽漏洞。\n"
const SKILL_FILE_9 = "# Koa Security Audit Guide\n\n> Koa.js Framework 安全审计模块\n> 适用于: Koa 2.x, Koa-Router, Koa-Body\n\n## 核心危险面\n\nKoa 作为 Express 的精简继任者,采用中间件级联架构和 async/await 语法。主要安全风险:中间件顺序错误、请求体解析漏洞、不当的错误处理、原型污染、路径遍历、NoSQL注入等。\n\n---\n\n## 中间件顺序和安全配置\n\n```javascript\n// 危险配置 - 中间件顺序错误\nconst app = new Koa();\n\napp.use(router.routes()); // ❌ 路由在安全中间件之前\napp.use(helmet()); // ❌ 太晚了\n\n// 正确顺序\nconst app = new Koa();\n\n// 1. 错误处理 (最外层)\napp.use(async (ctx, next) => {\n try {\n await next();\n } catch (err) {\n ctx.status = err.status || 500;\n ctx.body = { error: 'Internal Server Error' }; // ✓ 不泄露详情\n ctx.app.emit('error', err, ctx);\n }\n});\n\n// 2. 安全头\napp.use(helmet()); // ✓\n\n// 3. CORS\napp.use(cors({\n origin: 'https://app.example.com', // ✓ 具体域名\n credentials: true\n}));\n\n// 4. 请求体解析\napp.use(bodyParser({\n jsonLimit: '1mb', // ✓ 限制大小\n formLimit: '1mb'\n}));\n\n// 5. 身份验证\napp.use(auth());\n\n// 6. 路由 (最后)\napp.use(router.routes());\napp.use(router.allowedMethods());\n```\n\n---\n\n## 请求体解析和原型污染\n\n```javascript\n// 危险配置 - koa-body / koa-bodyparser\napp.use(bodyParser()); // ❌ 默认配置可能不安全\n\n// 审计检查\n- jsonLimit / formLimit 配置\n- 是否过滤 __proto__\n- 是否验证 Content-Type\n\n// 原型污染示例\napp.use(bodyParser());\n\napp.post('/api/update', async (ctx) => {\n const data = ctx.request.body;\n Object.assign(config, data); // ❌ High: 原型污染\n ctx.body = { success: true };\n});\n\n// 攻击载荷\nPOST /api/update\nContent-Type: application/json\n\n{\n \"__proto__\": {\n \"isAdmin\": true,\n \"role\": \"admin\"\n }\n}\n\n// 安全修复\napp.use(bodyParser({\n jsonLimit: '1mb',\n formLimit: '1mb',\n textLimit: '1mb',\n enableTypes: ['json', 'form'], // ✓ 明确类型\n extendTypes: {\n json: ['application/json']\n }\n}));\n\napp.post('/api/update', async (ctx) => {\n const data = ctx.request.body;\n\n // 1. 过滤危险键\n const dangerousKeys = ['__proto__', 'constructor', 'prototype'];\n for (const key of dangerousKeys) {\n if (key in data) {\n ctx.throw(400, 'Invalid key');\n }\n }\n\n // 2. 使用白名单\n const allowedKeys = ['name', 'email', 'bio'];\n const safeData = {};\n for (const key of allowedKeys) {\n if (data.hasOwnProperty(key)) {\n safeData[key] = data[key];\n }\n }\n\n Object.assign(config, safeData); // ✓\n ctx.body = { success: true };\n});\n```\n\n---\n\n## NoSQL 注入检测 (MongoDB)\n\n```javascript\n// 危险操作\napp.get('/user', async (ctx) => {\n const filter = ctx.query;\n const user = await User.findOne(filter); // ❌ High: NoSQL注入\n ctx.body = user;\n});\n\n// 攻击载荷\nGET /user?username[$ne]=null&password[$ne]=null // 绕过认证\nGET /user?$where=this.password.length<20 // $where注入\n\n// 审计正则\nfindOne\\(ctx\\.(query|request\\.body)\\)|find\\(ctx\\.(query|request\\.body)\\)\n\\$where|\\$regex\n\n// 安全修复\napp.get('/user', async (ctx) => {\n const { username } = ctx.query;\n\n // 1. 类型验证\n if (typeof username !== 'string') {\n ctx.throw(400, 'Invalid username type');\n }\n\n // 2. 禁止操作符\n if (username.startsWith('$')) {\n ctx.throw(400, 'Invalid username');\n }\n\n // 3. 明确字段\n const user = await User.findOne({ username: username }); // ✓\n\n ctx.body = user;\n});\n\n// 高级防护 - 递归清理\nfunction sanitizeQuery(obj) {\n if (typeof obj !== 'object' || obj === null) {\n return obj;\n }\n\n for (const key in obj) {\n // 删除操作符键\n if (key.startsWith('$')) {\n delete obj[key];\n continue;\n }\n\n // 递归清理\n if (typeof obj[key] === 'object') {\n obj[key] = sanitizeQuery(obj[key]);\n }\n }\n\n return obj;\n}\n\napp.use(async (ctx, next) => {\n ctx.query = sanitizeQuery(ctx.query); // ✓ 全局清理\n if (ctx.request.body) {\n ctx.request.body = sanitizeQuery(ctx.request.body);\n }\n await next();\n});\n```\n\n---\n\n## SSRF 检测\n\n```javascript\n// 危险操作\nconst axios = require('axios');\n\napp.get('/fetch', async (ctx) => {\n const url = ctx.query.url;\n const response = await axios.get(url); // ❌ High: SSRF\n ctx.body = response.data;\n});\n\n// 攻击载荷\nGET /fetch?url=http://169.254.169.254/latest/meta-data/\nGET /fetch?url=http://localhost:6379/\nGET /fetch?url=file:///etc/passwd\n\n// 审计正则\naxios\\.(get|post).*ctx\\.query|got\\(ctx\\.query|fetch\\(ctx\\.query\n\n// 安全修复\nconst { URL } = require('url');\nconst dns = require('dns').promises;\n\napp.get('/fetch', async (ctx) => {\n const urlString = ctx.query.url;\n\n try {\n const url = new URL(urlString);\n\n // 1. 协议白名单\n if (!['http:', 'https:'].includes(url.protocol)) {\n ctx.throw(403, 'Invalid protocol');\n }\n\n // 2. 主机白名单\n const allowedHosts = ['api.example.com', 'cdn.example.com'];\n if (!allowedHosts.includes(url.hostname)) {\n ctx.throw(403, 'Host not allowed');\n }\n\n // 3. DNS解析检查内网IP\n const addresses = await dns.resolve4(url.hostname);\n for (const addr of addresses) {\n if (isPrivateIP(addr)) {\n ctx.throw(403, 'Internal IP not allowed');\n }\n }\n\n // 4. 超时和大小限制\n const response = await axios.get(url.href, {\n timeout: 5000,\n maxContentLength: 1024 * 1024 // 1MB\n });\n\n ctx.body = response.data;\n } catch (err) {\n ctx.throw(400, 'Invalid request');\n }\n});\n\n// IP检查函数\nfunction isPrivateIP(ip) {\n const parts = ip.split('.').map(Number);\n return (\n parts[0] === 10 ||\n parts[0] === 127 ||\n (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||\n (parts[0] === 192 && parts[1] === 168) ||\n parts[0] === 169 && parts[1] === 254\n );\n}\n```\n\n---\n\n## 路径遍历检测\n\n```javascript\n// 危险操作\nconst fs = require('fs').promises;\nconst path = require('path');\n\napp.get('/download', async (ctx) => {\n const filename = ctx.query.file;\n const filePath = path.join(__dirname, 'files', filename);\n ctx.body = await fs.readFile(filePath); // ❌ High: 路径遍历\n});\n\n// 攻击载荷\nGET /download?file=../../etc/passwd\nGET /download?file=..%2f..%2f..%2fetc%2fpasswd\n\n// 审计正则\nfs\\.readFile.*ctx\\.query|fs\\.createReadStream.*ctx\\.query\npath\\.join.*ctx\\.(query|params)\n\n// 安全修复\napp.get('/download', async (ctx) => {\n const filename = ctx.query.file;\n\n // 1. 基本验证\n if (!filename || filename.includes('..') || filename.includes('/')) {\n ctx.throw(400, 'Invalid filename');\n }\n\n // 2. 扩展名白名单\n const ext = path.extname(filename).toLowerCase();\n const allowedExts = ['.pdf', '.jpg', '.png', '.txt'];\n if (!allowedExts.includes(ext)) {\n ctx.throw(403, 'File type not allowed');\n }\n\n // 3. 路径规范化和验证\n const baseDir = path.join(__dirname, 'files');\n const filePath = path.normalize(path.join(baseDir, filename));\n\n // 确保最终路径在 baseDir 内\n if (!filePath.startsWith(baseDir + path.sep)) {\n ctx.throw(403, 'Access denied');\n }\n\n // 4. 检查文件存在\n try {\n await fs.access(filePath, fs.constants.R_OK);\n } catch {\n ctx.throw(404, 'File not found');\n }\n\n // 5. 设置安全头\n ctx.set('Content-Disposition', `attachment; filename=\"${path.basename(filePath)}\"`);\n ctx.set('X-Content-Type-Options', 'nosniff');\n\n ctx.body = await fs.readFile(filePath); // ✓\n});\n```\n\n---\n\n## 命令注入检测\n\n```javascript\n// 危险操作\nconst { exec } = require('child_process');\nconst util = require('util');\nconst execPromise = util.promisify(exec);\n\napp.get('/ping', async (ctx) => {\n const host = ctx.query.host;\n const { stdout } = await execPromise(`ping -c 4 ${host}`); // ❌ Critical\n ctx.body = stdout;\n});\n\n// 攻击载荷\nGET /ping?host=google.com;whoami\nGET /ping?host=`cat /etc/passwd`\n\n// 审计正则\nexec\\(.*ctx\\.|spawn\\(.*ctx\\.|execSync\\(.*ctx\\.\n\n// 安全修复\nconst { execFile } = require('child_process');\nconst execFilePromise = util.promisify(execFile);\n\napp.get('/ping', async (ctx) => {\n const host = ctx.query.host;\n\n // 1. 验证格式\n if (!/^[a-zA-Z0-9.-]+$/.test(host)) {\n ctx.throw(400, 'Invalid hostname');\n }\n\n // 2. 使用 execFile (不启动shell)\n try {\n const { stdout } = await execFilePromise('ping', ['-c', '4', host], {\n timeout: 10000\n }); // ✓\n ctx.body = stdout;\n } catch (err) {\n ctx.throw(500, 'Ping failed');\n }\n});\n\n// 更安全: 避免命令执行,使用原生库\nconst ping = require('ping');\n\napp.get('/ping', async (ctx) => {\n const host = ctx.query.host;\n const result = await ping.promise.probe(host); // ✓ 纯JavaScript实现\n ctx.body = result;\n});\n```\n\n---\n\n## JWT 安全检测\n\n```javascript\n// 危险操作\nconst jwt = require('jsonwebtoken');\n\napp.use(async (ctx, next) => {\n const token = ctx.headers.authorization;\n const decoded = jwt.decode(token); // ❌ High: 未验证签名\n ctx.state.user = decoded;\n await next();\n});\n\n// 弱密钥\nconst SECRET = 'secret'; // ❌ Critical\n\n// none 算法\njwt.sign(payload, null, { algorithm: 'none' }); // ❌ Critical\n\n// 审计正则\njwt\\.decode\\((?!.*verify)|JWT_SECRET.*=.*['\"][^'\"]{1,16}|algorithm.*none\n\n// 安全修复\napp.use(async (ctx, next) => {\n const authHeader = ctx.headers.authorization;\n\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n ctx.throw(401, 'Missing token');\n }\n\n const token = authHeader.substring(7);\n\n try {\n const decoded = jwt.verify(token, process.env.JWT_SECRET, {\n algorithms: ['HS256'] // ✓ 明确算法\n });\n ctx.state.user = decoded;\n } catch (err) {\n ctx.throw(401, 'Invalid token');\n }\n\n await next();\n});\n\n// 密钥管理\n// .env\nJWT_SECRET=at_least_32_bytes_random_string_here\n\n// 生成强密钥\nconst crypto = require('crypto');\nconst secret = crypto.randomBytes(32).toString('hex');\n```\n\n---\n\n## CORS 配置检测\n\n```javascript\n// 危险配置\nconst cors = require('@koa/cors');\n\napp.use(cors({\n origin: '*', // ❌ High: 允许所有域\n credentials: true // ❌ 与 * 冲突\n}));\n\n// 审计正则\ncors\\(\\{[^}]*origin:.*\\*|credentials:\\s*true\n\n// 漏洞示例\napp.use(cors({\n origin: ctx => ctx.headers.origin // ❌ High: 反射Origin\n}));\n\n// 安全修复\n// 方法1: 静态白名单\napp.use(cors({\n origin: 'https://app.example.com', // ✓\n credentials: true,\n allowMethods: ['GET', 'POST'],\n allowHeaders: ['Content-Type', 'Authorization'],\n maxAge: 86400\n}));\n\n// 方法2: 动态验证\nconst allowedOrigins = [\n 'https://app.example.com',\n 'https://admin.example.com'\n];\n\napp.use(cors({\n origin: (ctx) => {\n const origin = ctx.headers.origin;\n if (allowedOrigins.includes(origin)) {\n return origin; // ✓\n }\n return false; // 拒绝\n },\n credentials: true\n}));\n\n// 方法3: 子域名模式\napp.use(cors({\n origin: (ctx) => {\n const origin = ctx.headers.origin;\n if (/^https:\\/\\/[a-z0-9-]+\\.example\\.com$/.test(origin)) {\n return origin; // ✓ 允许子域名\n }\n return false;\n },\n credentials: true\n}));\n```\n\n---\n\n## 错误处理和信息泄露\n\n```javascript\n// 危险配置\napp.on('error', (err, ctx) => {\n console.error(err.stack); // ❌ 仅记录\n});\n\n// 默认错误处理\napp.use(async (ctx) => {\n throw new Error('Database connection failed: user=admin, pass=secret123');\n // ❌ Medium: 错误可能泄露到客户端\n});\n\n// 审计正则\nthrow new Error.*password|console\\.error.*stack|ctx\\.body.*err\\.stack\n\n// 安全修复\n// 全局错误处理中间件\napp.use(async (ctx, next) => {\n try {\n await next();\n } catch (err) {\n ctx.status = err.status || err.statusCode || 500;\n\n // 后端记录完整错误\n console.error({\n error: err.message,\n stack: err.stack,\n url: ctx.url,\n method: ctx.method,\n ip: ctx.ip,\n user: ctx.state.user?.id\n });\n\n // 生产环境隐藏详情\n if (process.env.NODE_ENV === 'production') {\n ctx.body = {\n error: 'An error occurred',\n code: err.code || 'INTERNAL_ERROR'\n }; // ✓\n } else {\n ctx.body = {\n error: err.message,\n stack: err.stack // 仅开发环境\n };\n }\n\n // 触发应用级错误事件\n ctx.app.emit('error', err, ctx);\n }\n});\n\n// 日志脱敏\nconst logger = require('pino')();\n\napp.use(async (ctx, next) => {\n const sanitized = { ...ctx.request.body };\n delete sanitized.password;\n delete sanitized.token;\n logger.info({ body: sanitized }, 'Request received'); // ✓\n await next();\n});\n```\n\n---\n\n## CSRF 保护\n\n```javascript\n// Koa 没有内置 CSRF 保护,需要手动实现\nconst csrf = require('koa-csrf');\n\n// 危险: 未启用 CSRF\napp.use(router.routes()); // ❌ 状态改变操作无 CSRF 保护\n\n// 安全配置\napp.use(new csrf({\n invalidTokenMessage: 'Invalid CSRF token',\n invalidTokenStatusCode: 403,\n excludedMethods: ['GET', 'HEAD', 'OPTIONS'], // ✓ 仅豁免安全方法\n disableQuery: false\n}));\n\n// 在表单中包含 CSRF token\napp.use(async (ctx) => {\n await ctx.render('form', {\n csrfToken: ctx.csrf // ✓\n });\n});\n\n// HTML\n\n\n// AJAX 请求\naxios.defaults.headers.common['X-CSRF-Token'] = csrfToken;\n```\n\n---\n\n## 开放重定向检测\n\n```javascript\n// 危险操作\napp.get('/redirect', async (ctx) => {\n const url = ctx.query.url;\n ctx.redirect(url); // ❌ Medium: 开放重定向\n});\n\n// 攻击载荷\nGET /redirect?url=https://evil.com/phishing\n\n// 审计正则\nctx\\.redirect\\(ctx\\.(query|request\\.body)\n\n// 安全修复\napp.get('/redirect', async (ctx) => {\n const url = ctx.query.url;\n\n // 方法1: 白名单\n const allowedUrls = ['/home', '/dashboard', '/profile'];\n if (!allowedUrls.includes(url)) {\n ctx.redirect('/'); // 默认安全页面\n return;\n }\n\n // 方法2: 验证相对路径\n if (!url.startsWith('/') || url.startsWith('//')) {\n ctx.throw(400, 'Invalid redirect URL');\n }\n\n // 方法3: 验证域名\n try {\n const parsed = new URL(url, 'http://example.com');\n if (parsed.hostname !== 'example.com') {\n ctx.throw(403, 'External redirect not allowed');\n }\n } catch {\n ctx.throw(400, 'Invalid URL');\n }\n\n ctx.redirect(url); // ✓\n});\n```\n\n---\n\n## 速率限制\n\n```javascript\n// 缺少速率限制\napp.post('/login', async (ctx) => {\n // ❌ 无限制登录尝试\n});\n\n// 安全配置\nconst ratelimit = require('koa-ratelimit');\nconst Redis = require('ioredis');\n\nconst db = new Redis();\n\napp.use(ratelimit({\n driver: 'redis',\n db: db,\n duration: 60000, // 1分钟\n errorMessage: 'Too many requests',\n id: (ctx) => ctx.ip,\n headers: {\n remaining: 'Rate-Limit-Remaining',\n reset: 'Rate-Limit-Reset',\n total: 'Rate-Limit-Total'\n },\n max: 100, // 每分钟100个请求\n disableHeader: false\n}));\n\n// 针对特定路由\nconst loginLimiter = ratelimit({\n driver: 'redis',\n db: db,\n duration: 60000,\n max: 5, // 登录每分钟5次\n id: (ctx) => ctx.request.body.email || ctx.ip\n});\n\nrouter.post('/login', loginLimiter, async (ctx) => {\n // 登录逻辑\n});\n```\n\n---\n\n## 敏感信息和调试\n\n```javascript\n// 危险操作\napp.use(async (ctx) => {\n console.log(ctx.request.body); // ❌ 可能记录密码\n ctx.body = process.env; // ❌ Critical: 环境变量泄露\n});\n\n// .env 泄露\nDB_PASSWORD=secret123 // ❌ 如果代码泄露\n\n// 审计正则\nconsole\\.log.*password|ctx\\.body.*process\\.env|DB_PASSWORD=\n\n// 安全措施\n// .gitignore\n.env\n.env.local\n.env.*.local\n\n// 环境变量验证\nconst requiredEnvVars = ['DB_PASSWORD', 'JWT_SECRET', 'SESSION_SECRET'];\nfor (const varName of requiredEnvVars) {\n if (!process.env[varName]) {\n throw new Error(`Missing required env var: ${varName}`);\n }\n}\n\n// 日志脱敏\nconst sanitizedBody = { ...ctx.request.body };\ndelete sanitizedBody.password;\ndelete sanitizedBody.creditCard;\nlogger.info({ body: sanitizedBody }); // ✓\n```\n\n---\n\n## 搜索模式汇总\n\n```regex\n# 原型污染\nObject\\.assign.*ctx\\.request\\.body|_\\.merge.*ctx\\.request\\.body\n\n# NoSQL注入\nfindOne\\(ctx\\.query\\)|find\\(ctx\\.request\\.body\\)\n\n# SSRF\naxios\\.(get|post).*ctx\\.query|fetch\\(ctx\\.query\n\n# 路径遍历\nfs\\.readFile.*ctx\\.query|createReadStream.*ctx\\.query\n\n# 命令注入\nexec\\(.*ctx\\.|spawn\\(.*ctx\\.\n\n# JWT\njwt\\.decode\\((?!.*verify)|algorithm.*none\n\n# CORS\ncors.*origin:.*\\*\n\n# 开放重定向\nctx\\.redirect\\(ctx\\.query\n\n# 敏感信息\nconsole\\.log.*password|ctx\\.body.*process\\.env\n```\n\n---\n\n## 快速审计检查清单\n\n```markdown\n[ ] 检查 package.json 依赖漏洞 (npm audit)\n[ ] 检查中间件顺序 (安全中间件应在前)\n[ ] 搜索 Object.assign (原型污染)\n[ ] 检查 findOne/find 的参数来源 (NoSQL注入)\n[ ] 检查文件操作的路径拼接 (路径遍历)\n[ ] 检查 HTTP 请求函数 (SSRF)\n[ ] 检查 exec/spawn 命令执行\n[ ] 检查 JWT 验证逻辑\n[ ] 检查 CORS 配置\n[ ] 检查错误处理是否泄露信息\n[ ] 检查 CSRF 保护\n[ ] 检查重定向目标验证\n[ ] 检查登录/敏感接口的速率限制\n[ ] 检查 .env 是否在 .gitignore\n```\n\n---\n\n## 最小 PoC 示例\n```bash\n# 原型污染\nnode -e \"const a={}; Object.assign(a, JSON.parse('{\\\"__proto__\\\":{\\\"polluted\\\":true}}')); console.log(({}).polluted)\"\n\n# SSRF\ncurl \"http://localhost:3000/fetch?url=http://169.254.169.254/latest/meta-data/\"\n\n# 路径遍历\ncurl \"http://localhost:3000/download?file=../../etc/passwd\"\n```\n\n---\n\n## 参考资源\n\n- [Koa Official Documentation](https://koajs.com/)\n- [Koa Security Best Practices](https://github.com/koajs/koa/wiki/Error-Handling)\n- [Node.js Security Checklist](https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html)\n- [OWASP Top 10](https://owasp.org/www-project-top-ten/)\n"
const SKILL_FILE_10 = "# Laravel Security Audit Guide\n\n> Laravel Framework 安全审计模块\n> 适用于: Laravel 8.x/9.x/10.x/11.x\n\n## 核心危险面\n\nLaravel 作为 PHP 生态最流行的框架,其特有的 Eloquent ORM、Blade 模板引擎、路由系统和序列化机制带来独特的安全挑战:Mass Assignment、反序列化RCE、SQL注入、SSTI等。\n\n---\n\n## Mass Assignment 漏洞检测\n\n```php\n// 危险操作\nUser::create($request->all()); // ❌ High: 批量赋值攻击\n$user->fill($request->input()); // ❌ High\n$user->update($request->only(['name'])); // ❌ 如果未正确配置\n\n// 审计正则\n::create\\s*\\(\\s*\\$request->all\\(\\)|->fill\\s*\\(\\s*\\$request->|->update\\s*\\(\\s*\\$request->\n\n// 漏洞示例\npublic function updateProfile(Request $request) {\n $user = Auth::user();\n $user->update($request->all()); // ❌ Critical: 可修改任意字段\n return redirect('/profile');\n}\n\n// 攻击载荷\nPOST /profile\n{\n \"name\": \"Attacker\",\n \"is_admin\": true, // ← 提权\n \"role\": \"admin\",\n \"credits\": 999999\n}\n\n// 安全修复方法1: 使用 $fillable 白名单\nclass User extends Model {\n protected $fillable = ['name', 'email', 'bio']; // ✓ 仅允许这些字段\n // 或使用 $guarded 黑名单\n protected $guarded = ['id', 'is_admin', 'role']; // ✓ 禁止这些字段\n}\n\n// 安全修复方法2: 显式指定字段\npublic function updateProfile(Request $request) {\n $user = Auth::user();\n $user->update([\n 'name' => $request->input('name'),\n 'email' => $request->input('email'),\n ]); // ✓ 明确控制\n}\n\n// 安全修复方法3: 使用 validated()\npublic function updateProfile(UserRequest $request) {\n $user = Auth::user();\n $user->update($request->validated()); // ✓ 仅更新验证通过的字段\n}\n```\n\n---\n\n## SQL 注入检测\n\n```php\n// 危险操作 - Raw Queries\nDB::select(\"SELECT * FROM users WHERE id = \" . $id); // ❌ Critical\nDB::statement(\"DELETE FROM users WHERE role = '$role'\"); // ❌\n\n// 危险操作 - Query Builder\nDB::table('users')->whereRaw(\"name = '$name'\")->get(); // ❌\nUser::whereRaw(\"email = '$email'\")->first(); // ❌\n\n// 危险操作 - orderByRaw / groupByRaw\nUser::orderByRaw($request->input('sort'))->get(); // ❌\n\n// 审计正则\nDB::(select|statement|raw|delete|update|insert)\\s*\\(.*\\$|whereRaw.*\\$|orderByRaw.*\\$\nhavingRaw|groupByRaw\n\n// 漏洞示例\npublic function search(Request $request) {\n $keyword = $request->input('keyword');\n $users = DB::select(\"SELECT * FROM users WHERE name LIKE '%{$keyword}%'\"); // ❌\n return view('users', compact('users'));\n}\n\n// 攻击载荷\nGET /search?keyword=' OR '1'='1\nGET /search?keyword='; DROP TABLE users--\n\n// 安全修复 - 参数化查询\n$users = DB::select(\n \"SELECT * FROM users WHERE name LIKE ?\",\n ['%' . $keyword . '%']\n); // ✓\n\n// 使用 Query Builder (最佳)\n$users = User::where('name', 'like', '%' . $keyword . '%')->get(); // ✓\n\n// whereRaw 安全用法\nUser::whereRaw('YEAR(created_at) = ?', [$year])->get(); // ✓ 带参数绑定\n\n// orderBy 安全用法\n$allowedColumns = ['name', 'created_at', 'email'];\n$sort = $request->input('sort', 'name');\nif (!in_array($sort, $allowedColumns)) {\n $sort = 'name';\n}\nUser::orderBy($sort)->get(); // ✓ 白名单验证\n```\n\n---\n\n## Laravel 反序列化 RCE\n\n```php\n// 危险操作\nunserialize($request->cookie('user')); // ❌ Critical: 反序列化用户输入\nunserialize(base64_decode($data)); // ❌ Critical\nunserialize(file_get_contents($path)); // ❌ 如果路径可控\n\n// Laravel 特有的反序列化链\n// APP_KEY 泄露 → 伪造加密Cookie → RCE\n\n// 审计正则\nunserialize\\s*\\(|APP_KEY\\s*=|Illuminate\\\\.*__destruct\n\n// 漏洞场景1: APP_KEY 泄露\n// .env 文件泄露\nAPP_KEY=base64:YourSecretKeyHere // ❌ 如果泄露可伪造签名\n\n// 检查点\n- .env 是否在 .gitignore\n- 是否在公开仓库中\n- debug 模式是否泄露\n- /.env HTTP 访问是否被拦截\n\n// 漏洞场景2: 直接反序列化\npublic function processData(Request $request) {\n $data = $request->input('data');\n $object = unserialize(base64_decode($data)); // ❌ Critical\n return $object->process();\n}\n\n// Laravel POP链示例 (CVE-2021-3129)\n- Illuminate\\Broadcasting\\PendingBroadcast::__destruct()\n- Illuminate\\Events\\Dispatcher::dispatch()\n- Illuminate\\Bus\\Dispatcher::dispatchNow()\n- Illuminate\\Foundation\\Application::call()\n\n// 安全措施\n// 1. 保护 APP_KEY\nAPP_KEY=base64:$(openssl rand -base64 32) # 强随机密钥\n# 绝不提交到版本控制\n\n// 2. 避免反序列化用户输入\n$data = json_decode($request->input('data')); // ✓ 使用 JSON\n\n// 3. 禁止 .env 访问\n# .htaccess\n\n Order allow,deny\n Deny from all\n\n\n# nginx\nlocation ~ /\\.env {\n deny all;\n}\n\n// 4. 升级到安全版本\nLaravel 8.83.27+ / 9.5.1+\n```\n\n---\n\n## Blade 模板注入 (SSTI)\n\n```php\n// 危险操作\n{!! $userInput !!} // ❌ High: 不转义输出HTML\n@php echo $request->input('code'); @endphp // ❌ Critical: 执行PHP代码\nBlade::render($template) // ❌ 如果$template用户可控\n\n// 审计正则\n\\{\\!\\!.*\\$|@php\\s+.*\\$request|Blade::render.*\\$request\n\n// 漏洞示例1: 不转义输出\n\n {!! $user->bio !!} // ❌ High: XSS\n
\n\n// 攻击载荷\nPOST /profile\nbio=\n\n// 漏洞示例2: 动态模板\npublic function render(Request $request) {\n $template = $request->input('template');\n return Blade::render($template, ['user' => Auth::user()]); // ❌ Critical: SSTI\n}\n\n// 攻击载荷\nPOST /render\ntemplate={{ system('id') }}\ntemplate=@php system($_GET['cmd']); @endphp\n\n// 安全修复\n// 使用 {{ }} 自动转义\n\n {{ $user->bio }} // ✓ HTML转义\n
\n\n// 如果必须输出HTML,使用 Purifier\n\n {!! clean($user->bio) !!} // ✓ 使用 HTMLPurifier\n
\n\n// 禁止用户控制模板\n// 使用预定义模板列表\n$allowedTemplates = ['profile', 'dashboard'];\n$template = $request->input('template', 'profile');\nif (!in_array($template, $allowedTemplates)) {\n abort(400);\n}\nreturn view($template); // ✓\n```\n\n---\n\n## 路径遍历和文件操作\n\n```php\n// 危险操作\nStorage::get($request->input('file')); // ❌ High: 路径遍历\nfile_get_contents($path); // ❌ 如果$path用户可控\nFile::delete($request->input('path')); // ❌ Critical: 任意文件删除\ninclude($request->input('page') . '.php'); // ❌ Critical: 文件包含\n\n// 审计正则\nStorage::(get|put|delete).*\\$request|file_get_contents.*\\$request\nFile::(get|put|delete).*\\$request|include.*\\$request|require.*\\$request\n\n// 漏洞示例\npublic function download(Request $request) {\n $filename = $request->input('file');\n $path = storage_path('app/files/' . $filename);\n return response()->download($path); // ❌ High: 路径遍历\n}\n\n// 攻击载荷\nGET /download?file=../../.env\nGET /download?file=../../../etc/passwd\n\n// 安全修复\npublic function download(Request $request) {\n $filename = $request->input('file');\n\n // 1. 验证文件名\n if (strpos($filename, '..') !== false || strpos($filename, '/') !== false) {\n abort(403);\n }\n\n // 2. 白名单扩展名\n $ext = pathinfo($filename, PATHINFO_EXTENSION);\n if (!in_array($ext, ['pdf', 'jpg', 'png'])) {\n abort(403);\n }\n\n // 3. 使用真实路径验证\n $basePath = storage_path('app/files');\n $fullPath = realpath($basePath . '/' . $filename);\n\n if (!$fullPath || strpos($fullPath, $basePath) !== 0) {\n abort(403);\n }\n\n // 4. 检查文件存在\n if (!Storage::disk('files')->exists($filename)) {\n abort(404);\n }\n\n return Storage::disk('files')->download($filename); // ✓\n}\n\n// 文件包含安全化\n$allowedPages = ['home', 'about', 'contact'];\n$page = $request->input('page', 'home');\nif (!in_array($page, $allowedPages)) {\n abort(404);\n}\nreturn view($page); // ✓ 使用 view() 而非 include\n```\n\n---\n\n## SSRF 检测\n\n```php\n// 危险操作\nHttp::get($request->input('url')); // ❌ High: SSRF\nfile_get_contents($url); // ❌\nGuzzle\\Client->get($url); // ❌\n\n// 审计正则\nHttp::(get|post|request).*\\$request|file_get_contents.*\\$request\nGuzzle.*->get.*\\$request\n\n// 漏洞示例\nuse Illuminate\\Support\\Facades\\Http;\n\npublic function fetch(Request $request) {\n $url = $request->input('url');\n $response = Http::get($url); // ❌ High: SSRF\n return $response->json();\n}\n\n// 攻击载荷\nGET /fetch?url=http://169.254.169.254/latest/meta-data/\nGET /fetch?url=http://localhost:6379/\nGET /fetch?url=file:///etc/passwd\n\n// 安全修复\npublic function fetch(Request $request) {\n $url = $request->input('url');\n\n // 1. URL验证\n $parsed = parse_url($url);\n if (!$parsed || !isset($parsed['scheme'], $parsed['host'])) {\n abort(400, 'Invalid URL');\n }\n\n // 2. 协议白名单\n if (!in_array($parsed['scheme'], ['http', 'https'])) {\n abort(403, 'Invalid protocol');\n }\n\n // 3. 主机白名单\n $allowedHosts = ['api.example.com', 'cdn.example.com'];\n if (!in_array($parsed['host'], $allowedHosts)) {\n abort(403, 'Host not allowed');\n }\n\n // 4. 禁止内网IP\n $ip = gethostbyname($parsed['host']);\n if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {\n abort(403, 'Internal IP not allowed');\n }\n\n $response = Http::timeout(5)->get($url); // ✓\n return $response->json();\n}\n```\n\n---\n\n## 认证和授权绕过\n\n```php\n// 危险操作\n// 未验证所有权\npublic function deletePost($id) {\n Post::find($id)->delete(); // ❌ High: IDOR\n}\n\n// 弱认证检查\nif ($request->header('X-Admin') == 'true') { // ❌ Critical\n // admin actions\n}\n\n// 基于客户端的授权\nif ($request->input('role') == 'admin') { // ❌ Critical\n // ...\n}\n\n// 审计正则\n->delete\\(\\)|->update\\(\\) # 检查是否有授权验证\n\\$request->(input|header).*==.*admin\n\n// 漏洞示例1: IDOR\npublic function updatePost(Request $request, $id) {\n $post = Post::findOrFail($id);\n $post->update($request->all()); // ❌ 未检查所有权\n return redirect()->back();\n}\n\n// 攻击场景\nPUT /posts/123 // 修改他人文章\n\n// 漏洞示例2: 权限绕过\nRoute::group(['prefix' => 'admin'], function () {\n Route::get('/users', [AdminController::class, 'users']);\n});\n// ❌ 缺少 middleware('auth') 或 can() 检查\n\n// 安全修复1: Policy授权\n// app/Policies/PostPolicy.php\npublic function update(User $user, Post $post) {\n return $user->id === $post->user_id; // ✓\n}\n\n// Controller\npublic function updatePost(Request $request, Post $post) {\n $this->authorize('update', $post); // ✓ 检查授权\n $post->update($request->validated());\n return redirect()->back();\n}\n\n// 安全修复2: 中间件\nRoute::group(['middleware' => ['auth', 'role:admin']], function () {\n Route::get('/admin/users', [AdminController::class, 'users']);\n}); // ✓\n\n// 安全修复3: Gate\nif (Gate::denies('update-post', $post)) {\n abort(403);\n}\n```\n\n---\n\n## CSRF 保护检测\n\n```php\n// 危险配置\n// app/Http/Middleware/VerifyCsrfToken.php\nprotected $except = [\n '*', // ❌ Critical: 全局禁用\n 'api/*', // ⚠️ API端点需其他保护\n '/webhook/*' // ✓ 合理(外部回调)\n];\n\n// 审计正则\nVerifyCsrfToken.*\\$except|@csrf # 检查是否正确使用\n\n// 漏洞场景\n// 状态改变操作未启用CSRF\nRoute::post('/transfer', [TransferController::class, 'transfer']);\n// ❌ 如果在 $except 中\n\n// 安全措施\n// 1. 仅对必要路径禁用CSRF\nprotected $except = [\n 'webhook/stripe', // ✓ 第三方回调\n 'webhook/paypal',\n];\n\n// 2. Blade 模板中包含 CSRF token\n\n\n// 3. AJAX 请求包含 CSRF token\n// resources/js/bootstrap.js\naxios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name=\"csrf-token\"]').content;\n\n// 4. API 使用 Sanctum 而非禁用 CSRF\nRoute::middleware('auth:sanctum')->post('/api/transfer', ...);\n```\n\n---\n\n## 开放重定向检测\n\n```php\n// 危险操作\nreturn redirect($request->input('url')); // ❌ Medium\nreturn redirect()->intended($request->input('next')); // ❌\n\n// 审计正则\nredirect\\s*\\(\\s*\\$request|redirect\\(\\)->.*\\$request\n\n// 漏洞示例\npublic function logout(Request $request) {\n Auth::logout();\n $next = $request->input('redirect');\n return redirect($next); // ❌ Medium: 开放重定向\n}\n\n// 攻击载荷\nGET /logout?redirect=https://evil.com/phishing\n\n// 安全修复\npublic function logout(Request $request) {\n Auth::logout();\n\n $next = $request->input('redirect', '/');\n\n // 方法1: 仅允许相对路径\n if (parse_url($next, PHP_URL_HOST) !== null) {\n $next = '/';\n }\n\n // 方法2: 白名单验证\n $allowedDomains = ['example.com', 'app.example.com'];\n $host = parse_url($next, PHP_URL_HOST);\n if ($host && !in_array($host, $allowedDomains)) {\n $next = '/';\n }\n\n return redirect($next); // ✓\n}\n\n// 方法3: 使用 route() 或 action()\nreturn redirect()->route('home'); // ✓ 最安全\nreturn redirect()->action([HomeController::class, 'index']); // ✓\n```\n\n---\n\n## XXE 漏洞检测\n\n```php\n// 危险操作\n$xml = simplexml_load_string($request->input('xml')); // ❌ High: XXE\n$dom = new DOMDocument();\n$dom->loadXML($xmlString); // ❌ 默认不安全\n\n// 审计正则\nsimplexml_load_(string|file)|DOMDocument.*loadXML|XMLReader\n\n// 攻击载荷\n\n\n]>\n&xxe;\n\n// 安全修复\n// 禁用外部实体\nlibxml_disable_entity_loader(true); // PHP < 8.0\n\n$dom = new DOMDocument();\n$dom->loadXML($xmlString, LIBXML_NOENT | LIBXML_DTDLOAD | LIBXML_DTDATTR); // ❌ 危险标志\n\n// ✓ 安全加载\n$dom = new DOMDocument();\n$dom->loadXML($xmlString, LIBXML_NOCDATA | LIBXML_NONET); // ✓\n\n// 最佳实践: 避免XML,使用JSON\n$data = json_decode($request->input('data'), true); // ✓\n```\n\n---\n\n## 命令注入检测\n\n```php\n// 危险操作\nexec($request->input('cmd')); // ❌ Critical\nshell_exec(\"ping \" . $host); // ❌\nsystem(\"convert \" . $file); // ❌\n`whoami $user`; // ❌ 反引号执行\n\n// 审计正则\nexec\\s*\\(.*\\$|shell_exec.*\\$|system\\s*\\(.*\\$|passthru.*\\$|`.*\\$\n\n// 漏洞示例\npublic function ping(Request $request) {\n $host = $request->input('host');\n $output = shell_exec(\"ping -c 4 \" . $host); // ❌ Critical\n return $output;\n}\n\n// 攻击载荷\nGET /ping?host=google.com;whoami\nGET /ping?host=`cat /etc/passwd`\n\n// 安全修复\npublic function ping(Request $request) {\n $host = $request->input('host');\n\n // 1. 验证格式\n if (!filter_var($host, FILTER_VALIDATE_IP) && !filter_var($host, FILTER_VALIDATE_DOMAIN)) {\n abort(400, 'Invalid host');\n }\n\n // 2. 使用 escapeshellarg\n $output = shell_exec(\"ping -c 4 \" . escapeshellarg($host)); // ✓\n\n return $output;\n}\n\n// 最佳实践: 避免 shell,使用原生函数\n// ❌ shell_exec(\"rm \" . $file);\n// ✓ File::delete($file);\n\n// ❌ exec(\"cp $src $dst\");\n// ✓ File::copy($src, $dst);\n```\n\n---\n\n## 敏感信息泄露\n\n```php\n// 危险配置\n// config/app.php\n'debug' => true, // ❌ 生产环境启用debug\n\n// .env 泄露\nAPP_DEBUG=true // ❌ 生产环境\nDB_PASSWORD=secret123 // ❌ 如果.env泄露\n\n// 代码泄露\nreturn response()->json($exception); // ❌ 异常对象泄露\ndd($user); // ❌ 调试输出忘记删除\nLog::debug($request->all()); // ❌ 密码记录到日志\n\n// 审计正则\nAPP_DEBUG=true|dd\\(|dump\\(|var_dump\\(|print_r\\(\nLog::.*password\n\n// 安全措施\n// 1. 生产环境配置\nAPP_ENV=production\nAPP_DEBUG=false\nLOG_LEVEL=error\n\n// 2. 自定义错误页面\n// resources/views/errors/500.blade.php\n@if(config('app.debug'))\n {{ $exception->getMessage() }}\n@else\n Something went wrong. // ✓ 通用错误\n@endif\n\n// 3. 日志脱敏\nLog::info('Login attempt', [\n 'email' => $email,\n 'password' => '***', // ✓ 不记录密码\n]);\n\n// 4. 保护 .env\n# .gitignore\n.env\n.env.backup\n\n# nginx\nlocation ~ /\\.env {\n deny all;\n}\n```\n\n---\n\n## 速率限制和暴力破解\n\n```php\n// 缺少速率限制\nRoute::post('/login', [AuthController::class, 'login']); // ❌ 无限重试\n\n// 审计正则\nRoute::post.*login|Route::post.*register # 检查是否有throttle\n\n// 安全措施\n// 1. 使用内置throttle中间件\nRoute::post('/login', [AuthController::class, 'login'])\n ->middleware('throttle:5,1'); // ✓ 每分钟5次\n\n// 2. 自定义速率限制\n// app/Providers/RouteServiceProvider.php\nRateLimiter::for('login', function (Request $request) {\n return Limit::perMinute(5)->by($request->input('email')); // ✓ 按邮箱限制\n});\n\nRoute::post('/login', [AuthController::class, 'login'])\n ->middleware('throttle:login');\n\n// 3. 使用验证码\n// 登录失败3次后要求验证码\n```\n\n---\n\n## 搜索模式汇总\n\n```regex\n# Mass Assignment\n::create\\s*\\(\\s*\\$request->all|->fill\\s*\\(\\s*\\$request\n\n# SQL注入\nDB::(select|statement).*\\$|whereRaw.*\\$|orderByRaw.*\\$request\n\n# 反序列化\nunserialize\\s*\\(|APP_KEY\\s*=\n\n# SSTI\n\\{\\!\\!.*\\$|@php.*\\$request|Blade::render.*\\$request\n\n# 路径遍历\nStorage::(get|delete).*\\$request|include.*\\$request\n\n# SSRF\nHttp::(get|post).*\\$request|file_get_contents.*\\$request\n\n# 命令注入\nexec\\s*\\(.*\\$|shell_exec|system\\s*\\(|passthru|`\n\n# 授权\n->delete\\(\\)|->update\\(\\) # 检查是否有authorize\n\n# CSRF\nVerifyCsrfToken.*\\$except\n\n# 敏感信息\nAPP_DEBUG=true|dd\\(|var_dump\\(\n\n# 开放重定向\nredirect\\s*\\(\\s*\\$request\n```\n\n---\n\n## 快速审计检查清单\n\n```markdown\n[ ] 检查 composer.json 依赖版本\n[ ] 检查 .env 是否在 .gitignore\n[ ] 检查 APP_DEBUG 生产环境配置\n[ ] 搜索 create/fill/update ($request->all)\n[ ] 检查 Model 的 $fillable/$guarded\n[ ] 搜索 whereRaw/orderByRaw (SQL注入)\n[ ] 检查 unserialize 和 APP_KEY 泄露\n[ ] 搜索 {!! (Blade不转义输出)\n[ ] 检查文件操作的路径拼接\n[ ] 检查 Http::get/file_get_contents (SSRF)\n[ ] 检查 exec/shell_exec (命令注入)\n[ ] 检查 Policy 和 authorize 使用\n[ ] 检查 VerifyCsrfToken 的 $except\n[ ] 检查 redirect 开放重定向\n[ ] 检查登录/注册接口的速率限制\n```\n\n---\n\n## 最小 PoC 示例\n```bash\n# Blade SSTI/未转义输出\ncurl \"http://localhost/profile?name={{7*7}}\"\n\n# SQL 注入 (whereRaw)\ncurl \"http://localhost/users?orderByRaw=id desc;select version()\"\n\n# SSRF\ncurl \"http://localhost/fetch?url=http://169.254.169.254/latest/meta-data/\"\n```\n\n---\n\n## 参考资源\n\n- [Laravel Security Best Practices](https://laravel.com/docs/security)\n- [OWASP Laravel Security](https://cheatsheetseries.owasp.org/cheatsheets/Laravel_Cheat_Sheet.html)\n- [Laravel CVEs](https://www.cvedetails.com/vulnerability-list/vendor_id-16542/Laravel.html)\n- [Enlightn Security Scanner](https://github.com/enlightn/enlightn)\n"
const SKILL_FILE_11 = "# MyBatis SQL Injection Security Audit Guide\n\n> MyBatis 框架 SQL 注入专项审计模块\n> 适用于: MyBatis 3.x, MyBatis-Plus, Spring Boot + MyBatis 集成\n> 来源: DataEase 审计中发现的关键差距 — `${}` 注入检测需系统化覆盖\n\n## 核心原理: `${}` vs `#{}`\n\n```xml\n\n\n\n\n\n\n\n\n\n```\n\n---\n\n## `${}` 基础检测\n\n```xml\n\n\n\n\n\n\ngrep -rn '\\$\\{' --include=\"*.xml\" src/main/resources/mapper/\n\n\nWHERE.*\\$\\{|AND.*\\$\\{|OR.*\\$\\{|HAVING.*\\$\\{|SET.*\\$\\{\n\n\n\n```\n\n---\n\n## ORDER BY / LIMIT 注入 (无法使用 #{})\n\n```xml\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nORDER\\s+BY\\s+\\$\\{|LIMIT\\s+\\$\\{|GROUP\\s+BY\\s+\\$\\{|OFFSET\\s+\\$\\{\n\n\n```\n```java\n// ORDER BY 安全处理\nprivate static final Set ALLOWED_COLUMNS = Set.of(\"id\", \"name\", \"created_at\");\nprivate static final Set ALLOWED_ORDERS = Set.of(\"ASC\", \"DESC\");\n\npublic List listUsers(String sortColumn, String sortOrder) {\n if (!ALLOWED_COLUMNS.contains(sortColumn)) {\n sortColumn = \"id\"; // ✓ 默认值\n }\n if (!ALLOWED_ORDERS.contains(sortOrder.toUpperCase())) {\n sortOrder = \"ASC\"; // ✓\n }\n return mapper.listUsers(sortColumn, sortOrder);\n}\n\n// LIMIT 安全处理: 强制类型转换\nint offset = Integer.parseInt(offsetStr); // ✓ 非数字会抛异常\nint limit = Math.min(Integer.parseInt(limitStr), 100); // ✓ 上限\n```\n\n---\n\n## 表名 / 列名动态注入\n\n```xml\n\n\n\n\n\n\n\nFROM\\s+\\$\\{|INTO\\s+\\$\\{|UPDATE\\s+\\$\\{|JOIN\\s+\\$\\{\nSELECT\\s+\\$\\{|INSERT\\s+INTO.*\\(\\s*\\$\\{\n\n\n```\n```java\nprivate static final Set ALLOWED_TABLES = Set.of(\"users\", \"orders\", \"products\");\n\npublic List