Harbeter's Blog
type: channel
link: https://shl528.cn/
description: Live like the Wind
items: Arkime部署文档
items: 主流Agent文件目录解析
items: Web架构梳理
items: SSL证书颁发机制与方式
items: ModSecurity搭建文档
items: HTTP 2协议流程
items: Socks5与HTTP代理
items: NAT穿透
items: HTTP协议发展史
items: Go中的结构体与接口

Arkime部署文档
type: item
date: 2026-06-23T15:14:00+00:00
description: 最近在部署Arkime，可给折磨坏了，终于试了各种错，找出了一个比较优雅的部署方案。Arkime简介Arkime（原Moloch）是一套面向大规模网络流量分析与取证场景设计的开源网络流量捕获平台，其核心目标是在保证全流量留存的前提下，实现PB级网络数据的高效索引、检索与关联分析。Arkime采用“全包捕获（Full Packet Capture）+元数据索引”的架构设计，通过高速抓包引擎持续采集网络数据包，并将原始PCAP文件存储至本地磁盘，同时提取会话元数据（Session Metadata）写入OpenSearch或Elasticsearch索引，从而兼顾长期流量存储能力与快速查询性能。在架构层面，Arkime主要由Capture、Viewer和OpenSearch三部分组成。其中Capture组件负责网络数据包采集、协议解析和会话重组；Viewer组件提供Web管理界面及REST API接口；OpenSearch负责存储和检索流量元数据索引。Arkime支持对HTTP、DNS、TLS、SMTP、SSH等多种常见协议进行深度解析，并能够基于IP地址、端口、域名、URL、证书指纹、用户代理等字段进行复杂查询和关联分析。与传统网络分析工具相比，Arkime最大的特点在于其将原始流量与索引数据进行解耦存储。用户在检索时首先通过OpenSearch快速定位目标会话，再根据索引记录回溯对应的PCAP数据，从而避免直接扫描海量数据包带来的性能瓶颈。这种设计使Arkime能够在数十TB甚至PB级流量环境下仍保持较高的查询效率。在实际应用中，Arkime广泛部署于安全运营中心（SOC）、网络取证平台以及威胁狩猎环境中，常与Zeek、Suricata等安全分析工具协同工作。其中Arkime负责流量采集与回溯取证，Zeek负责生成高层协议日志和行为分析结果，Suricata负责实时威胁检测与告警，三者共同构建覆盖“检测—分析—溯源”全流程的网络安全监测体系。这里，我们选择用Docker方式部署，因为Docker环境隔离做的很漂亮，而且不用考虑乱七八糟的依赖，这里我们一共用到两个Docker镜像，一个是Arkime的，另一个是Opensearch的。创建目录首先创建 Arkime 所需的数据目录，用于后续 Docker 容器挂载：sudo mkdir -p /opt/arkime/{raw,etc,opensearch}目录说明如下：目录作用/opt/arkime/raw存储 Arkime 抓取的原始 PCAP 数据/opt/arkime/etc存储 Arkime 配置文件、证书等运行配置/opt/arkime/opensearch存储 OpenSearch 数据库持久化数据通过将数据挂载到宿主机目录，即使容器重建或升级，抓包数据和数据库数据也不会丢失。编写 Docker Compose 文件Arkime 采用分组件架构运行，核心组件包括：Capture：负责网络流量采集与 PCAP 存储；Viewer：提供 Web 界面以及数据查询功能；OpenSearch：负责索引存储和会话检索。下面给出一个最小可运行版本：services: opensearch: image: opensearchproject/opensearch:2.19.1 container_name: opensearch environment: - discovery.type=single-node - OPENSEARCH_INITIAL_ADMIN_PASSWORD=ArkimeOpenSearch123! - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g volumes: - /opt/arkime/opensearch:/usr/share/opensearch/data ports: - "9200:9200" restart: unless-stopped capture: image: ghcr.io/arkime/arkime/arkime:v6-latest container_name: arkime-capture network_mode: host cap_add: - NET_ADMIN - NET_RAW - SYS_NICE command: > /opt/arkime/bin/docker.sh capture volumes: - /opt/arkime/raw:/opt/arkime/raw - /opt/arkime/etc:/opt/arkime/etc restart: unless-stopped depends_on: - opensearch viewer: image: ghcr.io/arki…

