You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取crypto与crypto.subtle对象属性键以校验Web Crypto API完整性?

问题

我构建了一个用于校验Web Crypto API完整性的模块,校验通过后会冻结crypto和crypto.subtle对象以确保安全。原本的校验逻辑计划:

  1. 获取crypto和crypto.subtle的属性键
  2. 检查是否包含所有预期属性、属性内容是否为未篡改的原生API
  3. 验证SubtleCrypto的所有元素都是原生函数

但实际测试中,Object.keys(crypto);和Object.keys(crypto.subtle);都返回空数组,因为这两个对象的属性都是非可枚举的;JSON.stringify()也无法获取属性,返回空JSON。

目前只能用手动创建预期键列表的次优方案,但原本依赖动态获取属性键的逻辑完全失效,导致校验失败,控制台只会输出警告而非保护启用信息。以下是原代码:

function check_crypto()
  {
var l_cryptoapi;
var l_protected = true;
var l_crypt_keys;

// If crypto isn't object Crypto or crypto.subtle isn't object SubtleCrypto,
// someone has tampered with the API!
  if(crypto.toString() != '[object Crypto]')
    l_protected = false;
  else
    if((l_cryptoapi = crypto.subtle).toString() != '[object SubtleCrypto]')
      l_protected = false;
    else
      l_crypt_keys = Object.keys(crypto.subtle);    // THIS DOES NOT WORK!!!

// l_crypt_keys is empty so the next checks inevitably fail!
  if(l_protected)
// If there aren't twelve properties in crypto.subtle, someone has tampered
// with the API!
    if(l_crypt_keys.length != 12)
      l_protected = false;
    else
// If we find an unknown key, someone has tampered with the API!
      l_protected = l_crypt_keys.every(p_value => [ 'decrypt', 'deriveBits', 'deriveKey', 'digest', 'encrypt', 'exportKey', 'generateKey', 'importKey',
                                                    'sign', 'unwrapKey', 'verify', 'wrapKey' ].includes(p_value));

// If any function isn't native code, someone has tampered with the API!
  if(l_protected)
    l_protected = l_crypt_keys.every(p_value => (typeof l_cryptoapi[p_value] == 'function') && l_cryptoapi[p_value].toString().match(/\(\)\s\{\n\s{4}\[native\ code\]\n\}$/));

  if(l_protected)
    {
    Object.freeze(l_cryptoapi);
    Object.freeze(crypto);
    console.info('protect.js: The cryptographic API is now PROTECTED against polyfill attacks!');
    }
  else
    {
    console.error('protect.js: The cryptographic API has been found to have been tampered with!');
    console.error('protect.js: Anything that is relying on this API has to be considered insecure!');
    console.error('protect.js: !!! WATCH OUT !!! SOMEONE MAY BE DOING SOMETHING REALLY NASTY !!!');
    }

  return l_protected;
  };

现在需要解决的问题:有没有办法绕过非可枚举属性的限制,让原本的动态校验逻辑生效?

解决方案

核心原因

Object.keys()只会返回对象的可枚举自有属性,而Web Crypto API的属性都是非可枚举的,所以无法通过它获取。我们需要用能获取所有自有属性(包括非可枚举)的API。

可行方案

可以使用Object.getOwnPropertyNames()或Reflect.ownKeys()来获取crypto.subtle的所有自有属性,包括非可枚举的。但需要注意:

  • 不同浏览器可能会有少量非标准的额外属性,所以校验逻辑需要调整:以预期的属性列表为基准,而非严格匹配属性数量
  • 同时可以额外检查是否存在未预期的属性,作为篡改的判断依据

修改后的代码

function check_crypto() {
  const l_cryptoapi = crypto.subtle;
  let l_protected = true;
  // 定义SubtleCrypto的标准预期属性列表
  const expectedKeys = [
    'decrypt', 'deriveBits', 'deriveKey', 'digest', 
    'encrypt', 'exportKey', 'generateKey', 'importKey',
    'sign', 'unwrapKey', 'verify', 'wrapKey'
  ];

  // 第一步:校验对象类型
  if (crypto.toString() !== '[object Crypto]' || l_cryptoapi.toString() !== '[object SubtleCrypto]') {
    l_protected = false;
  } else {
    // 获取crypto.subtle的所有自有属性(包括非可枚举)
    const allSubtleKeys = Object.getOwnPropertyNames(l_cryptoapi);

    // 第二步:检查所有预期属性是否都存在
    const allExpectedExist = expectedKeys.every(key => allSubtleKeys.includes(key));
    if (!allExpectedExist) {
      l_protected = false;
    } else {
      // 可选:检查是否存在未预期的额外属性(部分浏览器可能有合法扩展,可根据需求调整)
      const hasUnexpectedKeys = allSubtleKeys.some(key => !expectedKeys.includes(key));
      if (hasUnexpectedKeys) {
        l_protected = false;
      } else {
        // 第三步:校验所有预期属性都是原生函数
        l_protected = expectedKeys.every(key => {
          const fn = l_cryptoapi[key];
          return typeof fn === 'function' && /\(\)\s\{\n\s{4}\[native\ code\]\n\}$/.test(fn.toString());
        });
      }
    }
  }

  if (l_protected) {
    Object.freeze(l_cryptoapi);
    Object.freeze(crypto);
    console.info('protect.js: The cryptographic API is now PROTECTED against polyfill attacks!');
  } else {
    console.error('protect.js: The cryptographic API has been found to have been tampered with!');
    console.error('protect.js: Anything that is relying on this API has to be considered insecure!');
    console.error('protect.js: !!! WATCH OUT !!! SOMEONE MAY BE DOING SOMETHING REALLY NASTY !!!');
  }

  return l_protected;
};

关键调整说明

  1. 用Object.getOwnPropertyNames(l_cryptoapi)替代Object.keys(),获取所有自有属性(包括非可枚举)
  2. 校验逻辑改为以预期属性列表为核心:先确保所有预期属性都存在,再检查是否有额外属性(可选步骤,可根据浏览器兼容性调整)
  3. 直接遍历预期列表校验每个属性是否为原生函数,避免依赖动态获取的键数组
  4. 代码结构优化,提升可读性

内容的提问来源于stack exchange,提问作者Robidu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 07:48:12