PHP 怎么PHP 安全扫描流水线

wen PHP项目 3

本文目录导读:

PHP 怎么PHP 安全扫描流水线

  1. 完整的 PHP 安全扫描流水线架构
  2. Docker 化流水线配置
  3. GitHub Actions 工作流配置
  4. 自定义扫描规则配置
  5. 报告生成与聚合
  6. 最佳实践建议

我来详细说明 PHP 安全扫描流水线的搭建方法。

完整的 PHP 安全扫描流水线架构

# .gitlab-ci.yml 示例
stages:
  - dependencies
  - static_analysis
  - security_scan
  - dynamic_scan
# 依赖安装
install_deps:
  stage: dependencies
  script:
    - composer install --no-dev --optimize-autoloader
    - npm ci
  artifacts:
    paths:
      - vendor/
      - node_modules/
# 静态代码分析
static_analysis:
  stage: static_analysis
  parallel:
    matrix:
      - TOOL: [phpcs, phpmd, phpstan, psalm]
  script:
    - |
      case $TOOL in
        phpcs)
          vendor/bin/phpcs --standard=PSR12 src/
          ;;
        phpmd)
          vendor/bin/phpmd src/ text cleancode,codesize,controversial,design,naming,unusedcode
          ;;
        phpstan)
          vendor/bin/phpstan analyse src/ --level=9
          ;;
        psalm)
          vendor/bin/psalm --show-info=true
          ;;
      esac
# 安全扫描
security_scan:
  stage: security_scan
  parallel:
    matrix:
      - TOOL: 
          - composer_audit
          - security_checker
          - snuffleupagus
          - php_sec_info
          - owasp_dependency_check
  script:
    - |
      case $TOOL in
        composer_audit)
          composer audit --format=json > reports/composer-audit.json
          ;;
        security_checker)
          vendor/bin/security-checker security:check composer.lock
          ;;
        snuffleupagus)
          # 运行 Snuffleupagus 规则
          php -d "snuffleupagus.rules"="default" run_tests.php
          ;;
        php_sec_info)
          vendor/bin/php-security-info
          ;;
        owasp_dependency_check)
          dependency-check --scan . --format JSON > reports/owasp.json
          ;;
      esac
  artifacts:
    paths:
      - reports/
    expire_in: 30 days
# DAST 动态扫描
dynamic_scan:
  stage: dynamic_scan
  services:
    - name: owasp/zap2docker-stable
      alias: zap
  script:
    - |
      # 启动目标应用
      php -S 0.0.0.0:8000 -t public/ &
      # 等待应用启动
      sleep 5
      # 运行 ZAP 扫描
      zap-cli quick-scan --self-contained \
        --start-options '-config api.disablekey=true' \
        http://app:8000/
      # 生成报告
      zap-cli report -o reports/zap-report.html -f html
  artifacts:
    paths:
      - reports/zap-report.html
# 代码安全审计
code_audit:
  stage: security_scan
  script:
    - |
      # 运行 Progpilot
      vendor/bin/progpilot --input src/ --output reports/progpilot.json
      # 运行 RIPS 扫描
      php -r "
        \$scanner = new RipsScanner('src/');
        \$scanner->scan();
        file_put_contents('reports/rips.html', \$scanner->getHtmlReport());
      "
      # 运行 Phan Security Plugin
      vendor/bin/phan --plugin SecurityPlugin \
        --output-mode json \
        --output reports/phan-security.json
  artifacts:
    paths:
      - reports/
# 依赖漏洞扫描
dependency_scan:
  stage: security_scan
  script:
    - |
      # Syft 扫描
      syft packages dir:. -o json > reports/syft.json
      # Grype 漏洞扫描
      grype dir:. -o json > reports/grype.json
      # Trivy 扫描
      trivy filesystem --severity HIGH,CRITICAL . > reports/trivy.txt
      # Snyk 扫描
      snyk test --json > reports/snyk.json
  artifacts:
    paths:
      - reports/
# 配置安全检查
config_scan:
  stage: security_scan
  script:
    - |
      # 检查 PHP 配置安全性
      php -r "
        \$secure = [
          'expose_php' => 'Off',
          'display_errors' => 'Off',
          'allow_url_fopen' => 'Off',
          'allow_url_include' => 'Off',
          'session.cookie_httponly' => 'On',
          'session.cookie_secure' => 'On',
          'session.use_only_cookies' => 'On',
          'session.use_strict_mode' => 'On',
          'session.cookie_samesite' => 'Lax'
        ];
        foreach (\$secure as \$key => \$expected) {
          \$actual = ini_get(\$key);
          if (\$actual != \$expected) {
            echo \"WARNING: \$key should be \$expected, current is \$actual\n\";
          }
        }
      " > reports/config-check.txt
  artifacts:
    paths:
      - reports/config-check.txt

Docker 化流水线配置

# Dockerfile.security
FROM php:8.2-cli
# 安装依赖
RUN apt-get update && apt-get install -y \
    git \
    curl \
    wget \
    unzip \
    libzip-dev \
    libcurl4-openssl-dev \
    && docker-php-ext-install zip curl
