JS `Proxy Server` 配置与 `Man-in-the-Middle` (MITM) 攻击

咳咳,各位观众老爷们,晚上好!今天咱们来聊聊一个既能让你抓包爽歪歪,又能让你差点进局子的技术:JS Proxy Server 配置与 Man-in-the-Middle (MITM) 攻击。

友情提示: 以下内容仅供技术学习和安全研究,请勿用于非法用途。否则,后果自负,本讲座概不负责。出了事儿别说是听我讲的!

开场白:什么是Proxy Server?

简单来说,代理服务器就像一个中间人,你和目标服务器之间所有的数据都得经过它。你可以把它想象成一个快递驿站,你的包裹(数据)先送到驿站(代理服务器),驿站再帮你转发到你家(目标服务器)。

Proxy Server的作用

  • 访问限制网站: 比如,某些网站限制了特定地区的访问,你可以通过代理服务器绕过这些限制。
  • 隐藏真实IP地址: 代理服务器可以隐藏你的真实IP地址,保护你的隐私。
  • 加速访问: 某些代理服务器可以缓存数据,加速你的访问速度。
  • 抓包分析: 这是咱们今天的主角!通过代理服务器,我们可以拦截、修改、甚至伪造HTTP/HTTPS请求和响应,从而分析网络协议、调试程序,甚至进行一些…你懂的…事情。

JS Proxy Server:为啥用JS?

传统的代理服务器(比如Nginx、Squid)通常用C/C++编写,配置复杂,不易扩展。而JS Proxy Server,尤其是基于Node.js的,具有以下优点:

  • 易于上手: JavaScript上手简单,开发效率高。
  • 高度可定制: 可以用JS编写自定义的代理逻辑,灵活处理各种场景。
  • 跨平台: Node.js可以运行在各种操作系统上。
  • 生态丰富: Node.js生态系统提供了大量的第三方库,可以方便地实现各种功能。

搭建一个简单的JS Proxy Server

咱们用Node.js和http-proxy库来搭建一个最简单的HTTP代理服务器。

  1. 安装依赖:

    npm install http-proxy
  2. 编写代码:

    const http = require('http');
    const httpProxy = require('http-proxy');
    
    // 创建代理服务器
    const proxy = httpProxy.createProxyServer({});
    
    // 监听代理服务器的错误事件
    proxy.on('error', function (err, req, res) {
      console.error(err);
      res.writeHead(500, {
        'Content-Type': 'text/plain'
      });
      res.end('Something went wrong.  And we are reporting a custom error message.');
    });
    
    // 创建HTTP服务器
    const server = http.createServer(function(req, res) {
      // 将请求代理到目标服务器
      proxy.web(req, res, {
        target: 'http://example.com' // 替换成你想要代理的目标服务器
      });
    });
    
    console.log("listening on port 3000")
    server.listen(3000);

    代码解释:

    • httphttp-proxy是Node.js内置的HTTP模块和http-proxy库。
    • httpProxy.createProxyServer({})创建了一个代理服务器实例。
    • proxy.on('error', ...) 监听代理服务器的错误事件,防止程序崩溃。
    • http.createServer(function(req, res) { ... }) 创建一个HTTP服务器,接收客户端的请求。
    • proxy.web(req, res, { target: 'http://example.com' }) 将客户端的请求代理到目标服务器http://example.com
  3. 运行代码:

    node your_proxy_file.js

    现在,你的代理服务器就在localhost:3000上运行了。

  4. 配置客户端:

    在你的浏览器或者HTTP客户端中,将代理服务器设置为localhost:3000。 例如,在Chrome浏览器中,你可以通过以下步骤设置代理:

    • 打开Chrome设置
    • 搜索“代理”
    • 打开“打开您计算机的代理设置”
    • 选择“手动设置代理”
    • 填写代理服务器地址和端口号。

    现在,当你访问任何网站时,所有的请求都会经过你的代理服务器。

进阶:拦截和修改HTTP请求

光是转发请求太没意思了,咱们来点刺激的,拦截和修改HTTP请求。

const http = require('http');
const httpProxy = require('http-proxy');