主流Agent文件目录解析
type: item
date: 2026-06-16T14:59:00+00:00
description: 本篇文章主要梳理一下现在主流的Agent框架如何做文件系统管理的，如会话、记忆如何存放，如何做项目隔离等等。引言随启随用的成熟Agent框架大大简化了我们的工作流，但其目录结构和文件组织形式却常被忽略，所以本文将针对Claude Code、Codex和Copilot三大主流Vibe Coding Agent框架的文件系统进行简要介绍。Claude CodeClaude Code的目录分为两种，即全局~/.claude/和项目级.claude/，且项目级配置的优先级高于全局配置。首先，我们以全局文件目录入手，展示Claude Code的文件目录是如何组织的~ # 用户目录 ├── .claude.json # 自动生成的本地应用状态文件，用于保存登录会话、MCP配置等运行时信息 └── .claude/ # 全局个人配置，跨所有项目生效 ├── CLAUDE.md # 全局个人记忆，每次会话与项目CLAUDE.md同时加载冲突时项目级优先 ├── settings.json # 全局默认设置包括权限/hooks/模型/env，会被项目级settings.json覆盖 ├── keybindings.json # 自定义快捷键映射，启动时加载，文件变更后热重载 ├── stats-cache.json # /usage展示的token与费用累计统计缓存，永久保留，不自动清理 ├── remote-settings.json # 企业managed settings本地缓存；每次启动刷新，永久保留 ├── history.jsonl # 所有会话中输入过的prompt历史，支持上箭头跨会话回调，永久保留 ├── .last-cleanup # 记录上次自动清理的时间戳，启动时用于判断是否触发清理任务 ├── .last-update-result.json # 记录上次自动更新的结果即成功/失败/版本号，用于显示更新提示 ├── settings.json.ds # settings.json的结构说明文件文件，用于IDE做自动补全和校验 ├── themes/ # 自定义颜色主题 │ └── <name>.json # 通过/theme创建或手写，/theme <name>切换 ├── rules/ # 全局规则 │ └── *.md # 可以随意命名，无paths参数每次启动时加载，有paths匹配文件进入上下文时加载 ├── skills/ # 全局个人技能，任意项目中用/<name>调用，或由Claude自动触发 │ └── <name>/ # name为自定义字段 │ ├── SKILL.md # description决定Claude是否自动触发，支持$ARGUMENTS │ └── [辅助文件] # 与SKILL.md捆绑的参考文档、模板、脚本等，Claude可按需读取 ├── commands/ # 单文件命令，旧机制，现已与skills合并，并建议新工作流改用skills │ └── <name>.md # /<name>调用；同名时skill优先于command ├── agents/ # 用于定义具有独立上下文与工具权限的子Agent │ └── <name>.md # @<name>手动调用或由Claude自动委派 ├── workflows/ # 由Claude生成或用户定义的可执行工作流脚本，用于把多步骤任务自动化成一个命令 │ └── <name>.js # 启动时加载为/<name>命令，会被同名项目级workflow覆盖 ├── agent-memory/ # memory:user子代理的跨项目持久记忆 │ └── <name>/ # name与agents中的保持一致 │ └── MEMORY.md # 子Agent自动维护，启动时注入system prompt，注入前200行或25KB ├── output-styles/ # 用于定义回答的语气、结构和表达方式，会话启动时注入system prompt │ └── *.md # 通过settings.json中的outputStyle字段设置启用 ├── plugins/ # 插件市场与已安装插件数据，由claude plugin命令管理，不建议手动编辑 │ ├── config.json # 全局插件配置 │ ├── installed_plugins.json # 已安装插件列表 │ ├── known_marketplaces.json # 插件市场源配置 │ └── marketplaces/ # 已克隆的插件市场内容与插件文件 ├── ide/ # IDE集成相关的锁文件与通信文件，供IDE扩展与CLI进程协调使用 ├── sess…

