文章851
标签121
分类10

[踩坑] PHP 7.2 项目中安装 aws/aws-sdk-php 的完整踩坑与解决方案

背景
一个运行在 PHP 7.2.34 的老项目,需要引入 aws/aws-sdk-php,在 Composer 2.x 环境下经历了一系列失败,最终成功安装 3.288.1
本文记录完整排障过程与最终正确解法。

一、环境背景

基础环境

PHP:        7.2.34
Composer:   2.9.2
OS:         CentOS / RHEL 7

Composer 镜像

"repositories": {
  "packagist": {
    "type": "composer",
    "url": "https://packagist.org"
  }
}

二、最初的问题:aws/aws-sdk-php 无法安装

尝试安装:

composer require aws/aws-sdk-php:3.199.3

错误信息(核心):

found aws/aws-sdk-php[3.199.3] but these were not loaded,
because they are affected by security advisories

尝试过的方案(全部失败)

  • --no-audit
  • "audit": { "block-insecure": false }
  • 忽略 PHP 版本
  • 降级 / 固定版本
  • Composer 自降级

结论

**在 Composer 2.6+ + Packagist 环境下,
aws/aws-sdk-php 某些旧版本已经被仓库层“强制封禁”,无法通过任何参数绕过。**

三、选择可行版本:3.288.1

改用一个 PHP 7.2 可用,且未被封禁 的版本:

composer require aws/aws-sdk-php:3.288.1

却遇到了新的错误:

aws/aws-sdk-php 3.288.1 requires guzzlehttp/promises ^1.4.0 || ^2.0
but the package is fixed to v1.3.1 (lock file version)

四、真正的坑:Composer 的 Partial Update + Lock 文件

问题本质

  • 项目中 guzzlehttp/promises 被锁死在 1.3.1
  • AWS SDK 要求 >= 1.4.0
  • 使用了 部分更新(partial update)
  • Composer 不允许隐式修改已锁定依赖

错误提示里这句是关键:

the package is fixed to v1.3.1 (lock file version) by a partial update

五、正确解法(关键步骤)

最终成功命令

composer update aws/aws-sdk-php guzzlehttp/promises --with-all-dependencies

或等价写法:

composer require aws/aws-sdk-php:3.288.1 -W

-W / --with-all-dependencies 的含义

允许 Composer:

  • 升级
  • 降级
  • 移除

当前 composer.lock 中的相关依赖


六、推荐的依赖组合(PHP 7.2 / 7.3)

"require": {
  "aws/aws-sdk-php": "3.288.1",
  "guzzlehttp/guzzle": "^6.5.8 || ^7.5",
  "guzzlehttp/psr7": "^1.9 || ^2.4",
  "guzzlehttp/promises": "^1.5 || ^2.0"
}

七、经验总结(非常重要)

1️⃣ Composer 装不上包,不一定是版本不兼容

  • 可能是:

    • 仓库级封禁
    • 安全 advisory
    • lock 文件锁死

2️⃣ composer require 默认是“部分更新”

不会动 lock 里的其它包

需要显式加:

-W

3️⃣ 老项目升级依赖的正确姿势

  • 不要死磕最新版
  • 选择 “生态可接受”的最后可用版本
  • 控制升级范围
  • 一步一步来

八、最终结论

**在 PHP 7.2 + Composer 2.x 环境下,
成功安装 aws/aws-sdk-php 的关键不是版本号本身,
而是正确处理 Composer 的依赖锁与更新策略。**

九、附:最终成功命令(备忘)

composer update aws/aws-sdk-php guzzlehttp/promises --with-all-dependencies

指纹分析开源工具库

✅ 1. FingerprintJS BotD — 浏览器指纹 + Bot 检测(开源)

📌 GitHub 上的 BotD 是一套开源的浏览器指纹与机器人检测库(客户端 JS 采集),适合服务端校验配合使用。它能收集浏览器端特征用于判断是否自动化环境(Selenium、无头浏览器等)。 (GitHub)

  • 特点

    • 开源、MIT 授权
    • 收集和分析浏览器端特征
    • 可结合服务端 API 验证结果
    • 适合作为 bot/爬虫判定前端埋点
  • 用途

    • 在服务端判断客户端是否像机器人
    • 结合 CC 防护分数作风险评估
    • 在 Gin/Express 等应用中接收指纹信息并分析

📍 GitHub: https://github.com/fingerprintjs/BotD (GitHub)