const proxy = httpProxy.createProxyServer({});

proxy.on('proxyReq', function (proxyReq, req, res, options) {
  // 修改请求头
  proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');

  // 打印请求信息
  console.log('Request URL:', req.url);
});

proxy.on('proxyRes', function (proxyRes, req, res) {
  // 修改响应头
  proxyRes.headers['X-Custom-Header'] = 'Hello from proxy!';

  // 打印响应状态码
  console.log('Response Status:', proxyRes.statusCode);
});

const server = http.createServer(function(req, res) {
  proxy.web(req, res, {
    target: 'http://example.com'
  });
});

console.log("listening on port 3000")
server.listen(3000);

代码解释:

  • proxy.on('proxyReq', ...) 监听proxyReq事件,该事件在请求发送到目标服务器之前触发。我们可以在这里修改请求头。
  • proxy.on('proxyRes', ...) 监听proxyRes事件,该事件在收到目标服务器的响应之后触发。我们可以在这里修改响应头。

更进一步:拦截和修改HTTP响应体

修改请求头和响应头还不够,咱们来修改响应体,这才是真正的黑魔法!

const http = require('http');
const httpProxy = require('http-proxy');
const zlib = require('zlib');

const proxy = httpProxy.createProxyServer({});

proxy.on('proxyRes', function (proxyRes, req, res) {
  let body = [];
  proxyRes.on('data', function (chunk) {
    body.push(chunk);
  });

  proxyRes.on('end', function () {
    body = Buffer.concat(body).toString();

    // 修改响应体
    let modifiedBody = body.replace('example', 'HACKED!');

    // 发送修改后的响应
    res.writeHead(proxyRes.statusCode, proxyRes.headers);
    res.end(modifiedBody);
  });
});

const server = http.createServer(function(req, res) {
  proxy.web(req, res, {
    target: 'http://example.com'
  });
});

console.log("listening on port 3000")
server.listen(3000);

代码解释:

  • 我们首先监听proxyRes事件。
  • proxyRes.on('data', ...)中,我们将响应体的数据块收集到body数组中。
  • proxyRes.on('end', ...)中,我们将body数组转换为字符串,并使用replace()方法修改响应体。
  • 最后,我们使用res.writeHead()res.end()方法发送修改后的响应。

重要提示: 如果响应体是经过压缩的(比如gzip),你需要先解压缩,修改后再压缩。zlib模块可以帮助你完成这个任务。

HTTPS代理:如何抓取HTTPS流量?

HTTPS使用SSL/TLS加密,保证数据传输的安全性。直接用HTTP代理无法抓取HTTPS流量,因为数据是加密的。

要抓取HTTPS流量,我们需要进行中间人攻击 (MITM)

MITM攻击原理:

  1. 客户端向代理服务器发起HTTPS请求。
  2. 代理服务器伪装成目标服务器,向客户端发送一个伪造的SSL/TLS证书。
  3. 客户端信任该证书(或者你手动信任),并与代理服务器建立HTTPS连接。
  4. 代理服务器与目标服务器建立HTTPS连接。
  5. 代理服务器解密客户端发送的数据,并加密后发送给目标服务器。
  6. 代理服务器解密目标服务器发送的数据,并加密后发送给客户端。

通过这种方式,代理服务器就可以拦截、修改和分析HTTPS流量。

实现HTTPS代理的步骤:

  1. 生成自签名证书:

    openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365

    这会生成两个文件:key.pem (私钥) 和 cert.pem (证书)。

  2. 安装mitmproxy:

    mitmproxy是一个强大的HTTPS代理工具,可以自动生成和管理证书。

    pip install mitmproxy
  3. 运行mitmproxy:

    mitmproxy

    mitmproxy会生成一个CA证书,你需要手动安装到你的操作系统和浏览器中,才能信任mitmproxy生成的证书。

  4. 配置客户端:

    将客户端的代理服务器设置为mitmproxy的地址(默认是localhost:8080)。

