【问题标题】:True difference between HttpRequest and XMLHttpRequestHttpRequest 和 XMLHttpRequest 之间的真正区别
【发布时间】:2019-08-09 20:41:36
【问题描述】:

阅读前注意

这不是what-are-differences-between-xmlhttprequest-and-httprequest 的副本 对于信息,我尝试了this lib,但没有成功,因为它复制了 XMLHttpRequest 的结构,但实际上并没有像它那样工作。


我想知道来自 Node 的 HttpRequest 和来自浏览器的 XMLHttpRequest 之间真正的网络区别是什么。

如果我只是在 chrome 的 devtools 中查看 XMLHttpRequest,我在请求中看不到任何 X-Requested-with 标头。

此外,CloudFlare 的 WAF 背后还有一个在线服务,带有自定义规则。如果我使用XMLHttpRequest 发出请求,它就可以正常工作,但我使用https.request 发出请求,它无法被 CF 防火墙。

我需要使用HttpRequest 来配置代理。

两者之间的网络区别是什么,如何从 HttpRequest 模拟 XMLHttpRequest ?这甚至可能吗? 我看了铬的来源here 却找不到什么有趣的东西。

也许它与 IO 层不同? TCP 握手?

需要建议。谢谢


编辑

这是 XMLHttpRequest(工作)

let req = new XMLHttpRequest();
req.open("post", "https://haapi.ankama.com/json/Ankama/v2/Api/CreateApiKey", true);
req.withCredentials = true;
req.setRequestHeader('Accept', 'application/json');
req.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
req.setRequestHeader('Accept-Encoding', 'gzip, deflate, br');
req.onload = function() {
    console.log(req.response)
};
req.send("login=smallladybug949&password=Tl9HDKWjusopMWy&long_life_token=true");

与 cURL 相同(不通过 CF 的防火墙)

curl 'URL' \
-H 'origin: null' \
-H 'accept-encoding: gzip, deflate, br' \
-H 'user-agent: Mozilla/5.0 (Linux; Android 6.0.1; Z988 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36' \
-H 'content-type: text/plain;charset=UTF-8' \
-H 'accept: application/json' \
-H 'authority: URL.com' \
--data-binary 'login=123&password=def' \
--compressed

这里是 HttpRequest(不通过 CF 的防火墙)

let opts = url.parse(URL);
opts.method = post;
opts.headers = {
    'Accept': 'application/json',
    'Content-Type': 'text/plain;charset=UTF-8',
    'Accept-Encoding': 'gzip, deflate, br',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36'
}
let req = https.request(opts, function (res) {
    res.setEncoding('utf8');
    res.body = "";
    res.on('data', (chunk) => {
      res.body += chunk;
    });
    res.on('end', (chunk) => {
      try {
        res.body = JSON.parse(res.body);
      } catch (e) {
        return reject(res.body); // error, http 403 / 1020 error from CF (custom FW rule)
      }
      console.log(res.body); // we'll not reach this
    });
});
req.on('error', e => {
  console.error('error', e);
});
req.write("login=abc&password=def");
req.end();

编辑 2

经过几次测试,curl 命令可以正常工作,XHR 也可以,但是使用 Postman 或 HttpRequest,它会失败。 这是邮递员与 curl 的视频:https://streamable.com/81s57 视频中的 curl 命令是这样的:

curl -X POST \
  https://haapi.ankama.com/json/Ankama/v2/Api/CreateApiKey \
  -H 'accept: application/json' \
  -H 'accept-encoding: gzip, deflate, br' \
  -H 'accept-language: fr' \
  -H 'authority: haapi.ankama.com' \
  -H 'content-type: text/plain;charset=UTF-8' \
  -H 'origin: null' \
  -H 'user-agent: Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36' \
  -d 'login=smallladybug949&password=Tl9HDKWjusopMWy&long_life_token=true'

(这是一个测试帐户,所以我不需要它,您可以使用它进行测试)。您可以将 --compressed 标志添加到 curl 请求以将其解压缩或通过管道将其发送到 gunzip


编辑 3(最终)

我发现这是由于滥用(对于 CF)TLS 协议造成的。通过降级使用 OpenSSL/1.1.0f 的 curl,调用就可以正常工作了。但自从 OpenSSL/1.1.0g 之后,它们就没有了。 您可以阅读有关 OpenSSL 更改日志的更多信息here

【问题讨论】:

  • 我在发布之前做过,但它没有回答真正的问题,这些是解释的话,但我需要技术细节。网络中的“获取资源”是什么意思?它仍然是套接字连接吗?当然,因为每个连接都有一个套接字,但是 使请求被 CF 的防火墙阻止的真正区别。在他们的网站上,他们谈论“如何提出请求”。也许这里面有暗示。
  • @Sw0ut 来自浏览器的 XHR 请求发送各种标头,如“User-Agent”和“Referrer”,但从 Node 服务器发出的请求并非如此,即。 Http请求。服务器上的来源实际用于检测请求来源的安全目的。
  • @AkanshGulati 是的,但我也发送了带有 HttpRequest 的 User-Agent 标头,但它仍然被阻止。我从人类的角度提出了完全相同的要求。相同的 URL,相同的标头,相同的参数。我将编辑我的问题,以添加有关我如何处理双方请求的更多详细信息。
  • @Sw0ut 如果您可以将这两个请求共享为 cUrl,那就太好了,以便可以比较标头、方法类型、正文、查询参数、协议、路径名等所有内容。另外,我希望您了解浏览器针对任何跨域请求发出的飞行前请求。