⚠️ 注意:BotD 是客户端采集库,还需要服务端验证配合,适合做指纹 + 行为评分基础。


✅ 2. AntiBotDetector(辅助被动识别工具)

虽然不是专门针对 CC 攻击的库,但 AntiBotDetector 是一个开源项目,能够识别诸如 Cloudflare、Akamai、reCAPTCHA、DataDome 等防护系统,并用于 bot 识别链路。 (Reddit)

  • 特点

    • 依据检测逻辑识别流量是否来自自动化工具
    • 원래用于爬虫/反防护分析
    • 可集成到抓取/防护策略中

👉 可作为辅助分析库在服务端检测请求真实度。


⚠️ 3. WebDecoy TLS Fingerprint SDK(商业 + SDK)

WebDecoy 提供了 TLS 指纹 SDK(包括 Go、Node、PHP 版本),用于从网络层(TLS 握手)识别客户端异常模式。虽不是完全开源,但提供 SDK 可用于服务端集成。 (webdecoy.com)

它的功能包括:

  • JA3/JA4 TLS 指纹分析
  • 识别非标准客户端(如 bots / 自动化工具)
  • 可做 CC 风险识别前置条件

👉 适合想在服务端做TLS层指纹识别的工程场景。


📌 4. 自己构建 Fingerprint/CC 判断模块(通用开源思路)

除了现成库,很多实际工程上是这样组合的:

✅ TLS / JA3 指纹提取

  • 用于识别不同客户端 TLS 行为
  • 有多个开源工具/库可用来收集 JA3 / 指纹

✅ HTTP Header 指纹

  • 排列顺序、字段组合作为特征向量
  • NGINX/Lua、Go Middleware 等都可以提取

✅ 行为 + 模式分析

通过统计模型或简单规则分析:

  • 请求间隔分布(随机 vs 固定)
  • 路径访问熵(重复 vs 正常路径)
  • 参数模式(固定 vs 随机)

这些思路常被写成开源或自研库构建 CC 检测规则。 (CSDN博客)


🧠 注意点:开源 Fingerprint 系统 vs 工业级指纹

方案开源度实用性需要额外组件
FingerprintJS/BotD⭐⭐⭐⭐⭐需要前端 JS + 服务端验证
AntiBotDetector⭐⭐适合爬虫检测辅助
自研指纹⭐⭐⭐⭐⭐⭐⭐⭐⭐需数据 + 模型
商用 SDK(WebDecoy 等)⭐⭐⭐⭐⭐⭐取决于授权

📌 总结

可以利用开源库做服务端识别 / 指纹分析

  • BotD:开源浏览器指纹 + bot 检测库(MIT)(GitHub)
  • AntiBotDetector:用于检测反爬/防护链路(辅助)(Reddit)

⚠️ 这些库大多:

  • 不是一条龙的 CC 防护平台
  • 更像采集与判别基础组件
  • 需要结合策略、阈值、行为模型、分数机制构成整体防护系

[踩坑] 一次 SSH + Git + SourceTree 代理

—— 从 HTTP Basic deniedcorkscrew not found

场景背景:

  • 内部 Git(GitLab / 私有 Git)
  • 需要 SSH + 代理 IP 才能访问
  • 客户端使用 SourceTree(macOS)
  • 遇到一系列看似不相关、实际强关联的问题

一、问题现象汇总

在一次看似“很普通”的 Git fetch 中,连续遇到多个问题:

1️⃣ HTTP 拉取失败

HTTP Basic: Access denied.
you're required to use a token instead of a password

2️⃣ 切换 SSH 后,测试连接失败

ssh -T git@git.topu-internal.dev

报错:

zsh:1: command not found: corkscrew
Connection closed by UNKNOWN port 65535

3️⃣ SourceTree 无法正常 fetch / pull

即使已经:

  • 配置了 SSH Key
  • Remote URL 改成了 git@xxx

二、问题本质拆解(非常关键)

❗ 本质 1:HTTP 密码认证被禁用

这是 Git 平台的安全策略升级,不是客户端问题:

  • ❌ 用户名 + 密码
  • ❌ HTTP Basic Auth
  • ✅ Access Token
  • ✅ SSH Key(推荐)

👉 解决方向:必须切 SSH 或 Token


❗ 本质 2:SSH 实际走了代理,但代理命令不存在

错误信息里最关键的一句是:

command not found: corkscrew