# 安装 Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# 安装安全工具
RUN wget https://github.com/OWASP/dependency-check/releases/download/v8.3.1/dependency-check-8.3.1-release.zip \
    && unzip dependency-check-8.3.1-release.zip -d /opt/dependency-check \
    && ln -s /opt/dependency-check/bin/dependency-check.sh /usr/local/bin/dependency-check
# 安装安全扫描器
RUN composer global require \
    sensiolabs/security-checker \
    phpstan/phpstan \
    vimeo/psalm \
    phan/phan \
    rips/rips-scanner
# 安装开源工具
RUN wget https://github.com/anchore/syft/releases/download/v0.87.1/syft_0.87.1_linux_amd64.deb \
    && dpkg -i syft_0.87.1_linux_amd64.deb
RUN wget https://github.com/anchore/grype/releases/download/v0.65.1/grype_0.65.1_linux_amd64.deb \
    && dpkg -i grype_0.65.1_linux_amd64.deb
# 设置工作目录
WORKDIR /workspace
COPY composer.json composer.lock ./
COPY src/ src/
COPY config/ config/
CMD ["composer", "install", "--no-dev", "--optimize-autoloader"]

GitHub Actions 工作流配置

# .github/workflows/security-scan.yml
name: PHP Security Scan
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * *'  # 每天凌晨2点运行
jobs:
  security-checks:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        php-version: ['8.1', '8.2']
        tool: ['phpstan', 'psalm', 'security-checker']
    steps:
    - uses: actions/checkout@v3
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: ${{ matrix.php-version }}
        extensions: mbstring, xml, json, curl, gd
        coverage: xdebug
        tools: composer:v2
    - name: Install dependencies
      run: composer install --no-interaction --prefer-dist
    - name: Run ${{ matrix.tool }}
      run: |
        case ${{ matrix.tool }} in
          phpstan)
            vendor/bin/phpstan analyse src/ --level=9
            ;;
          psalm)
            vendor/bin/psalm --show-info=true --stats
            ;;
          security-checker)
            vendor/bin/security-checker security:check composer.lock
            ;;
        esac
    - name: Upload report
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: security-report-${{ matrix.tool }}-php${{ matrix.php-version }}
        path: |
          reports/
  composer-audit:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
    - name: Composer audit
      run: composer audit --format=json
      continue-on-error: true
    - name: Check for vulnerabilities
      run: |
        AUDIT_RESULT=$(composer audit --format=json)
        echo "$AUDIT_RESULT" | jq '.advisories'
        if echo "$AUDIT_RESULT" | jq -e '.advisories | length > 0' > /dev/null; then
          echo "::warning::Vulnerabilities found in dependencies"
          exit 1
        fi
  dependency-check:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: OWASP Dependency Check
      uses: dependency-check/Dependency-Check_Action@main
      with:
        project: 'PHP Application'
        path: '.'
        format: 'HTML'
        args: >
          --failOnCVSS 7
          --enableExperimental
          --suppression suppression.xml
    - name: Upload Dependency Check report
      uses: actions/upload-artifact@v3
      with:
        name: dependency-check-report
        path: reports/dependency-check-report.html
  sast-scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Phan Security Scan
      run: |
        vendor/bin/phan --plugin SecurityPlugin \
          --output-mode pylint \
          --output reports/phan.log
    - name: Progpilot Scan
      run: |
        vendor/bin/progpilot --input src/ \
          --output reports/progpilot.json \
          --config progpilot_config.yml
    - name: Upload SAST results
      uses: actions/upload-artifact@v3
      with:
        name: sast-reports
        path: reports/

自定义扫描规则配置

// progpilot_config.yml
scanner:
  inputs:
    - src/
  outputs:
    - reports/progpilot.json
  rules:
    # SQL注入检查
    sql_injection:
      enabled: true
      sinks:
        - mysqli_query
        - PDO::query
        - db_query
    # XSS检查
    xss:
      enabled: true
      sinks:
        - echo
        - print
        - print_r
    # 命令注入
    command_injection:
      enabled: true
      sinks:
        - exec
        - system
        - shell_exec
        - passthru
    # 文件包含
    file_inclusion:
      enabled: true
      sinks:
        - include
        - include_once
        - require
        - require_once
    # 文件操作
    file_operations:
      enabled: true
      sinks:
        - fopen
        - file_get_contents
        - file_put_contents
        - unlink
    # 序列化
    deserialization:
      enabled: true
      sinks:
        - unserialize
        - json_decode
# 自定义漏洞规则
custom_vulnerabilities:
  - name: "Weak Password Hash"
    pattern: "md5|sha1"
    severity: "high"
  - name: "Hardcoded Credentials"
    pattern: "(password|passwd|pwd)\s*=\s*['\"](?!.*(env|getenv|\$_SERVER))"
    severity: "critical"
  - name: "Insecure Random"
    pattern: "rand\(|mt_rand\("
    severity: "medium"

报告生成与聚合