Web架构梳理
type: item
date: 2026-06-11T15:09:00+00:00
description: 本篇博文主要梳理Web网站框架和技术栈，旨在更深刻理解Web工作原理，助于渗透测试。其实网站种类很多，架构也五花八门，这里从大类角度，将网站分为静态网站和动态网站。静态网站静态网站是最简单的一类网站。其核心特点是：Web服务器直接返回已经存在的HTML文件，而不会在访问时动态生成页面内容。访问流程如下：浏览器->Web服务器->HTML文件 例如客户端直接访问https://example.com/index.html，服务器直接读取网站目录下的index.html文件并返回给浏览器客户端。这里有个常见的误区，即只有动态网站才会使用Web服务器。实际上，无论是静态网站还是动态网站，都离不开Web服务器。Web服务器（Web Server）本质上是一种能够接收HTTP请求并返回HTTP响应的软件。例如，当用户在浏览器中访问某个URL，浏览器会向服务器发送HTTP请求，Web服务器接收到请求后，会根据配置找到对应的资源并返回给浏览器。常见的Web服务器包括Nginx、Apache、Caddy和IIS等，常用于本地测试的Python http.server模块也可看作Web服务器。此外，还有一种比较特殊的、经常用于搭建个人博客或项目文档的网站部署方式，即静态网站托管服务（Static Website Hosting Service）。这类服务通常由平台提供现成的服务器、HTTPS证书、CDN加速以及自动部署能力，用户只需上传或同步静态网站文件即可完成发布，无需自行购买服务器或配置Web服务器。常见的静态网站托管服务包括GitHub Pages、Cloudflare Pages、Netlify和Vercel等。由于这类服务通常仅负责托管HTML、CSS、JavaScript、图片等静态资源，无法直接运行PHP、Java、Python等服务端程序，因此本质上仍属于静态网站的范畴。现代开发中，静态网站早已脱离了“纯手写HTML”的原始阶段。开发者通常使用静态网站生成器（Static Site Generator，SSG），常见的如Hexo、Hugo、VitePress）来自动生成页面。此外，借助JAMstack架构，静态网站可以通过JavaScript在客户端调用第三方API或Serverless函数。这意味着，即使网站本质上是静态托管的，依然可以实现动态评论、在线搜索、用户登录等丰富的交互功能。HTML、CSS和JavaScript是构建网页最基础的前端技术，也是绝大多数网站前端部分的核心组成。静态网站具有部署简单、访问速度快、服务器资源消耗低以及安全风险较少等显著优点。然而，其缺点也同样明显：由于页面内容固定，导致用户的交互能力有限，一旦数据需要更新，就必须重新生成整个页面。动态网站动态网站主要是指页面内容并非预先生成并存储为固定的HTML文件，而是在用户访问时由服务端程序或客户端脚本根据请求动态生成。与静态网站相比，动态网站能够根据用户身份、数据库内容以及业务逻辑实时返回不同的页面和数据。通信流程如下所示：浏览器->Web服务器->应用服务器->数据库->HTML代码 传统一体化传统一体化是早期Web开发最常见的模式。在这种架构下，前端页面和后端业务逻辑运行在同一个应用系统中，由后端程序直接生成HTML页面并返回给浏览器。一个较为完整的动态网站架构，通常包含Web服务器、应用服务器和数据库等组件。其中，应用服务器（Application Server）负责处理业务逻辑、访问数据库并动态生成HTML代码返回给Web服务器，最终由Web服务器返回给客户端。在实际部署中，并非所有动态网站都会包含上述全部组件。例如一些小型网站可以直接由Tomcat、Spring Boot或Node.js运行时对外提供Web服务，而无需额外部署Nginx或Apache等独立Web服务器。这是因为许多现代应用服务器或运行环境本身已经具备处理HTTP请求和返回HTTP响应的能力；对于数据量较小或内容固定的场景，网站甚至可以不接入数据库。此外，在LAMP（Linux+Apache+MySQL+PHP）和LNMP（Linux+Nginx+MySQL+PHP）等经典PHP架构中，通常不存在类似Tomcat、WebLogic等独立的应用服务器。Web服务器会根据配置规则匹配请求，通常通过请求文件的后缀名等，将PHP请求转发给PHP运行环境，如mod_php或PHP-FPM执行，PHP解释器负责处理业务逻辑、访问数据库并生成动态页面。从功能上看，PHP运行环境承担了应用服务器的职责，因此也可以视为应用服务器层的一种实现形式。当前互联网网站最常见的服务端技术生态主要包括PHP、Java、Python、.NET以及Node.js，它们覆盖了绝大多数企业网站、管理系统和互联网应…

SSL证书颁发机制与方式
type: item
date: 2026-06-04T15:42:00+00:00
description: 这篇文章讲一下申请SSL证书的几种流程，包括Web、ACME等方式。SSL证书签发机制CA（Certificate Authority，证书颁发机构）是互联网中的“可信第三方”，负责验证域名所有权并签发SSL/TLS证书。CA的核心职责不是为域名生成密钥对，而是对用户生成的公钥进行签名，证明这个公钥确实属于某个身份，如域名、企业等。浏览器之所以信任某个网站的证书，不是因为信任网站本身，而是因为该证书最终能够追溯到浏览器或操作系统内置的受信任根CA。像Let's Encrypt、DigiCert、Sectigo等都是被主流浏览器信任的公开CA，而腾讯云、阿里云等证书申请平台通常只是代办或管理平台，真正签发证书的仍然是这些CA。判断一个CA是否可信，最简单的方法就是看它的根证书是否被Chrome、Firefox、Windows、macOS等主流系统和浏览器默认信任。此外，如果一个恶意SSL证书被浏览器/系统信任，那么这个恶意证书所有者便可以伪装服务端，对客户端发起中间人攻击。Web端申请这是最传统的SSL证书申请方式，流程如下：登录证书申请网站——>填写申请域名信息——>完成域名验证——>下载证书——>部署证书 常见的证书申请平台包括ZeroSSL、腾讯云SSL证书服务、阿里云SSL证书服务等。其中，DNS TXT记录验证是目前最常见的方式之一，通过在域名的DNS配置中添加指定的TXT记录，证书颁发机构（Certificate Authority，CA）即可验证申请者对域名的控制权。ACME端申请ACME 全称：Automated Certificate Management Environment，中文一般翻译为自动化证书管理环境，它本身是一种协议，由IETF（Internet Engineering Task Force）制定。除了ACME，IETF还制定了HTTP、DNS、TCP/IP等等众多协议，包括先前博文提到的RFC文档也是这个组织发布的。相较于繁琐的Web控制台申请方式，ACME是目前最主流的SSL证书申请方式，其大致流程如下：安装ACME客户端——>向CA发起证书申请——>CA下发Challenge——>客户端自动完成域名验证——>CA签发证书——>客户端自动下载证书——>部署证书 在RFC 8737中，ACME的Challenge一共就3种，如下表所示：Challenge验证方式端口需求支持泛域名HTTP-01Web 服务80否DNS-01DNS 记录无是TLS-ALPN-01TLS 服务443否下面详细介绍3种Challenge的认证机制HTTP-01在ACME协议中，HTTP-01 Challenge是最常见的域名所有权验证方式。其核心思想是：通过验证申请者是否能够控制目标域名对应的Web服务，来证明其拥有该域名的管理权限。当客户端向 CA 申请证书时，CA 会生成一个随机的 Challenge Token，并要求客户端将该 Token 按照规定格式放置在 Web 服务器的指定路径下：http://<domain>/.well-known/acme-challenge/<token> 随后，CA会主动向该URL发起HTTP请求。如果能够成功获取到预期内容，则认为申请者拥有该域名的控制权，验证通过后即可签发证书。这个临时的Web服务，通常由ACME客户端自动化轻量化提供，所以，通过这种方式签发SSL证书时，一定要确保主机80端口没有被占用。DNS-01DNS-01是ACME协议中基于DNS记录的域名验证方式。与HTTP-01不同，DNS-01不依赖Web服务，而是通过验证指定DNS TXT记录的内容来确认申请者拥有目标域名的控制权。由于域名解析记录通常只有域名管理者才能修改，因此能够成功创建指定TXT记录即可证明对该域名拥有管理权限。当客户端向 CA 申请证书时，CA 会生成一个随机 Challenge Token，并要求客户端在目标域名下创建一条特定的 TXT 记录：_acme-challenge.<domain> 客户端将计算得到的 Challenge Value 写入该 TXT 记录后，CA 会主动查询 DNS 服务器。如果查询结果与预期内容一致，则验证通过，CA 随后签发证书。通过ACME客户端申领时，由于要配置DNS解析，所以要提供DNS服务商的API凭证，以确保ACME客户端能够自动化配置DNS解析。这种方式其实和Web端申领SSL证书的原理一样。TLS-ALPN-01与HTTP-01通过HTTP文件验证不同，TLS-ALPN-01通过TLS握手过程中的ALPN（Application-Layer Protocol Negotiation）扩展完成域名所有权验证。该方式要求申请者能够控制目标域名对应…