说明:

  • SSH 配置中 使用了 ProxyCommand corkscrew ...
  • 但系统中 根本没安装 corkscrew
  • SSH 无法建立 TCP 通道
  • 最终报出一个“看起来很玄学”的端口错误:
Connection closed by UNKNOWN port 65535

⚠️ 这个 65535 不是目标端口,而是 ProxyCommand 失败后的兜底行为。


三、为什么会出现 corkscrew?

这是一个历史遗留问题

  • 早期 SSH 走 HTTP 代理的标准工具:corkscrew
  • 很多旧教程 / 旧配置默认用它
  • 但在现代系统中:

    • ❌ 默认不安装
    • ❌ 维护基本停止
    • ❌ 只支持 HTTP CONNECT

四、正确且现代的解决方案(推荐)

✅ 使用 nc(netcat)作为 ProxyCommand

这是 OpenSSH 官方推荐方式,兼容性最好。


五、最终正确配置(核心)

~/.ssh/config

Host git.topu-internal.dev
    HostName git.topu-internal.dev
    User git
    ProxyCommand nc -X 5 -x 127.0.0.1:1080 %h %p

说明:

参数含义
-X 5SOCKS5 代理
-x代理地址
%h %p目标主机和端口
如果是 HTTP 代理:
ProxyCommand nc -X connect -x 127.0.0.1:8080 %h %p

六、验证方式(非常重要)

1️⃣ 终端验证(最权威)

ssh -T git@git.topu-internal.dev

成功应看到:

Welcome to GitLab, @username!

2️⃣ 查看 SSH 实际使用的代理命令

ssh -G git.topu-internal.dev | grep -i proxy

输出示例:

proxycommand nc -X 5 -x 127.0.0.1:1080 %h %p

👉 说明配置已生效


七、SourceTree 是否支持 SSH + 代理?

完全支持,而且是原生支持。

前提条件只有 3 个:

  1. Remote URL 是 SSH:
git@git.topu-internal.dev:group/repo.git
  1. SourceTree 使用 OpenSSH
SourceTree → 设置 → Git → SSH 客户端:OpenSSH
  1. 终端 ssh -T 能通
只要终端能通,SourceTree 一定能通
SourceTree 本质只是调用 Git + OpenSSH

八、为什么之前会误以为是 SourceTree 的问题?

因为这类问题的错误传播路径很迷惑

代理命令不存在
  ↓
SSH 建立失败
  ↓
Git fetch 失败
  ↓
SourceTree 提示网络 / 认证错误

👉 真正的根因藏在 SSH 的 ProxyCommand 里


九、排错 Checklist(以后直接对)

# 1. 是否还在用 https
git remote -v

# 2. SSH 是否能直连
ssh -T git@git.xxx

# 3. SSH 实际用的代理
ssh -G git.xxx | grep proxy

# 4. corkscrew 是否还被引用
grep -R corkscrew ~/.ssh

十、经验总结

Git / SourceTree 出问题,先看 SSH,不要先怀疑 Git
代理相关问题,80% 是 ProxyCommand
nc > corkscrew(现代环境)

附:如果一定要用 corkscrew(不推荐)

brew install corkscrew

但强烈建议直接迁移到 nc

[踩坑] Swoole 常驻进程中使用 mysqli 的那些坑与正确姿势

关键词:Swoole、mysqli、MySQL、errno=110、send of 5 bytes failed、连接复用、死连接

在 PHP-FPM 时代,mysqli 几乎不会暴露什么“连接管理问题”。
但一旦进入 Swoole / 常驻进程 / RPC 服务,很多团队会突然遇到类似这样的错误:

send of 5 bytes failed with errno=110 Connection timed out

而且往往具备以下特征:

  • ❌ 不是高并发
  • ❌ 不是慢 SQL
  • ❌ 不是攻击
  • 每天稳定、零散出现
  • ✅ 重试一次就恢复

本文结合真实生产案例,讲清楚 为什么会出现、为什么“你明明重连了还有报错”、以及如何正确处理


一、错误现象解析

典型错误日志:

Links\Db::getMysql(): send of 5 bytes failed with errno=110 Connection timed out

关键信息拆解

  • errno=110
    Linux 网络错误码:ETIMEDOUT,TCP 发送超时
  • send of 5 bytes
    MySQL 协议层的极小包:

    • ping
    • handshake
    • 协议头

👉 说明问题发生在“连接层”,而不是 SQL 执行阶段


二、为什么在 Swoole 中特别容易出现?