标签: javascript networking xmlhttprequest httprequest


【解决方案1】:

正如我在 cmets 中所讨论的,我可以重现:第一个“编辑”中的 XMLHttpRequest 示例有效(HTTP 状态 = 200),而它的“复制为 cURL”版本从 Cloudfare 返回 403。

--cert-status 添加到 curl 使其对我有用,因此 Cloudfare 在决定拒绝请求时似乎会分析 TLS 级别的通信。

第一次编辑中的 curl 命令与我使用“复制为 cURL”时获得的版本有一些其他差异:

  • curl 'URL' 而不是 https://haapi.ankama.com/json/Ankama/v2/Api/CreateApiKey 显然失败了,请不要让重现结果变得更加困难。
  • -H 'origin: null'-H 'Origin: https://localhost:4443' -H 'Referer: https://localhost:4443/test_http.html' - 这没有区别。
  • 我还有一些额外的标题 -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Cookie: __cfduid=dcf1b80eef19562054c9b64f79139509e1566138746' 也没有什么区别。
  • 变化 -H 'user-agent: - 也不影响 Cloudfare
  • 您有一个额外的-H 'authority: URL.com'(用占位符代替真实域),这也没有什么区别。
  • POST数据是否正确--data-binary 'login=123&password=def'只影响API结果;不影响 403。
  • 缺少 -H 'Accept-Language: 标头会导致来自 Cloudfare 的 403。

因此,您可以尝试将缺少的 Accept-Language 添加到 Node 版本中,看看是否有帮助。

我的 Node 版本没有在 TLS 客户端 Hello 中发送 Extension: status_request(这似乎是带有或不带有 --cert-status 的 curl 调用之间的区别),我不知道您将如何启用它。在这一点上,如果可能的话,我会尝试联系支持人员,或者回退到从节点调用 curl。

附:在调试它时,我尝试比较 curl 与浏览器的 Wireshark 捕获(节点不支持SSLKEYLOGFILEforcing you to jump through hoops, so I didn't even try checking how its capture looks)。有很多细微的差异,试图对 Cloudfare 使用的规则进行逆向工程将非常耗时。 --cert-status 是一个幸运的猜测。

跨 Firefox/curl/node 的 SSL Client Hello 非常不同: 火狐 卷曲 node11

【讨论】:

  • TLS 的好消息。这有点奇怪,但如果我在 curl 命令中添加--cert-status,它会失败。 (curl: (91) OCSP response verification failed) 我猜 Postman 是基于节点的,所以它也不起作用。从我在编辑 2 部分制作的视频中可以看出,Accept-Language 已包含在请求中。我也在节点版本中尝试过,但仍然得到 403。所以你的猜测是关于客户端 Hello 中的 Extension: status_request 不是由节点发送的,但是如何确定 curl 可以管理这种棘手的请求?
  • 看,主要的一点是各种客户端之间存在很多差异(请参阅我添加的屏幕截图 - 它只是一个 TLS 客户端 Hello 请求!),因此对保护规则进行逆向工程几乎是不可能的。例如,在 Firefox 中禁用 status_request(通过 security.ssl.enable_ocsp_stapling)似乎并没有破坏它。不要试图在不知道确切规则的情况下让 node 看起来像浏览器,使用适合您的客户端 - 您已经设法让 curl 工作,尽管使用的调用与适合我的调用不同。跨度>
  • 很好,它回答了问题,即使它没有解决问题。我会想办法的。谢谢。
【解决方案2】:

我认为 curl 和节点 HttpRequest 缺少有效的 origin 标头。 XMLHttpRequest 使用浏览器引擎,因此发送和验证跨域策略并指定这些标头。

这用于保护网站免于访问它们不属于的其他网站的 API 端点。又名网络管理员可以指定可以与您的 API 通信的源域。所有 http-rest-requests 浏览器实现都会发送并验证源头。

Curl 和 HttpRequest 不是浏览器/网站的技术。看看CORSSame-origin-policyorigin-header。我想这会澄清这个问题。

【讨论】:

  • 关键是通过使用cordova制作的移动应用程序访问端点。来源是null,因为端点是通过file:// URL 访问的。当您谈论 Origin 标头时,也许您是对的,但是我尝试了几个 URL(null,API 的基本 URL,file:///www/index.html)并没有成功。 // 编辑:从工作 XHR 复制的 cURL 失败,源包含在其中。
猜你喜欢
  • 1970-01-01
  • 2010-10-26
  • 2012-11-21
  • 2012-07-30
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 2018-06-10
  • 1970-01-01
相关资源
最近更新 更多