ModSecurity搭建文档
type: item
date: 2026-04-29T14:19:00+00:00
description: 好久没有更新博客了，最近在忙很多事情，今天突来的兴致，把之前梳理的一些文档整理成博客，今天先搞一个ModSecurity+Nginx搭建流程。补充OpenResty由于原版的Nginx不能对请求进行精细化控制，所以出现了OpenResty，即Nginx+Lua。OpenResty可以通过Lua语言，写相应的脚本，从而实现将Nginx接受的HTTP请求，原封不动的转为文本，然后发送给WAF的API。OpenResty安装ModSecurity与Nginx几乎一模一样，其版本与Nginx版本对应。make时要用gmakewget https://openresty.org/download/openresty-1.25.3.2.tar.gz # 编译用gmake gamke gamke install 1. 编译安装ModSecurity参考ModSecurity官方文档，找到指定系统的编译安装指令。Ubuntu 22.04参考22.10，下述补充了OpenResty编译需要的库。sudo apt-get install git g++ apt-utils autoconf automake build-essential libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre2-dev libtool libxml2-dev libyajl-dev pkgconf zlib1g-dev wget libssl-dev libpcre3 libpcre3-dev curl git clone https://github.com/owasp-modsecurity/ModSecurity cd ModSecurity/ git submodule init git submodule update sh build.sh ./configure --with-pcre2 make make install ./configure能正常输出依赖结果即可2. 下载ModSecurity-nginx连接器git clone https://github.com/owasp-modsecurity/ModSecurity-nginx 只需下载，无需做任何操作。3. 编译安装Nginx注：由于WAF需要，应该将Nginx替换为OpenResty。使用ModSecurity必须用源码方式安装Nginx，注意不同ModSecurity版本对Nginx的要求wget https://nginx.org/download/nginx-1.26.1.tar.gz tar -zxvf nginx-1.26.1.tar.gz cd nginx-1.26.1 将下载好的ModSecurity-nginx第三方模块静态编译进Nginx，同时开启NginxSSL等模块，这些模块在APT方式安装的二进制Nginx里面都已经被编译进去了，自己编译Nginx时，需要手动指定。./configure --add-module=/path/to/ModSecurity-nginx --with-http_ssl_module make make install 如果报错没有发现OpenSSL库，则需安装，Ubuntu安装命令如下：apt install libssl-dev 安装完成后，找到configure中显示的nginx安装目录，执行下述命令查看当前Nginx配置cd /usr/local/nginx ./sbin/nginx -V 回显如下表示正常：root@c4dcf83416c9:/usr/local/nginx/sbin# ./nginx -V nginx version: nginx/1.26.1 built by gcc 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04.2) built with OpenSSL 3.0.2 15 Mar 2022 TLS SNI support enabled configure arguments: --add-module=/root/ModSecurity-nginx --with-http_ssl_module 4. 下载ModSecurity配置文件ModSecurity是根据某些规则运作的，所以要下载相应的规则配置文件。git clone https://github.com/coreruleset/coreruleset.git 将最开始下载的ModSecurity的源码中的modsecurity.conf-recommended文件重命名为modsecurity.conf，将此文件同unicode.mapping一起移动到co…

