三滑稽甲苯 发表于 2024-10-3 22:09

[JavaScript] GitHub 去跟踪原理

本帖最后由 三滑稽甲苯 于 2024-10-3 22:22 编辑

## 前言

众所周知,GitHub 会对用户操作信息进行收集,装上 uBlock Origin 并且订阅了 EasyPrivacy 规则后即可在控制台中看到被屏蔽的请求:



那么我们的目标就是阻止对这两个网站的请求的发送。注意,我们想要的是将其扼杀在摇篮里,而不是等它将要发送的时候再阻止。

## Part 1

既然对 `https://collector.github.com/github/collect` 的请求较多,那就先分析这个网址。从网络面板中找到请求,然后检视它的调用堆栈:



进入最顶部的调用者,可以看到是这一行代码发出的请求:



要是我们可以阻止 `hydroAnalyticsClient` 初始化就好了。搜索此变量,可以发现其初始化的位置:



主要考虑怎么在 `getOptionsFromMeta` 或 `new AnalyticsClient` 时出错,那么根据代码和注释 (`This most likely means analytics are disabled.`),应该是不会进行信息收集了。往上翻代码,可以看到这俩玩意都是从 `@github/hydro-analytics-client` 导入的:

```typescript
import {AnalyticsClient, getOptionsFromMeta} from '@github/hydro-analytics-client'
import type {Context} from '@github/hydro-analytics-client'
// ...
```

那么我们随便下个断点,刷新页面来寻找这俩玩意代码的位置。首先看 `AnalyticsClient`:



这里点进去,可以看到它的定义:



经检查,没有可以利用的地方,那么我们继续检查 `getOptionsFromMeta`:



```typescript
// 源代码映射: https://github.githubassets.com/assets/node_modules/@github/hydro-analytics-client/dist/meta-helpers.js
export function getOptionsFromMeta(prefix = 'ha') {
    let collectorUrl;
    const baseContext = {};
    const metaTags = document.head.querySelectorAll(`meta`);
    for (const meta of Array.from(metaTags)) {
      const { name: originalName, content } = meta;
      const name = originalName.replace(`${prefix}-`, '').replace(/-/g, '_');
      if (name === 'url') {
            collectorUrl = content;
      }
      else {
            baseContext = content;
      }
    }
    if (!collectorUrl) {
      throw new Error(`AnalyticsClient ${prefix}-url meta tag not found`);
    }
    return {
      collectorUrl,
      ...(Object.keys(baseContext).length > 0 ? { baseContext } : {})
    };
}
```

可以看到,它是从文档头部所有 `name` 属性以 `${prefix}-` 开头的 `meta` 元素中获取配置信息。根据前面 `hydro-analytics.ts` 中的代码 `const options = getOptionsFromMeta('octolytics')`,或者自行下断点,我们都可以知道这里 `prefix === "octolytics"`。继续分析代码,可以发现如果不存在 `name` 属性 为 `${prefix}-url` 的元素时,`collectorUrl` 就会为 `undefined`,那么后续代码就会报错,从而达成我们的目的。既然如此,阻止这块的信息收集就轻而易举了:

```javascript
$$("meta").forEach(el => el.remove());
```

需要注意的是,这一段代码必须在 `getOptionsFromMeta` 之前被调用,否则就太晚了。若使用篡改猴的话,需要设置 `@run-at document-start`。

## Part 1 - 验证

```javascript
// ==UserScript==
// @name         GitHub Tracking Prevention Test
// @namespace    http://tampermonkey.net/
// @version      0.1.0
// @descriptiontry to take over the world!
// @author       PRO-2684
// @match      https://github.com/*
// @icon         https://github.com/favicon.ico
// @run-at       document-start
// @license      gpl-3.0
// @grant      none
// ==/UserScript==

(function() {
    'use strict';
    const $$ = document.querySelectorAll.bind(document);
    $$("meta").forEach(el => el.remove());
})();
```

安装上述脚本,随后清空网络日志并刷新页面。在网络日志中搜索关键词 `collect`,并未找到任何结果,可知我们已经成功阻止了向这个网址提交的跟踪信息。



## Part 2

接下来,我们分析 `https://api.github.com/_private/browser/stats`。同 Part 1,我们从网络面板中找到请求,然后检视它的调用堆栈:



进入最顶部的调用者,可以看到是这一行代码发出的请求:



心急的朋友肯定有这个想法:把 [`navigator.sendBeacon`](https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/sendBeacon) 覆盖掉不就好了?我知道你很急,但是你先别急。可以看到这是在函数 `safeSend` 内被调用的,那么我们就先看看到底是谁调用了这个函数。在文件内搜索关键词 `safeSend`,只找到了另外一处匹配:



查看附近代码,发现可以利用的点:

```typescript
const url = ssrSafeDocument?.head?.querySelector<HTMLMetaElement>('meta')?.content
if (!url) {
return
}
```

那么我们只需要移除文档头中 `name` 属性为 `browser-stats-url` 的 `meta` 元素即可。需要注意的是,这一段代码必须在 `flushStats` 之前被调用,否则就太晚了。若使用篡改猴的话,需要设置 `@run-at document-start`。是不是和 Part 1 十分相似呢?

```javascript
$("meta")?.remove();
```

## Part 2 - 验证

```javascript
// ==UserScript==
// @name         GitHub Tracking Prevention Test
// @namespace    http://tampermonkey.net/
// @version      0.1.1
// @descriptiontry to take over the world!
// @author       PRO-2684
// @match      https://github.com/*
// @icon         https://github.com/favicon.ico
// @run-at       document-start
// @license      gpl-3.0
// @grant      none
// ==/UserScript==

(function() {
    'use strict';
    const $ = document.querySelector.bind(document);
    const $$ = document.querySelectorAll.bind(document);
    $$("meta").forEach(el => el.remove());
    $("meta")?.remove();
})();
```

更新上述脚本,随后清空网络日志并刷新页面。在网络日志中搜索关键词 `stats`,并未找到任何结果,可知我们已经成功阻止了向这个网址提交的跟踪信息。



## 后言

带有此功能的脚本已上传至 (https://greasyfork.org/scripts/510742),欢迎各位安装体验。除了阻止跟踪的功能外,它还可以显示 Release 中文件的上传者、下载次数与下载次数直方图。若有需要,我可以新开一个帖子讲解这些功能的实现方法。

![](https://raw.githubusercontent.com/PRO-2684/gadgets/refs/heads/main/github_plus/assets.jpg)

WXJYXLWMH 发表于 2024-10-3 23:11

支持原创作品 谢谢分享

xixicoco 发表于 2024-10-3 23:30

都收集了什么信息???

rimelight 发表于 2024-10-4 13:37

原来meta标签在document-start就可以获取了

wasm2023 发表于 2024-10-4 14:15

学习了,感谢分享

侃遍天下无二人 发表于 2024-10-5 17:58

你这不是欺负老实人吗.jpg

枕下的悲情 发表于 2024-10-8 10:49

感谢分享

HUIANG 发表于 2024-10-8 12:24

看不懂,但安装了,感谢。

无心凡尘 发表于 2024-10-9 23:07

本帖最后由 无心凡尘 于 2024-10-13 23:18 编辑

看懂了!感谢分享!!

狗哥哥 发表于 2024-10-12 08:22

大佬牛逼学习了
页: [1] 2
查看完整版本: [JavaScript] GitHub 去跟踪原理