1️⃣ Swoole 是常驻进程

  • Worker 生命周期:小时 / 天
  • MySQL 连接生命周期:分钟级(wait_timeout、NAT、SLB)

生命周期不匹配是根因。


2️⃣ mysqli 会复用“已经死掉的连接”

mysqli 的行为是:

  • PHP 对象还在
  • TCP 连接可能已经被回收
  • mysqli 不会主动告诉你“我已经死了”

直到你下一次使用它。


3️⃣ “每天几百条”的错误是一个强信号

如果你看到的是:

  • 少量
  • 长期
  • 稳定
  • 与流量无强相关

那几乎可以直接判定:

这是“空闲连接被回收后再次复用”

三、为什么“我明明重连了,还是会报错?”

这是最容易被误解的地方。

❗关键事实

mysqli 的 ping() / 状态检测,本身就会触发底层 send()

示例代码:

if (!$mysqli->ping()) {
    $mysqli = new mysqli(...);
}

实际执行顺序(真实世界)

调用 ping()
→ mysqli 内部立刻向 socket 发送 5 bytes
→ TCP 已经半死
→ errno=110 产生(Notice / Warning)
→ PHP 才返回控制权
→ 你的重连逻辑才开始执行

👉 错误发生在“检测阶段”,不是“使用阶段”

所以你看到的是:

  • 日志里有 notice
  • 但业务逻辑已经成功重连并继续执行

这是一个时序问题,不是代码逻辑错误


四、mysqli 在 Swoole 中的三大坑

❌ 1. 共享一个 mysqli 实例给多个协程

static $mysqli;

后果:

  • 非协程安全
  • 随机超时
  • 状态错乱

❌ 2. 依赖 mysqli 自动重连

mysqli.reconnect = On

这是 不可靠的历史遗留特性,在现代环境中不应使用。


❌ 3. 把持久连接当“优化手段”

在 Swoole 中:

  • 长连接 × 常驻进程
  • = 死连接概率翻倍

五、生产级正确做法

✅ 方案一:在统一入口做连接健康兜底(推荐)

public static function getMysql(string $name): \mysqli
{
    $mysqli = self::$pool[$name] ?? null;

    try {
        if (!$mysqli || !$mysqli->ping()) {
            if ($mysqli instanceof \mysqli) {
                @$mysqli->close();
            }
            $mysqli = self::createMysql($name);
            self::$pool[$name] = $mysqli;
        }
    } catch (\Throwable $e) {
        if ($mysqli instanceof \mysqli) {
            @$mysqli->close();
        }
        $mysqli = self::createMysql($name);
        self::$pool[$name] = $mysqli;
    }

    return $mysqli;
}

📌 所有业务代码不感知重连逻辑


✅ 方案二:定时任务 / 低频任务直接“每轮新连接”

$mysqli = new mysqli($host, $user, $pass, $db);

适用场景:

  • cron
  • 定时 RPC
  • 结算 / 对账

成本可接受,逻辑最简单。


✅ 方案三(最推荐):使用 Swoole 官方 MySQL 客户端

$mysql = new Swoole\Coroutine\MySQL();
$mysql->connect([
    'host' => '127.0.0.1',
    'user' => 'user',
    'password' => 'pass',
    'database' => 'db',
]);

优势:

  • 协程安全
  • 错误是返回值而不是 PHP notice
  • 官方支持连接池

六、MySQL 参数建议(配合使用)

wait_timeout = 60
interactive_timeout = 60
net_read_timeout = 30
net_write_timeout = 30

目的不是延长连接寿命,而是:

让死连接尽早暴露,而不是潜伏

七、关于日志:要不要“消灭” notice?

现实建议

  • 业务已正确重连 → 无需恐慌
  • 可以:

    • 降级为 info
    • 或精准吞掉 errno=110
set_error_handler(function ($errno, $errstr) {
    if (strpos($errstr, 'send of') !== false &&
        strpos($errstr, 'errno=110') !== false) {
        return true;
    }
    return false;
});

八、一句话总结

**mysqli 在 Swoole 里不是不能用,
而是不能再用“PHP-FPM 时代的用法”。**

九、适用人群

  • Swoole / RPC / TCP 服务
  • 常驻 Worker
  • MySQL 偶发超时但无法复现
  • 日志中出现 send of 5 bytes failed

深度解析 CC 攻击 & Cloudflare 防护实战指南

一、什么是 CC 攻击?

CC(Challenge Collapsar)攻击 是一种典型的 应用层 DDoS 攻击,主要针对 Web 服务(HTTP / HTTPS)。