HTTP 2协议流程
type: item
date: 2025-12-26T07:48:00+00:00
description: 最近在研究HTTP 2相关的协议，且大多数CDN都是默认HTTP 2，所以记录一下HTTP 2的详细通信流程。1、HTTP 2概述据Cloudflare统计，HTTP 2网站已经占到所有网站的50%，HTTP 3则占到了20.5%。所以HTTP 2在如今的网络环境中越来越重要。HTTP 2的RFC文档在此。HTTP 2通信常被分为两类，即h2c和h2。h2c：是基于HTTP 1.1 Upgrade试探升级HTTP 2，或在C/S框架中客户端已知服务端支持HTTP 2，直接发送HTTP 2数据包，不论哪一种，其通信过程是都是明文的，这是极其不安全的，主流浏览器都不支持，也很少有人使用，所以这里我们不做讨论。h2：是被大多数人推广使用的，即在TLS的ClientHello包中的ALPN(Application Layer Protocol Negotiation)扩展中就声明支持h2，若服务端支持h2，则会选中该扩展，发回客户端。随后TLS连接建立完之后，就可以直接用HTTP 2通信了。2、帧结构HTTP 2的帧（Frame）结构由固定9字节帧头（Frame Header）和可变长度帧负载（Frame Payload）组成，定义于RFC 7540。字段长度说明Length24bitPayload长度Type8bit帧类型Flags8bit与帧类型相关的标志位R1bit保留位，必须为0Stream Identifier31bit流ID，0表示连接级帧Length：默认大小区间为为0~16,384(2^14)，一旦超过默认最大值2^14(16384)，发送方将不再允许发送，除非接收到接收方定义的SETTINGS_MAX_FRAME_SIZE（一般此值区间为2^14 ~ 2^24）值的通知Type：帧类型，共10种，如下表所示Flags：与帧类型相关的标志位，如END_STREAM表示流的一端关闭、END_HEADERS表示Header块结束、ACK表示确认SETTINGS或PING帧R：保留位，必须为0Stream Identifier：帧所属的流ID，0表示连接帧，如SETTINGS、PING、GOAWAY，奇数表示由客户端创建的流，偶数表示由服务端建立的流Type帧类型简介0x0DATA承载请求或响应的实体数据，受流量控制机制约束0x1HEADERS传输请求或响应的首部块，使用HPACK压缩0x2PRIORITY指定流的优先级和依赖关系0x3RST_STREAM异常终止一个已存在的流0x4SETTINGS交换连接级参数配置，仅使用流ID 00x5PUSH_PROMISE服务器推送时，声明即将发送的请求0x6PING连接保活、RTT测量，携带8字节不透明数据0x7GOAWAY通知对端即将关闭连接并指明最后可处理流0x8WINDOW_UPDATE流量控制，更新连接或流的发送窗口0x9CONTINUATION继续传输未在HEADERS/PUSH_PROMISE中完成的首部块HTTP 2本质上没有改变HTTP语义，只是将HTTP的Header和Body部分重新用帧结构封装了一下。HEADERS采用了HPCAK压缩算法，且仅有其使用。3、通信流程HTTP 2整体上还是遵循客户端请求-服务端响应的通信结构，只不过会多一些控制帧。首先，客户端先发送Magic序列，这是一个固定的24字节序列，表明其要用HTTP 2协议通信，然后紧接着必须发送SETTING帧，之后还可能衔接WINDOW_UPDATE帧，随后则是HEADERS和DATA，且HEADERS必须先于DATA。服务端在TLS握手结束后，立即同步发送SETTING帧。

