Skip to content

Latest commit

 

History

History
202 lines (150 loc) · 6.38 KB

Chapter1.md

File metadata and controls

202 lines (150 loc) · 6.38 KB

第一节:HTTP中间人代理实现

想实现HTTPS的中间人代理,我们先定个小目标,先实现一个HTTP的中间人代理。

HTTP中间人代理

HTTP中间人代理示意图:

由于HTTP的传输内容都是明文,想实现中间人代理就变得非常简单。

部分代码实现如下:

const http = require('http');
const url = require('url');

let httpMitmProxy = new http.Server();
// 启动端口
let port = 6789;

httpMitmProxy.listen(port, () => {
    console.log(`HTTP中间人代理启动成功,端口:${port}`);
});
// 代理接收客户端的转发请求
httpMitmProxy.on('request', (req, res) => {

    // 解析客户端请求
    var urlObject = url.parse(req.url);
    let options =  {
        protocol: 'http:',
        hostname: req.headers.host.split(':')[0],
        method: req.method,
        port: req.headers.host.split(':')[1] || 80,
        path: urlObject.path,
        headers: req.headers
    };

    console.log(`请求方式:${options.method},请求地址:${options.protocol}//${options.hostname}:${options.port}${options.path}`);

    // 根据客户端请求,向真正的目标服务器发起请求。
    let realReq = http.request(options, (realRes) => {

        // 设置客户端响应的http头部
        Object.keys(realRes.headers).forEach(function(key) {
            res.setHeader(key, realRes.headers[key]);
        });

        // 设置客户端响应状态码
        res.writeHead(realRes.statusCode);

        // 通过pipe的方式把真正的服务器响应内容转发给客户端
        realRes.pipe(res);
    });

    // 通过pipe的方式把客户端请求内容转发给目标服务器
    req.pipe(realReq);

    realReq.on('error', (e) => {
        console.error(e);
    })
})

完整源码:../code/chapter1/httpMitmProxy.js

npm script运行方式

npm run httpMitmProxy

上面的代码实现了一个最简单的http代理。

流程概括如下:

  • 1、接收客户端的转发请求。
  • 2、根据客户端请求,向真正的目标服务器发起请求。
  • 3、把客户端请求内容转发给目标服务器。
  • 4、把真正的服务器响应内容转发给客户端。

设置代理

代理服务启动好后,我们需要把本机上的http请求都通过代理做转发。

Windows 下设置代理方式

第一步

第二步

第三步

MAC 下设置代理方式

第一步

第二步

第三步

作为中间人,能做什么?

通过该代理服务我们已经成功的与通信的两端(服务器和客户端)同时建立了连接。作为"中间人",轻而易举就能篡改经过的请求和响应。

通过http代理修改html内容:

const http = require('http');
const url = require('url');
const through = require('through2');

let httpMitmProxy = new http.Server();
// 启动端口
let port = 6789;

httpMitmProxy.listen(port, () => {
    console.log(`HTTP中间人代理启动成功,端口:${port}`);
});
// 代理接收客户端的转发请求
httpMitmProxy.on('request', (req, res) => {

    // 解析客户端请求
    var urlObject = url.parse(req.url);
    let options =  {
        protocol: 'http:',
        hostname: req.headers.host.split(':')[0],
        method: req.method,
        port: req.headers.host.split(':')[1] || 80,
        path: urlObject.path,
        headers: req.headers
    };

    // 为了方便起见,直接去掉客户端请求所支持的压缩方式
    delete options.headers['accept-encoding'];

    console.log(`请求方式:${options.method},请求地址:${options.protocol}//${options.hostname}:${options.port}${options.path}`);

    // 根据客户端请求,向真正的目标服务器发起请求。
    let realReq = http.request(options, (realRes) => {

        // 设置客户端响应的http头部
        Object.keys(realRes.headers).forEach(function(key) {
            res.setHeader(key, realRes.headers[key]);
        });

        // 设置客户端响应状态码
        res.writeHead(realRes.statusCode);

        // 通过响应的http头部判断响应内容是否为html
        if (/html/i.test(realRes.headers['content-type'])) {
            realRes.pipe(through(function(chunk, enc, callback) {
                let chunkString = chunk.toString();
                // 给html注入的alert的js代码
                let script = '<script>alert("Hello https-mitm-proxy-handbook!")</script>'
                chunkString = chunkString.replace(/(<\/head>)/ig, function (match) {
                    return  script + match;
                });
                this.push(chunkString);
                callback();
            })).pipe(res);
        } else {
            realRes.pipe(res);
        }

    });

    // 通过pipe的方式把客户端请求内容转发给目标服务器
    req.pipe(realReq);

    realReq.on('error', (e) => {
        console.error(e);
    })
})

完整源码:../code/chapter1/httpMitmProxy.js

npm script运行方式

npm run httpMitmProxy

相比第一个代理只实现了单纯的转发,这一次的实现的代理对客户端请求和服务器响应都做了篡改。

1、在处理客户端请求时,直接去掉了accept-encoding的http头部。
2、修改了服务器响应内容,给所有html注入了一段alert的js代码<script>alert("Hello https-mitm-proxy-handbook!")</script>。为了方便的修改pipe流的内容,这里用到了through2

现在通过代理来访问任何http的网站都会弹出alert框

HTTP的安全问题

从上面的http代理的实现可以看到,单纯的http协议是没有任何安全保障。想想平时身边的黑Wi-Fi、黑心运营商,想窥探或篡改通过http传输的内容是多么容易的一件事。