分享一个Java实现的开源代理工具,支持拦截HTTPS请求

tool   jproxy   project  
发布于 Aug 14, 2024

今天给大家分享一个 Java 实现的代理抓包工具,亲测好用,我个人更是曾经用这个项目赚到了一点小钱,最近刚好也有一个项目可能会用到这个技术,就把它单独拎出来跟大家分享一下。

想必大家都知道 fiddler 吧?就是一款流行的抓包工具(如果不知道的话,可以留言求分享,后续也可以简单介绍一下)。

直接上项目链接:

https://github.com/bytesops/jProxy

项目介绍

jProxy 是一个 JAVA 编写的 HTTP 代理服务器类库,支持 HTTP、HTTPS、Websocket 协议,并且支持 MITM (中间人攻击),可以对 HTTP、HTTPS 协议的报文进行捕获和篡改。

关于中间人攻击,大家可以自己搜一下,HTTPS 劫持算其中的一个具体实现,直白点理解就是:你原本想和小花说“我爱你”,但是一天,小强带着假发来到你身边,冒充小花,你就把这句话告诉了小强,然后小强又带上面具冒充你,跟小花说了一句“我讨厌你”,然后,就没有然后了。

熟悉 fiddler 的都知道 fiddler 可以截获 https 的内容,本项目的价值区别于 fiddler 的点就是可以实现程序代码介入 http 请求的全过程,至于能做什么,大家可以自由想象,欢迎留言区互动。

为了把技术讲的清楚一点,还是从网上抄点干货吧(如有错误,大家赶紧指正)。

HTTPS协议

首先来回顾下 https 协议的 ssl 握手(看不懂的也可以跳过,不过建议好好看一下,面试很有用)

简单叙述下 ssl 握手,上面只是单向验证过程(大多我们访问网站都是单向的,除非一些银行网站或服务器之间会用到双向验证)

  1. 客户端(左侧)向服务器发出 ssl 握手,发送 client-random 随机数
  2. 服务器返回 ssl 证书和 server-randon 随机数
  3. 客户端校验 ssl 证书,校验通过,再生成一个 premaster-secret 随机数用服务器证书里的公钥加密发送,这个时候客户端已经可以通过三个随机数算出对称加密的密钥了
  4. 服务器用私钥解密 premaster-secret,也拿到了三个随机数算出对称加密的密钥
  5. 两边都用算出来的对称密钥进行报文加密和解密,传输中的信息都是密文

注意第 3 步非常关键,ssl 证书是采用信任链的方式来验证 ssl 证书是否有效,在计算机中都会内置好许多受信任的 CA 证书(见下图 电脑查找设置- 管理计算机证书 ),而只有受信任的 CA 证书签发的 ssl 证书来访问浏览器才会验证通过,不然就会提示证书不安全。

计算机内置的,我们大体上认为是权威的(不过也发生过 权威机构 证书泄露的事件),那么我们是不是可以伪造一个呢?现在有请小强。

自定义根证书

我们可以借助 openssl 自签一个 ca 证书,并安装到自己的电脑里,并且选择信任它(相信小强是小花)。

注:如果为了快速验证上面的小项目,不必自己生成,可以直接使用项目里的 ca.crt 安装,下面了解即可,如遇到项目里的 ca 证书过期了,可以尝试自行生成,也可以留言,我来更新。

# key 的生成,生成 RSA 密钥,openssl 格式,2048位强度,ca.key 是密钥文件名
openssl genrsa -out ca.key 2048

# crt 的生成,通过-subj 选项可以自定义证书的相关信息
openssl req -sha256 -new -x509 -days 365 -key ca.key -out ca.crt \
    -subj "/C=CN/ST=GD/L=SZ/O=byteops/OU=study/CN=jProxyRoot"

生成了 ca.crt 即 ca 证书,双击可以安装(优先安装项目里的,方便演示

HTTPS拦截

安装证书后,需要重启一下计算机,不然证书可能有缓存,浏览器不识别。 重启后,执行项目里的

HttpProxyServerApp.java

main函数,启动项目

/**
 * 测试 main
 */
public class HttpProxyServerApp {

    public static void main(String[] args) {

        System.out.println("start proxy server");

        HttpProxyServerConfig config = new HttpProxyServerConfig();
        config.setHandleSsl(true);
        new HttpProxyServer()
                .serverConfig(config)
                .proxyInterceptInitializer(new HttpProxyInterceptInitializer() {
                    @Override
                    public void init(HttpProxyInterceptPipeline pipeline) {
                        pipeline.addLast(new FullResponseIntercept() {

                            @Override
                            public boolean match(HttpRequest httpRequest, HttpResponse httpResponse, HttpProxyInterceptPipeline pipeline) {
                                // 在匹配到百度首页时插入js
                                return HttpUtil.checkUrl(pipeline.getHttpRequest(), "^www.baidu.com$")
                                        && HttpUtil.isHtml(httpRequest, httpResponse);
                            }

                            @Override
                            public void handleResponse(HttpRequest httpRequest, FullHttpResponse httpResponse, HttpProxyInterceptPipeline pipeline) {
                                // 打印匹配到的 host
                                String host = httpRequest.headers().get(HttpHeaderNames.HOST);
                                System.out.println(host);
                                // 修改响应头和响应体
                                httpResponse.headers().set("handle", "edit head");
                                httpResponse.content().writeBytes("<script>alert('hello jproxy')</script>".getBytes());
                            }
                        });
                    }
                })
                .start(9999);
    }
}

开启代理

启动项目后,需要开启代理才能拦截到请求(目前还做不到 fiddler 的自动,也欢迎大家提 PR 一起改进),代理开启也很简单,电脑网络配置,代理,手动配置代理(记得点保存哟):

访问网站

打开浏览器,访问百度首页:

https://www.baidu.com

即会弹出:

F12 打开控制台,也可以刷新页面,可以看到 响应头 中多了一条记录:

本次的分享到此结束,希望对你有所帮助。

如果你对我分享的内容感兴趣,欢迎扫码关注公众号:新质程序猿,并设置星标,推送更实时哟!

本文由 黄彦祥 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。
可自由转载、引用,但需署名作者且注明文章出处。