Socks5与HTTP代理
type: item
date: 2025-11-04T11:54:00+00:00
description: 很多本地代理都为Socks5或者HTTP代理，本篇将从理论与实践角度，解释二者原理。本篇是代理技术鉴赏的一篇分支，更加细致的介绍了Socks5和HTTP代理。Socks5代理于RFC 1928文档中定义，其是Socks4的升级版，在其基础上添加了认证机制、UDP代理和IPv6支持，Socks4已被Socks5完全替代。Socks5代理TCP时，分为认证、请求和通信3个阶段。1、Socks5代理TCP1.1 TCP认证首先，客户端会与Socks5代理建立TCP三次握手，之后发送认证请求，格式如下：VERNMETHODSMETHODS111 to 255VER：表示socks协议版本，值为0x05NMETHODS：表示支持多少种验证方式，也就是后面METHODS字段的长度METHODS：表示支持的验证方式，最多可提供255种，最少提供1种验证方式。0x00 无须验证 0x01 GSSAPI 0x02 用户名/密码 0x03~0x7F IANA分配 0x80~0xFE 自定义验证方式 0xFF 没有可验证的方式（这种情况下socks服务器必须关闭连接）流量示例：socks5服务端收到消息后，选择一种认证方式并发送给客户端，格式如下：VERMETHOD11流量示例：然后进入选定的认证方法认证阶段，这里以用户名/密码认证方式为例，给出流量示例：这里面用户名为bigbyto，密码为123456，然后服务端回复。1.2 TCP请求客户端认证后，就可以开始发送连接请求了，格式如下：VERCMDRSVATYPDST.ADDRDST.PORT110x001Variable2VER：表示socks协议版本，值为0x05CMD：请求命令类型，表示客户端希望代理执行的操作0x01 CONNECT（TCP连接请求） 0x02 BIND（绑定端口，用于服务器反向连接） 0x03 UDP ASSOCIATE（UDP中继，用于UDP转发）RSV：保留字段，必须填0x00。 ATYP：目标地址类型0x01 IPv4地址（4字节） 0x03 域名（第一个字节为域名长度） 0x04 IPv6地址（16字节）DST.ADDR：目标主机地址，格式取决于ATYPDST.PORT：目标主机端口大多数情况下，ATYP为域名，即客户端将域名直接传输给Socks5服务端，由服务端做DNS解析，由于Socks5服务端通常也在本地，故由后续的代理软件决定，如全局模式，就是在远端节点做DNS解析。这种情况下，客户端并不知道域名具体的IP，只知道已经通过代理转发给目的地址。流量示例：客户端请求访问a.baidu.com的80端口。socks5服务端收到访问请求后，回复客户端，格式如下：VERREPRSVATYPBND.ADDRBND.PORT110x001Variable2VER：协议版本，固定值为0x05。REP：回复字段，表示服务器执行请求的结果：0x00 成功 0x01 通用SOCKS服务器故障 0x02 连接不被规则集允许 0x03 网络不可达 0x04 主机不可达 0x05 连接被拒绝 0x06 TTL过期 0x07 不支持的命令 0x08 不支持的地址类型 0x09~0xFF 未分配RSV：保留字段，固定为0x00。ATYP：后续地址的类型：0x01 IPv4地址 0x03 域名 0x04 IPv6地址BND.ADDR：服务器绑定的地址。BND.PORT：服务器绑定的端口号。在最常见的CONNECT情况下，BND.ADDR和BND.PORT通常为0.0.0.0:0，只表示“连接成功”，具体绑定信息无意义。而BIND和UDP ASSOCIATE情况下，表示实际地址和端口，用于后续数据通道建立。流量示例：1.3 TCP通信连接建立后，客户端就可以和正常访问目标服务一样通信了，此时socks5代理仅会改变传输层以下的数据，而应用层的数据将原封不动的转发给目标服务器。也就是说，现在就可以把socks5代理服务器，当作目标服务器通信了。流量示例：2、Socks5代理UDP2.1 UDP认证Socks5代理UDP时，认证阶段与代理TCP时一样，且认证过程用的TCP连接。2.2 UDP请求认证通过后，客户端发送UDP代理请求，格式如下：VERCMDRSVATYPDST.ADDRDST.PORT110x001Variable2这个请求格式与Socks5代理TCP时相同，不过这里的CMD值是0x03，也就是UDP类型，说明本次请求是UDP请求，且目标地址和端口可以全0表示“尚不确定”。Socks5代理收到客户端发来的请求后，创建一个UDP监听端口，并将这个监听信息发回客户端，格式如下：VERREPRSVATYPBND.ADDRBND.PORT110x001Variable2这个回复格式依旧与S…