// ReportGenerator.php
<?php
class SecurityReportGenerator
{
    private array $reports = [];
    private array $vulnerabilities = [];
    public function __construct(private string $outputDir)
    {
        if (!is_dir($outputDir)) {
            mkdir($outputDir, 0755, true);
        }
    }
    public function generateSummary(array $scanResults): array
    {
        $summary = [
            'timestamp' => date('Y-m-d H:i:s'),
            'total_vulnerabilities' => 0,
            'critical' => 0,
            'high' => 0,
            'medium' => 0,
            'low' => 0,
            'scan_duration' => 0,
            'scans_performed' => count($scanResults)
        ];
        foreach ($scanResults as $tool => $results) {
            $counts = $this->parseToolResults($tool, $results);
            $summary['critical'] += $counts['critical'];
            $summary['high'] += $counts['high'];
            $summary['medium'] += $counts['medium'];
            $summary['low'] += $counts['low'];
            $summary['total_vulnerabilities'] += $counts['total'];
            $this->vulnerabilities[$tool] = $results;
        }
        return $summary;
    }
    public function generateHTMLReport(array $scanResults): string
    {
        $summary = $this->generateSummary($scanResults);
        $html = <<<HTML
<!DOCTYPE html>
<html>
<head>PHP Security Scan Report</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        h1 { color: #333; }
        .vulnerability { margin: 10px 0; padding: 10px; border-radius: 5px; }
        .critical { background-color: #ffebee; border-left: 4px solid #f44336; }
        .high { background-color: #fff3e0; border-left: 4px solid #ff9800; }
        .medium { background-color: #fff8e1; border-left: 4px solid #ffc107; }
        .low { background-color: #e8f5e9; border-left: 4px solid #4caf50; }
        .severity { font-weight: bold; padding: 2px 6px; border-radius: 3px; }
        .severity-critical { background-color: #f44336; color: white; }
        .severity-high { background-color: #ff9800; color: white; }
        .severity-medium { background-color: #ffc107; color: black; }
        .severity-low { background-color: #4caf50; color: white; }
    </style>
</head>
<body>
    <h1>PHP Security Scan Report</h1>
    <h2>Summary</h2>
    <p>Total Vulnerabilities: {$summary['total_vulnerabilities']}</p>
    <p>Critical: {$summary['critical']}</p>
    <p>High: {$summary['high']}</p>
    <p>Medium: {$summary['medium']}</p>
    <p>Low: {$summary['low']}</p>
    <p>Scan Date: {$summary['timestamp']}</p>
HTML;
        // 添加具体漏洞信息
        foreach ($this->vulnerabilities as $tool => $results) {
            $html .= "<h3>Tool: {$tool}</h3>";
            foreach ($results as $vuln) {
                $severity = strtolower($vuln['severity'] ?? 'medium');
                $html .= <<<VULN
                <div class="vulnerability {$severity}">
                    <span class="severity severity-{$severity}">{$vuln['severity']}</span>
                    <strong>{$vuln['title']}</strong>
                    <p>{$vuln['description']}</p>
                    <p>File: {$vuln['file']}:{$vuln['line']}</p>
                    <pre>{$vuln['code']}</pre>
                    <p>Recommendation: {$vuln['recommendation']}</p>
                </div>
VULN;
            }
        }
        $html .= '</body></html>';
        file_put_contents(
            $this->outputDir . '/security-report-' . date('Y-m-d') . '.html',
            $html
        );
        return $html;
    }
    private function parseToolResults(string $tool, array $results): array
    {
        // 根据不同工具格式解析结果
        switch ($tool) {
            case 'phpstan':
                return $this->parsePhpStanResults($results);
            case 'psalm':
                return $this->parsePsalmResults($results);
            case 'progpilot':
                return $this->parseProgpilotResults($results);
            default:
                return $this->parseGenericResults($results);
        }
    }
}

最佳实践建议

流水线集成建议

# 安全检查点配置
security_pipeline:
  # 1. 代码提交前检查(pre-commit hooks)
  pre_commit:
    - php -l {files}  # 语法检查
    - composer audit --format=json  # 依赖检查
  # 2. CI/CD 流水线检查
  ci_cd:
    - stage: dependencies
      check: composer validate && composer audit
    - stage: static_analysis  
      check: phpstan analyse --level=max
    - stage: security_scan
      check: |
        progpilot --input src/ \
        security-checker security:check
    - stage: dependency_scan
      check: grype dir:.
  # 3. 部署前检查(门禁)
  deployment_gates:
    - severity: critical
      action: block_deployment
      tools: [phpstan, psalm, security-checker]
    - severity: high
      action: require_approval
      tools: [progpilot, dependency-check]
    - severity: medium
      action: notify
      tools: [all]
  # 4. 定期扫描
  scheduled_scans:
    - frequency: daily
      time: "02:00"
      tools: [all]
    - frequency: weekly
      time: "03:00"
      tools: [full-scan, penetration-test]
    - frequency: monthly
      time: "04:00"
      tools: [owasp-zap, nuclei]

这样的流水线配置可以:

  • 在代码提交时实时检查
  • CI/CD 过程中全面扫描
  • 部署前自动门禁
  • 定期执行深度扫描
  • 生成详细报告并告警

抱歉,评论功能暂时关闭!