无法读取未定义的属性“disguiseToken”

Bri*_*itt 14 javascript browser

这个问题是关于属性disguiseToken本身,而不是直接关于错误。

disguiseToken没有出现在代码库中,谷歌搜索也没有出现任何东西。

  • 什么是disguiseToken
  • 它是什么属性?
  • 什么试图访问此属性?

调用时发生错误getOwnPropertyDescriptor


示例堆栈跟踪:

TypeError: Cannot read property 'disguiseToken' of undefined
at getOwnPropertyDescriptor (eval at  (:1:38695), :560:24)
at Function.Object.getOwnPropertyDescriptor (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:39700)
at a (https://www.mywebsite.com/main.25a9fda6ea42f4308b79.js:1:1075141)
at https://www.mywebsite.com/5.6c58d8732681a35a1f8b.js:1:1533
at Object.26NW (https://www.mywebsite.com/5.6c58d8732681a35a1f8b.js:1:1805)
at i (https://www.mywebsite.com/runtime.8928e149b3f1200cf1ca.js:1:507)
at Module.L6id (https://www.mywebsite.com/5.6c58d8732681a35a1f8b.js:1:62181)
at i (https://www.mywebsite.com/runtime.8928e149b3f1200cf1ca.js:1:507)
at https://www.mywebsite.com/main.25a9fda6ea42f4308b79.js:1:914848
at t.invoke (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8160)
at M (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:14076)
at M (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:13634)
at https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:14864
at t.invokeTask (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8844)
at Object.onInvokeTask (https://www.mywebsite.com/main.25a9fda6ea42f4308b79.js:1:467756)
at t.invokeTask (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8765)
at e.runTask (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:4026)
at g (https://www.mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:11111)
Run Code Online (Sandbox Code Playgroud)

此错误发生在具有不同引擎的多个浏览器中。在过去的 3 个月中(今天是 2020-06-13),我们已经在:

  • 铬合金
  • 移动浏览器
  • 铬手机
  • 火狐
  • 三星互联网

更新(1):

我完全理解错误的含义。我的问题是什么是“伪装令牌”?我从来没有听说过,这不是我的代码中的东西。我在我的整个代码库中搜索了“伪装”这个词,没有找到,所以它一定是 Angular 内部的东西。这是什么鬼?我每天收到几十次这个错误,但我不知道它是什么。我正在运行 Angular 7。

查看angular 和 angular.js的源代码并没有给出任何结果disguiseToken


更新(2):

此代码会产生与错误报告中看到的完全相同的错误消息。此代码段并非取自实际源代码。它包含在此处,以便其他人可以重现该错误。

<script type="application/javascript">
    try {
        class Dummy extends HTMLElement {
            get dummyFuncA() {
                return 'dummy';
            }
        }

        var old = Object.getOwnPropertyDescriptor;
        Object.getOwnPropertyDescriptor = function(obj, property) {
            var descriptor = old.call(this, obj, property);

            var _disguiseToken = descriptor.disguiseToken;

            return descriptor;
        };

        var descriptorA = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncA');
        var descriptorB = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncB');
    } catch (err) {
        console.warn(err);
    }
</script>
Run Code Online (Sandbox Code Playgroud)

此代码段本身可用于测试是否Object.getOwnPropertyDescriptor已被覆盖:

<script type="application/javascript">
    try {
        class Dummy extends HTMLElement {
            get dummyFuncA() {
                return 'dummy';
            }
        }

        var descriptorA = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncA');
        var descriptorB = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummyFuncB');
    } catch (err) {
        if (
            err &&
            err.toString &&
            err.toString().indexOf('Cannot read property \'disguiseToken\' of undefined') > -1
        ) {
            console.log('Something did override \'Object.getOwnPropertyDescriptor\'');
            console.warn(err);
            console.log(Object.getOwnPropertyDescriptor);
        }
    }
</script>
Run Code Online (Sandbox Code Playgroud)

更新(3):

我们添加了上述内容,但没有学到太多:/

登录console.log(Object.getOwnPropertyDescriptor);

function getOwnPropertyDescriptor() { [native code] }
Run Code Online (Sandbox Code Playgroud)

所以这似乎没问题。

我们还添加了日志,以便script在发生此错误时将所有标记源 url 或 innerHTML 发送到我们的错误报告服务。这没有产生任何意外的结果。


更新(4):

我们添加了代码和日志来捕获Proxy. 这不是我们在代码库中使用的东西,理论上这也可能是这个问题的一部分。

几个小时后,我们遇到了一些问题,似乎确实有什么东西正在注入创建 Proxy's 的。这是在这些页面上运行的不是我们的代码的第一个直接证据。

当某些东西试图构造一个时捕获的堆栈跟踪Proxy

[redacted-url]:72:24 
[redacted-url]:356:25 ObjectWithDefaultValues
[redacted-url]:404:59 parseMetaTags
[redacted-url]:453:18 
[redacted-url]:465:3 global code
Run Code Online (Sandbox Code Playgroud)

带 line 的第一行72是我们调用的地方new Error()。行356-456不是我们的代码,而是注入的东西。

我们用来检测Proxy使用的代码:

<script type="text/javascript">
    var proxyUses = [];

    function reportProxyUse() {
        if (true) { // [redacted] check if error reporting is ready
            for (var i = 0; i < proxyUses.length; i++) {
                console.log('target', proxyUses[i].target);
                console.log('handler', proxyUses[i].handler);
                
                // [redacted]
                // error reported here
            }

            proxyUses = [];
        } else {
            setTimeout(function() {
                reportProxyUse();
            }, 2000);
        }
    }

    window.Proxy = function( target, handler ) {
        proxyUses.push({
            'target': target,
            'handler': handler,
            'error': new Error() // this captures a stack trace
        });

        reportProxyUse();
        return target;
    };
</script>
Run Code Online (Sandbox Code Playgroud)

我们将添加一个MutationObservernext 来尝试捕获所有脚本。甚至那些删除自己的人。


更新(5):

尚未完全分析这一点,但似乎 Safari 中的“添加到阅读器列表”会执行页面的初始渲染并注入包含这些Proxy内容的 Javascript 。

由于这显然只是 Safari 的使用,Proxy上面的堆栈跟踪没有解释disguiseToken

x00*_*x00 3

陪审团仍然在那里。只是总结一下当前的状态和评论:

  1. disguise+ eval+ 页面上用户没有发现任何错误 + 无法找到该字符串。所有这些都意味着恶意 npm 包的可能性。由于代码可能很混乱,这就是搜索不会产生disguiseToken任何结果的原因。跑步npm install就是你获得它所需要的一切。有时,即使是受人尊敬的包裹也会被劫持。

  2. 但还有一些其他的可能性

    1. 通过浏览器扩展注入
    2. 由用户软件注入(防火墙、防病毒软件或病毒,天知道还有什么)
    3. 由一些糟糕的 ISP 注入。即使如此https,至少在某些国家(哈萨克斯坦)仍然是可能的。或者也许是在企业网络中。
    4. 由用户手动注入
  3. 似乎已经发现问题出现在自己的代码库之外。但这从一开始就众所周知。

  4. 看起来问题是从 a 内部产生的Proxy。但这不是必要的,所以从我们现在在问题中可以看到:

    • 某些代码创建 a 的事实Proxy并不意味着从该 抛出错误Proxy
    • 因为您通过返回 来阻止创建代理target,所以您不应该看到该错误。
    • 如果你这样做了 - 那么这不是Proxy造成麻烦的原因
    • 如果您不这样做,也没有多大意义,因为该错误很少发生。
  5. 可以做什么?一些开放性问题:

    1. 如果您用于try-catch日志,它将仅捕获代码及其依赖项内部的错误(意味着恶意 npm 包)。
    2. window.onerror- 它可以捕获一些注入脚本中的错误(那些在设置处理程序后注入并抛出的错误window.onerror
    3. MutationObserver可以捕获仅在设置后注入的脚本,以及仅从页面上已存在的脚本注入的脚本。这又意味着 -在代码的依赖项内部。它不会捕获MiM脚本,因为在这种情况下,用户加载其脚本已存在的页面。所以你可能<head>也应该记录。页面加载后立即。以及关于错误。(但是脚本也可以注入到 HTML 末尾或其他地方)。
    4. 尽管页面有可能仅在浏览器加载后才被修改。延伸的话很可能是这样。使用防病毒软件的可能性较小。MiM不太可能。
    5. 获取用户的IP地址,也许你会发现一些模式。即使您不使用服务器,它看起来仍然是可能的:How to get client's IP address using JavaScript?
    6. 正如@user120242所指出的,有人遇到了同样的问题: https: //gitlab.com/minds/front/-/issues/1864 我在那里发布了一条评论寻求帮助,但尚未回复。不幸的是,回复是:

      我认为我们没有做出任何更改来解决此问题,它已关闭,因为它停止在 Sentry 中被拾取。我猜供应商更新解决了根本问题。