NAT穿透
type: item
date: 2025-11-01T08:50:00+00:00
description: NAT穿透也常被称为UDP打洞，用于让处在不同内网、被NAT隔离的设备能直接建立点对点（Peer to Peer，P2P）连接。1、NAT介绍NAT全称Network Address Translation，即网络地址转换协议。起初网络设计者并没有想到互联网能够如此发达，以至于2^32个IPv4地址不够用。针对该问题，设计者给出了两种解决方案，一种就是推行IPv6，但这需要相应的硬件设备支持，且需要一定过渡时间；另一种方案便是使用NAT协议，将IP分为公网IP和内网IP。根据RFC 1918规定，将内网IP划分为3类。类别CIDR地址范围数量A10.0.0.0/810.0.0.0 – 10.255.255.25516,777,216B172.16.0.0/12172.16.0.0 – 172.31.255.2551,048,576C192.168.0.0/16192.168.0.0 – 192.168.255.25565,536在局域网中，设备分配内网IP，与外界进行通信时，ISP具有公网IP的路由器会维护一张NAT映射表，最初的NAT只改变IP，而Port不变，但随着发展，出现了PAT（Port Address Translation），即在NAT的基础上添加了端口映射，最终将内网IP:Port映射到路由器公网IP上的某个端口，然后通过这个公网端口与外界进行通信。PAT被视为NAT Overload，通常情况下不会刻意区分这两者，统一称为NAT，具体通信流程如下[1]。2、NAT穿透与STUNRFC 3489将NAT分为4类[2]，并定义了早期STUN（Simple Traversal of UDP Through NATs）协议，旨在帮助UDP应用穿越NAT，但后续由于实际网络中NAT行为复杂多变、检测结果极易错误。于是便推出了RFC 5389。NAT类型特点Full Cone内网IP:Port映射到固定公网IP:Port，任何外部IP都可回包Restricted Cone内网IP:Port映射固定，只有之前通信过的外部IP可以回包Port Restricted Cone内网IP:Port映射固定，只有之前通信过的外部IP:Port可以回包Symmetric内网IP:Port对每个目标IP:Port映射不同公网端口端口限制型与对称型的区别在于，端口限制锥型NAT是所有请求映射到相同的公网IP地址和端口，即改变目的IP:Port时，映射不变，而对称NAT是不同的请求有不同的映射，主要区别是内网源端口映射到公网端口是否随目标地址变化。判断NAT类型，只需要判断最外层NAT就行。且由于UDP无状态，没有繁杂的ESTABLISHED、LISTENING等状态，UDP端口只要映射出去就能用。RFC 5389彻底重定义并取代RFC 3489，将STUN定义为Session Traversal Utilities for NAT。它移除所有NAT类型分类逻辑，明确建议应用程序放弃诊断NAT，转而使用ICE协议框架通过实际连通性检查建立连接，从而简化协议、提升可靠性，成为现代WebRTC等P2P系统的基石。但如今涉及到NAT穿透，NAT的四种类型还是会有理论指导作用。ICE（Interactive Connectivity Establishment，交互式连接建立）是一套“自动试错找路”的协议框架，依赖STUN/TURN，最终让NAT后的两台设备像没NAT一样直连。RFC 3489的STUN“想太多”，试图诊断NAT类型，结果复杂、错误。RFC 5389的STUN“回归初心”，只干一件事，安全地告诉你公网映射，然后让ICE去试错选路。后来，又推出了RFC 5780重新定义了NAT类型，这次更加详细彻底，如下图所示，绿色为 Mapping Behavior，蓝色为 Filtering Behavior。3、NAT穿透与FRP这里需要区别FRP和NAT打洞的区别，FRP内网穿透是将内网服务映射到公网，而打洞仅仅是想建立两台设备的连接，两种均适用于TCP和UDP，但前者以TCP为主，后者以UDP为主[3]。因为后者即使在Full Cone的情况下，正常情况下，将内网TCP端口映射到公网，其TCP连接状态是ESTABLISHED，也就是以连接发送者的身份出现的，与用FRP穿透出去的LISTENING端口不同，其是以服务提供者的身份出现的。因为TCP连接是有状态的，所以若想将已经ESTABLISHED的端口转为LISTENING状态，则要显示的开启端口复用，否则则会报错端口占用。针对TCP打洞，有两种方案，第一，协调双方同时交叉发送SYN包，如下图，像极了TCP的彩蛋，理论成立，但现实很难做到。第二种就是通过SO_REUSEADDR实现TCP端口…

HTTP协议发展史
type: item
date: 2025-10-28T12:15:00+00:00
description: 最近在搞WebSocket、QUIC等协议，发现都和HTTP协议有关，遂整理一篇。1、概述HTTP至今已经5个版本了，详细信息如下表所示。版本正式发布年份最新RFC文档HTTP 0.91991无正式文档HTTP 1.01996RFC 1945HTTP 1.11997RFC 7230–7235HTTP 2.02015RFC 9113HTTP 3.02022RFC 9114不同HTTP版本占比可见Cloudflare调研文档。2、HTTP 0.9这是最早的HTTP版本，只提供了简单的GET方法，请求纯文本，且只有请求行（Request-Line），无请求头（Header），请求体（Message-Body）。如GET /index.html响应也没有状态行（Status-Line），响应头（Header），只有响应体（Message-Body），如 Hello World 3、HTTP 1.0相较于HTTP 0.9，该版本的HTTP添加了请求头，请求体，状态行，响应头。且添加了POST、HEAD等方法。请求示例如下：#请求行 GET /index.html HTTP/1.0 #请求头 Host: www.example.com User-Agent: MyBrowser/1.0 Accept: text/html #请求体 #一般在POST方法中存在。响应示例如下：#状态行 HTTP/1.0 200 OK #响应头 Date: Tue, 28 Oct 2025 10:00:00 GMT Server: ExampleServer/1.0 Content-Type: text/html Content-Length: 44 Connection: close #响应体 Hello World 4、HTTP 1.1HTTP 1.1 是在1.0的基础上增加了连接复用、请求方法、缓存控制等内容。在1.0版本时，客户端请求并受到服务端响应后，会断开TCP连接。而1.1版本添加了Connection请求头，默认为keep-alive，即会复用TCP连接，直到超时或关闭连接。一个网站最多会同时建立6个TCP连接，当然这是浏览器决定的。这也是常说的短连接（Short-lived Connection）和长连接（Persistent Connection）[1]。具体差异如下图所示。这里的流水线（Pipelining）传输方式最终并没有流行开来，而是被HTTP 2.0中的多路复用（Multiplexing）所替代，因为Pipelining还是串行传输，前者会影响后者，而多路复用则是任意顺序传输，且前者的使用受多种因素影响，如请求方法等。这里能明显看到，HTTP 1.1存在应用层的队头阻塞（Head-of-line blocking），即前一个HTTP Response没到达，客户端是不会发送下一个Request的。此外，知名的WebSocket协议就是在HTTP 1.1的基础上扩展而来的。5、HTTP 2.0HTTP 2.0在1.1的基础上，在保持HTTP语义不变的前提下，通过二进制分帧、多路复用、头部压缩和服务器推送，大幅提升性能和传输效率，直观展示请看[2]。Burpsuite也支持抓HTTP 2.0的包，现浏览器也大多支持了HTTP 2.0，具体也体现在JA4指纹中。HTTP 2.0与1.1的流水线传输方式类似，只不过其是乱序传输的，例如在HTTP 1.1中，客户端发送1.png, 2.png, 3.png三个图片的Request，其是顺序进行的，当然，浏览器可以开3个TCP连接，分别请求这3个图片，从应用层整体来看，也勉强能称得上乱序，但在单个TCP连接中，依旧是顺序的，且这个并行乱序是浏览器带来的，不是协议本身的特性。而HTTP 2.0中，其只建立1个TCP连接，3个图片的request被封装成多个Frame，每个Frame平等，随意混合，不存在文件先后顺序。每个文件都看作一个Stream，Stream由多个Frame组成，最后服务端通过Frame上带的Stream ID重组收到的Frame，最终获取完整Request，随后返回Response过程同理。具体关系如下图：由此，能看到HTTP 2.0解决了应用层上的队头堵塞，但传输层，也就是TCP层仍然存在队头阻塞，即TCP连接中，前一个字节没到达，就会阻塞后一个字节的传输。虽然在HTTP 2.0中，支持了服务器推送（Server Push），但其本质语义上还是request-response的形式，只不过当客户端请求index.html时，服务端能够顺带将相关的css和js文件一同推送给客户端，但由于一些兼容性问题，主流浏览器已经不再支持，基本处于弃用状态。而HTTP 1.1中的WebSo…

