欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

HTTP被动扫描代理的那些事

来源:本站整理 作者:佚名 时间:2019-09-10 TAG: 我要投稿

HTTP 代理这个名词对于安全从业人员应该都是熟知的,我们常用的抓包工具 burp 就是通过配置 HTTP 代理来实现请求的截获修改等。然而国内对这一功能的原理类文章很少,有的甚至有错误。笔者在做 xray 被动代理时研究了一下这部分内容,并整理成了这篇文章,这篇文章我们从小白的角度粗略的聊聊 HTTP 代理到底是如何工作的,在实现被动扫描功能时有哪些细节需要注意以及如何科学的处理这些细节。
开始之前我先来一波灵魂6问,读者可以先自行思考下,这些问题将是本文的关键点,并将在文章中一一解答:
1.http_proxy 和 https_proxy 有什么区别?
2.为什么需要信任证书才能扫描 HTTPS 的站点?
3.代理 HTTPS 的站点一定需要信任证书吗?
4.代理的隧道模式下如何区分是不是 TLS 的流量?
5.代理应如何处理 Websocket 和 HTTP2 的流量?
6.是否应该复用连接以及如何复用连接?
知识储备
我们在本地做开发时,有时会需要启动一个 HTTPS 的服务,通常使用 OpenSSL 自行签发证书并在系统中信任该证书,然后就可以正常使用这个 TLS 服务了。如果没有信任,浏览器就会提示证书不信任而无法访问,简言之,我们需要手动信任自行签发的证书才可以正常访问配置了该证书的网站。那么问题来了,为什么平日访问的那些网站都不需要信任证书呢?打开 baidu.com 查看其证书发现这里其实是一个证书链:

最顶层的 Global Sign RootCA 是一个根证书,第二个是一个中间证书,最后一个才是 baidu 的颁发证书,这三种证书的效力是:
RootCA >  Intermediates CA > End-User Cert
而且只要信任了 RootCA 由 RootCA 签发的包括其下级签发的证书都会被信任。而 Global Sign RootCA等是一些默认安装在系统和浏览器中的根证书。这些证书由一些权威机构来维护,可以确保证书的安全和有效性。而内置的这些根证书就允许我们访问一些公共的网站而无需手动信任证书了。
再来说下与 HTTP 代理相关的两个环境变量: HTTP_PROXY 和 HTTPS_PROXY,有的程序使用的是小写的,比如 curl。对于这两个变量,约定俗称的规则如下:
1.如果目标是 HTTP 的,则使用 HTTP_PROXY 中的地址
2.如果目标是 HTTPS 的,则使用 HTTPS_PROXY 中的地址
3.如果对应的环境变量为空,则不使用代理
这两个环境变量的值是一个 URI,常见的有如下三种形式:
http://127.0.0.1:7777
https://127.0.0.1:7777
socks5://127.0.0.1:7777
抛开与主题无关的 socks 不管,这里又有一个 http 和 https,别晕,这里的 http 和 https 指的是代理服务器的类型,类似 http://baidu.com 和 https://baidu.com 一个是裸的 HTTP 服务,一个套了一层 TLS 而已。那么组合一下就有 4 种情况了:
1.http_proxy=http://127.0.0.1:7777
2.https_proxy=http://127.0.0.1:7777
3.http_proxy=https://127.0.0.1:7777
4.https_proxy=https://127.0.0.1:7777
这四种情况都是合法的,也是代理实现时应该考虑的。但是如上面所说,这只是约定俗称的,没有哪个 RFC 规定必须这样做,导致上面四种情况在常见的工具中被实现的五花八门,为了避免把大家绕晕,我直接说结论:很多工具对后面两种不支持,比如 wget, python requests, 也就是说 https://还是被当成了 http://,因此我们这里只讨论前两种情况的实现。
代理中的 MITM
HTTP 代理的协议基于 HTTP,因此 HTTP 代理本身就是一个 HTTP 的服务,而其工作原理本质上就是中间人(MITM) ,即读取当前客户端的 HTTP 请求,从代理发送出去并获得响应,然后将响应返回给客户端。其过程类似下面的流程:

为了更直观的感受下,可以用 nc 监听 127.0.0.1:7777 然后使用
http_proxy=http://127.0.0.1:7777 curl http://example.com
会发现 nc 的数据包为:
GET http://example.com/ HTTP/1.1
Host: example.com
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4)
Accept: text/html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
看起来和 HTTP 的请求非常像,唯一的区别就是 GET 后的是一个完整的 URI,而不是 path,这主要是方便代理得到客户端的原始请求,如果不用完整的 URI,请求的 Scheme 将无从得知,端口号有时也可能是不知道的。
在 Go 中我们可以用几行简单的代码实现这种场景下的代理。
package main
import (
   "bufio"  
    "log"  
    "net"  
    "net/http"
)
var client = http.Client{}
func main() {
   listener, err := net.Listen("tcp", "127.0.0.1:7777")
   if err != nil {
      log.Fatal(err)
   }
   for {
      conn, err := listener.Accept()
      if err != nil {
         log.Fatal(err)
      }
      go handleConn(conn)
   }
}
func handleConn(conn net.Conn) {
   // 读取代理中的请求   req, err := http.ReadRequest(bufio.NewReader(conn))
   if err != nil {
      log.Println(err)
      return   }

[1] [2] [3]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载