与传统 DDoS 不同,CC 攻击并不是单纯靠大流量压垮带宽,而是:

利用大量“看似合法”的 HTTP 请求,消耗服务器 CPU、线程、数据库连接等资源,最终导致服务不可用。

CC 攻击的核心特征

  • 请求是 真实 HTTP 请求
  • 访问路径往往是:

    • /
    • /login
    • /api/*
  • 请求频率高,但不一定“爆发式”
  • 常伴随:

    • 随机 User-Agent
    • 随机 Referer
    • 多 IP(甚至 10w+)

二、为什么 CC 攻击特别难防?

1️⃣ IP 数量巨大

现代 CC 攻击往往使用:

  • 僵尸网络(Botnet)
  • 代理池 / VPN
  • 云主机资源

👉 单纯靠 封 IP 几乎无效。


2️⃣ 请求“看起来很正常”

  • GET / POST 都合法
  • 返回 200
  • 不触发传统防火墙规则

👉 很容易绕过基于规则的拦截。


3️⃣ 攻击消耗的是应用资源

CC 攻击直接打到:

  • PHP-FPM
  • Java 线程池
  • MySQL / Redis

即使带宽没满,服务也会先挂


三、Cloudflare 为什么能有效防 CC?

Cloudflare 的优势不在于“封 IP”,而在于 行为识别 + 边缘拦截

1️⃣ 流量先到 Cloudflare 边缘节点

用户 / 攻击者
      ↓
Cloudflare 边缘节点
      ↓
你的服务器

👉 大部分恶意请求 在边缘就被拦掉,不会消耗你任何资源。


2️⃣ 行为分析(不只看 IP)

Cloudflare 会综合分析:

  • 请求频率
  • 访问路径模式
  • Header 行为
  • Cookie / JS 执行情况
  • TLS 指纹(JA3)

即使攻击来自 10 万不同 IP
只要行为异常,也能被识别为攻击。


3️⃣ JS / Managed Challenge

对可疑请求自动下发挑战:

  • JS Challenge
  • Managed Challenge(新版无感)

✔ 真人浏览器几乎无影响
❌ 自动化脚本大量失败


四、Cloudflare 防 CC 核心配置(实战)

1️⃣ 开启 Bot Fight Mode(免费必开)

路径:

Security → Bots → Bot Fight Mode → ON

作用:

  • 自动拦截明显的自动化流量
  • 对正常用户影响极小

2️⃣ 配置 Rate Limiting(关键)

示例:限制 API 接口

规则示例:

URL: /api/*
10 秒内 > 30 次

触发动作:

  • Managed Challenge
  • 或 Block(更激进)

👉 可有效拦截高频 CC 请求。


3️⃣ WAF 规则(推荐)

示例 1:可疑 Bot 行为

(cf.client.bot)
→ Managed Challenge

示例 2:高风险接口加强防护

(http.request.uri.path contains "/login")
→ Challenge

4️⃣ 国家 / 地区限制(可选)

如果你的业务只面向特定地区:

(not ip.geoip.country in {"CN","JP"})
→ Challenge

五、一个非常重要但常被忽略的点

❗ 只允许 Cloudflare IP 访问源站

如果攻击者 绕过 Cloudflare 直连你的服务器
所有防护都会失效。

正确做法:

  • 防火墙只放行 Cloudflare IP
  • 拒绝所有其他来源

这是 Cloudflare 防护的 生命线


六、Cloudflare + 自建防护的最佳组合

Cloudflare 不是“万能”,最佳实践是 多层防御

Cloudflare(边缘拦截)
    ↓
Nginx 限速(兜底)
    ↓
Redis 应用层限流

应用层示例

  • IP 10 秒 10 次
  • 超限临时封禁
  • 异常行为记录

👉 即使少量攻击穿透 Cloudflare,也能被兜住。


七、Cloudflare 能防住哪些 CC?

✅ 非常擅长

  • 大规模代理 CC
  • 高并发 HTTP Flood
  • 普通脚本攻击

⚠️ 有一定成本

  • Headless Chrome
  • 慢速、低频 CC

❌ 几乎无法完全防

  • 真人参与的攻击(成本极高)

八、总结

CC 攻击的本质,是“用合法请求消耗你的资源”。

而 Cloudflare 的价值在于:

  • 把攻击挡在你服务器之外
  • 用行为模型对抗 IP 泛滥
  • 极大降低防御成本
">