Go中的结构体与接口
type: item
date: 2025-10-11T03:47:00+00:00
description: 最近在看一些协议的源码，其大都用Go语言实现，遂了解一下Go语言的基本数据结构。编程思想有很多，例如面向过程的C、面向对象的C++和JAVA等，而Go相较于C多了接口、相较于JAVA少了类，好像一种半面向对象的编程方法。1、结构体Go语言中数组可以存储同一类型的数据，但在结构体中可以为不同项定义不同的数据类型。结构体（struct）是由一系列具有相同类型或不同类型的数据构成的数据集合。下面是一个具体示例：package main import ( "fmt" ) //定义一个结构体 type Student struct { //定义结构体的属性 Name string Age int } func main() { //实例化一个结构体 var a Student //为实例a的属性赋值 a.Name="Alice" a.Age=22 fmt.Println(a) //实例化b并赋值 b:=Student{"Bob",24} fmt.Println(b) }结构体是由变量组成的，其中并不包含方法，即纯粹的数据属性。2、接口Go语言中的接口（interface）用于定义方法的集合，它通过描述类型（type，大多是struct，也可是其他，这里只讨论struct）必须实现的方法，规定了类型的行为契约。Go的接口与JAVA最大的区别在于，Go是由结构体隐式的实现某个接口，如果一个结构体满足了某个接口定义的所有方法，那么这个结构体就实现了该接口。而JAVA中则是由类显式的实现某个接口，并实现该接口的所有功能。下面是一个示例： package main import ( "fmt" ) //定义一个结构体 type Student struct { //定义结构体的属性 Name string Age int } //声明一个接口 type Action interface { //声明接口的方法 Introduction() string ChangeInfo(name string,age int) } //定义结构体的方法，以实现接口 //func (方法接收者) 方法名(参数) 返回值 func (s Student) Introduction() string { mystr:=fmt.Sprintf("My name is %s, my age is %d",s.Name,s.Age) return mystr } //这里传递指针，否则不会改变值 func (s *Student) ChangeInfo(name string,age int){ s.Name=name s.Age=age } //定义另一个结构体 type Teacher struct { Name string Age int Object string } //定义Teacher结构体的方法，以满足接口 func (t Teacher) Introduction() string { mystr:=fmt.Sprintf("My name is %s, my age is %d, my object is %s",t.Name,t.Age,t.Object) return mystr } func (t Teacher) ChangeInfo(name string,age int) { //不用该函数，但必须定义，否则无法满足接口需求 } func main() { //实例化一个结构体 var a Student //为实例a的属性赋值 a.Name="Alice" a.Age=22 fmt.Println(a) //实例化b并赋值 b:=Student{"Bob",24} fmt.Println(b) //声明接口 var i Action //由于定义了指针类型的方法，所以接口赋值时要用指针 i=&a fmt.Println(i.Introduction()) //调用方法修改结构体属性值 i.ChangeInfo("New Bob",22) //实例化一个教师结构体 c:=Teacher{"Mike",50,"English"} //赋值给接口 i=c fmt.Println(i.Introduction()) } 也就是说，接口可以对应拥有不同属性，相同方法的结构体。