注意事项:

  • 安全风险: MITM攻击会降低HTTPS的安全性,因为代理服务器可以解密和修改数据。请谨慎使用。
  • 证书信任: 你需要手动信任代理服务器生成的证书,否则浏览器会提示安全警告。
  • 法律风险: 在未经授权的情况下,进行MITM攻击是违法的。

JS 实现简单的HTTPS Proxy (不推荐生产环境使用,仅供学习):

虽然 mitmproxy 很好用,但为了学习,我们可以尝试用 JS 来实现一个简单的 HTTPS Proxy。 这个例子仅仅演示原理,不具备生产环境的安全性。

const https = require('https');
const http = require('http');
const tls = require('tls');
const fs = require('fs');

// 生成的证书
const options = {
  key: fs.readFileSync('./key.pem'),
  cert: fs.readFileSync('./cert.pem'),
};

const httpsProxy = http.createServer((req, res) => {
  // 处理HTTP请求,这里省略,可以参考之前的HTTP Proxy例子
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello from HTTP proxyn');
});

httpsProxy.on('connect', (req, socket, head) => {
    // 拦截CONNECT请求,建立TLS隧道
    const url = req.url.split(':');
    const hostname = url[0];
    const port = parseInt(url[1], 10);

    // 建立与目标服务器的连接
    const tlsSocket = tls.connect(port, hostname, {}, () => {
      // 通知客户端连接已建立
      socket.write('HTTP/1.1 200 Connection Establishedrnrn');
      // 开始双向转发数据
      tlsSocket.pipe(socket);
      socket.pipe(tlsSocket);
    });

    tlsSocket.on('error', (err) => {
      console.error('TLS Socket Error:', err);
      socket.write('HTTP/1.1 502 Bad Gatewayrnrn');
      socket.destroy();
    });

    socket.on('error', (err) => {
      console.error('Socket Error:', err);
      tlsSocket.destroy();
    });
});

const secureProxy = https.createServer(options, (req, res) => {
    //处理 HTTPS  请求,这里需要自己实现完整的 MITM 逻辑,比较复杂,这里简单返回
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello from HTTPS proxy (MITM)n');
});

console.log("listening on port 3000 (HTTP) and 3001 (HTTPS)")
httpsProxy.listen(3000);
secureProxy.listen(3001);

重要说明:

  • 安全性: 这个简单的HTTPS代理存在严重的安全漏洞,比如无法验证目标服务器的证书,容易受到中间人攻击。请勿在生产环境中使用!
  • 复杂性: 完整的HTTPS代理需要处理各种复杂的TLS协议细节,包括证书验证、密钥协商、加密解密等。
  • mitmproxy的优势: mitmproxy已经为你处理了这些复杂性,并提供了强大的功能,比如流量过滤、修改、重放等。

MITM攻击的道德与法律边界

MITM攻击是一把双刃剑。它可以用于安全研究、漏洞挖掘、协议分析等,但也可以用于非法窃取用户数据、篡改信息等。

在进行MITM攻击之前,请务必明确以下几点:

  • 法律法规: 了解你所在国家或地区的法律法规,确保你的行为不违法。
  • 授权许可: 获得目标系统的授权许可,才能进行安全测试。
  • 用户隐私: 尊重用户隐私,不要窃取或泄露用户敏感信息。

总结:

技术点 描述 风险等级 适用场景
HTTP Proxy 搭建简单的HTTP代理服务器,可以转发HTTP请求,修改请求头和响应头,修改响应体。 调试HTTP接口,分析网络协议,修改网页内容(例如,去除广告)。
HTTPS Proxy (MITM) 通过中间人攻击,拦截和解密HTTPS流量,可以分析和修改HTTPS请求和响应。 安全研究,漏洞挖掘,协议分析。
mitmproxy 强大的HTTPS代理工具,可以自动生成和管理证书,提供流量过滤、修改、重放等功能。 同 HTTPS Proxy(MITM),但工具更易用,功能更强大。

最后的忠告:

技术是中立的,但使用技术的人是有立场的。请始终牢记你的责任,遵守法律法规,尊重用户隐私。

好了,今天的讲座就到这里。希望大家有所收获,也希望大家不要因此被请去喝茶。咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注