一套可直接开源落地的))Hyperf PHP 安全基线工具箱从0到持续维护带核心代码骨架。 ---1)项目目标MVP 先做6项基线检查1. 运行时配置检查APP_ENV、APP_DEBUG、CORS、错误暴露2. HTTP 安全头检查CSP、HSTS、X-Frame-Options 等3. 依赖漏洞检查composer audit4. Secret 泄漏扫描AK/SK、私钥、Token5. 文件权限检查.env、storage、runtime6. 报告输出CLI JSON CI 失败门禁 ---2)仓库结构建议 hyperf-security-baseline/ ├─ src/ │ ├─ Contract/ │ │ ├─ RuleInterface.php │ │ └─ Finding.php │ ├─ Rule/ │ │ ├─ EnvRule.php │ │ ├─ HeaderRule.php │ │ ├─ ComposerAuditRule.php │ │ ├─ SecretScanRule.php │ │ └─ PermissionRule.php │ ├─ Service/ │ │ ├─ BaselineRunner.php │ │ └─ ReportFormatter.php │ ├─ Command/ │ │ └─ SecurityScanCommand.php │ └─ ConfigProvider.php ├─ config/ │ └─ autoload/security_baseline.php ├─ tests/ ├─ .github/workflows/ci.yml ├─ composer.json ├─ README.md ├─ SECURITY.md └─ LICENSE ---3)从0初始化mkdirhyperf-security-baselinecdhyperf-security-baselinecomposerinitcomposerrequire hyperf/command hyperf/contract hyperf/utils symfony/processcomposerrequire--devphpunit/phpunit phpstan/phpstan friendsofphp/php-cs-fixer ---4)核心代码4.1规则接口 Finding src/Contract/RuleInterface.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Contract;interface RuleInterface{publicfunctionname(): string;/** return Finding[]*/ publicfunctioncheck(array$context): array;}src/Contract/Finding.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Contract;final class Finding{publicfunction__construct(public string$rule, public string$level, // info|low|medium|high|critical public string$message, public ?string$filenull, public ?int$linenull, public ?string$fixnull,){}}---4.2规则示例EnvRule src/Rule/EnvRule.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Rule;use HyperfSecurityBaseline\Contract\Finding;use HyperfSecurityBaseline\Contract\RuleInterface;final class EnvRule implements RuleInterface{publicfunctionname(): string{returnenv_rule;}publicfunctioncheck(array$context): array{$findings[];$env(string)($context[env][APP_ENV]??dev);$debug(string)($context[env][APP_DEBUG]??true);if($envprodstrtolower($debug)true){$findings[]new Finding(rule:$this-name(), level:high, message:APP_DEBUG must be false in production, fix:Set APP_DEBUGfalse in .env for prod);}return$findings;}}---4.3规则示例ComposerAuditRule src/Rule/ComposerAuditRule.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Rule;use HyperfSecurityBaseline\Contract\Finding;use HyperfSecurityBaseline\Contract\RuleInterface;use Symfony\Component\Process\Process;final class ComposerAuditRule implements RuleInterface{publicfunctionname(): string{returncomposer_audit_rule;}publicfunctioncheck(array$context): array{$cwd$context[project_root];$pnew Process([composer,audit,--formatjson],$cwd);$p-run();if(!$p-isSuccessful()){return[new Finding($this-name(),medium,composer audit execution failed)];}$datajson_decode($p-getOutput(),true);$findings[];foreach(($data[advisories]??[])as $package$items){ foreach($items as $advisory){ $findings[]new Finding(rule:$this-name(),level:high,message:sprintf(Vulnerability found in%s:%s,$package,$advisory[title]??unknown advisory),fix:sprintf(Upgrade package %s to patched version,$package));} } return $findings;} }---4.4规则示例SecretScanRule src/Rule/SecretScanRule.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Rule;use HyperfSecurityBaseline\Contract\Finding;use HyperfSecurityBaseline\Contract\RuleInterface;final class SecretScanRule implements RuleInterface { public function name():string { return secret_scan_rule;} public function check(array $context):array { $patterns[ /AKIA[0-9A-Z]{16}/Possible AWS Access Key,/-----BEGIN(RSA|EC|OPENSSH)PRIVATE KEY-----/Private key material,/(?i)(api[_-]?key|secret|token)\s*[:]\s*[\][^\]{12,}[\]/Hardcoded secret,];$findings[];$files$context[scan_files]??[];foreach($files as $file){ $contentfile_get_contents($file);if($contentfalse){ continue;} foreach($patterns as $regex$label){ if(preg_match($regex,$content,$m,PREG_OFFSET_CAPTURE)){$offset$m[0][1];$linesubstr_count(substr($content,0,$offset),\n)1;$findings[]new Finding(rule:$this-name(), level:critical, message:$label, file:$file, line:$line, fix:Move secret to env/secret manager and rotate leaked credential);}}}return$findings;}}---4.5Runner 报告格式化 src/Service/BaselineRunner.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Service;use HyperfSecurityBaseline\Contract\RuleInterface;final class BaselineRunner{/** param RuleInterface[]$rules*/ publicfunction__construct(private array$rules){}publicfunctionrun(array$context): array{$all[];foreach($this-rules as$rule){$allarray_merge($all,$rule-check($context));}return$all;}}src/Service/ReportFormatter.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Service;use HyperfSecurityBaseline\Contract\Finding;final class ReportFormatter{/** param Finding[]$findings*/ publicfunctiontoJson(array$findings): string{$arrarray_map(fn(Finding$f)[rule$f-rule,level$f-level,message$f-message,file$f-file,line$f-line,fix$f-fix,],$findings);returnjson_encode([totalcount($arr),findings$arr], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);}}---4.6Hyperf 命令入口 src/Command/SecurityScanCommand.php?php declare(strict_types1);namespace HyperfSecurityBaseline\Command;use Hyperf\Command\Annotation\Command;use Hyperf\Command\Command as HyperfCommand;use HyperfSecurityBaseline\Service\BaselineRunner;use HyperfSecurityBaseline\Service\ReportFormatter;#[Command]final class SecurityScanCommand extends HyperfCommand{publicfunction__construct(private BaselineRunner$runner, private ReportFormatter$formatter){parent::__construct(security:scan);}publicfunctionconfigure(){parent::configure();$this-setDescription(Run security baseline checks);}publicfunctionhandle(){$projectRootgetcwd();$context[project_root$projectRoot,env$_ENV$_SERVER,scan_files$this-collectFiles($projectRoot),];$findings$this-runner-run($context);$json$this-formatter-toJson($findings);$this-line($json);$hasHighcollect($findings)-contains(fn($f)in_array($f-level,[high,critical],true));return$hasHigh?2:0;}privatefunctioncollectFiles(string$root): array{$riinew\RecursiveIteratorIterator(new\RecursiveDirectoryIterator($root));$files[];foreach($riias$file){if($file-isDir())continue;$path$file-getPathname();if(preg_match(/vendor|runtime|\.git/,$path))continue;if(preg_match(/\.(php|env|yaml|yml|json|ini)$/,$path)){$files[]$path;}}return$files;}}---5)ConfigProvider 与依赖注册 src/ConfigProvider.php?php declare(strict_types1);namespace HyperfSecurityBaseline;use HyperfSecurityBaseline\Rule\ComposerAuditRule;use HyperfSecurityBaseline\Rule\EnvRule;use HyperfSecurityBaseline\Rule\PermissionRule;use HyperfSecurityBaseline\Rule\SecretScanRule;use HyperfSecurityBaseline\Service\BaselineRunner;class ConfigProvider{publicfunction__invoke(): array{return[dependencies[BaselineRunner::classfunction(){returnnew BaselineRunner([new EnvRule(), new ComposerAuditRule(), new SecretScanRule(), new PermissionRule(),]);},],commands[\HyperfSecurityBaseline\Command\SecurityScanCommand::class,],];}}---6)CI 门禁开源仓库必须 .github/workflows/ci.yml name: ci on:[push, pull_request]jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: shivammathur/setup-phpv2 with: php-version:8.2- run:composerinstall--no-interaction --prefer-dist - run: vendor/bin/phpunit - run: vendor/bin/phpstan analyse src - run: php bin/hyperf.php security:scansecurity-report.json - run:|catsecurity-report.jsonifgrep-Elevel: (high|critical)security-report.json;thenechoHigh/Critical findings found;exit1;fi---7)开源发布流程从0到上线1. LICENSE 选 MIT 或 Apache-2.02. README 写清安装、命令、退出码、规则扩展方式3. SECURITY.md 写漏洞上报方式和响应时间4. 首个版本 v0.1.0MVP5. 打 Tag 并发 GitHub Release6. 提交 Packagist包名如 your-org/hyperf-security-baseline ---8)持续维护路线图 - v0.1: CLI 扫描 JSON 报告 CI 门禁 - v0.2: SARIF 输出接 GitHub Code Scanning - v0.3: 基线策略分级dev/staging/prod - v1.0: 规则插件化、基线豁免清单有审计记录 ---9)首版最容易踩坑1. 扫描范围太大把 vendor 扫进去导致噪声2. 高危判定太松CI 失去门禁意义3. 没有退出码规范流水线无法阻断4. Secret 命中后不做轮换流程5. 只做静态检查不做依赖漏洞检查 --- 你按这套骨架直接建仓库就能跑第一版。核心闭环是 security:scan -JSON 报告 -CI 根据 high/critical 失败这就是一个可用的 Hyperf 安全基线工具箱起点。