mip-sandbox
mip-sandbox 是一系列跟 MIP 沙盒相关的工具,包括沙盒对象定义、代码沙盒检测工具、代码沙盒替换工具等等。
安装
通过 npm
进行安装:
npm install --save-dev mip-sandbox
使用
沙盒对象
var sandbox = // setTimeoutsandbox // documentsandboxdocumentcookie // ...
不安全全局变量检测
使用 mip-sandbox/lib/unsafe-detect
方法进行不安全全局变量检测,该函数的定义如下
/** * 不安全全局变量检测 * * @params * @params * 在默认情况下,所有全局变量包括 window document 等均认为不安全, * 需要传入该参数进行条件过滤 * @return */
使用例子如下:
var detect = var keywords = var code = `var a = 1console.log(b)` // 严格模式 请使用 keywords.WHITELIST_STRICT// 在前端使用时,可通过 MIP.sandbox.WHITELIST 去拿该列表var results = console // [// {// type: 'Identifier',// name: 'b',// range: [...]// loc: [...]// }// ]
不安全全局变量替换
使用 mip-sandbox/lib/generate
方法进行不安全全局变量替换,该函数的定义如下
/** * 不安全全局变量替换 * * @param * @param * 在默认情况下,所有全局变量包括 window document 等均认为不安全, * 需要传入该参数进行条件过滤 * @param * @param * @param * @return */
使用例子如下:
var generate = var keywords = var code = `var a = 1console.log(b)window.console.log(a)` var result = console // var a = 1// console.log(MIP.sandbox.b)// MIP.sandbox.window.console.log(a)
对于严格模式下 window 需要替换成 MIP.sandbox.strict.window
在这种情况下,需要传入第三个参数:
options.prefix
默认的 options.prefix === 'MIP.sandbox',在严格下可以传入 MIP.sandbox.strict,得到的结果将如下所示:
var result = // var a = 1// console.log(MIP.sandbox.b)// MIP.sandbox.strict.window.console.log(a)
该方法使用 escodegen 实现的 ast to string
该方法的第三个参数 options.escodegen 将会透传给 escodegen 因此比如需要返回 sourcemap 的话,请于第二个参数传入 sourcemap 相关参数
如:
var output = // output.code// output.map
对于不需要生成 sourceMap 的情况,可以使用 generate-lite 来去掉 source-map 相关代码以减小打包体积。
该方法的定义如下:
/** * 不安全全局变量替换 * * @param * @param * 在默认情况下,所有全局变量包括 window document 等均认为不安全, * 需要传入该参数进行条件过滤 * @return */
var generate = var keywords = var code =
沙盒检测替换优化
在某些场景下需要同时使用 detect 和 generate 去实现功能,这时,如果对这两个方法传入的 code 都是字符串的话,就需要对字符串做两次 ast 解析和标记,为了解决这个问题,可以调用 global-mark 生成解析标记好的 ast,再将 ast 传入 detect 和 generate 中,从而提高效率:
var mark = var detect = var generate = var keywords = var ast = var unsafeList = var generated =
沙盒替换规则
沙盒替换采用白名单机制,即只允许开发者使用部分全局 API,不在白名单里的方法会在编译时自动将相关代码加上 MIP.sandbox
前缀,这样就会导致报错。
比如下面一段代码:
const a = consolewindowconsole
将会替换成:
const a = consoleMIPsandboxMIPsandboxwindowconsole
解释
上述代码中 require、console、eval、window、setTimeout、undefined、b 属于全局变量,其中 require、console 属于安全全局变量,所以不做任何处理;eval、window、b 属于不安全全局变量,因此会加上 MIP.sandbox
前缀,其中 MIP.sandbox.window 是有定义的,而 MIP.sandbox.eval 和 MIP.sandbox.b 没有定义,因此上述代码会报错。
this 沙盒替换
上述代码中经过对比可以看到 this 被替换成了 MIP.sandbox.this(this),这是因为在类似 function () {}.bind(undefined)
的情况下,函数内的 this 指向 window,而 诸如 document.addEventListener('scroll', function () {})
,的回调里的 this 指向 document,这些都是不安全全局变量,因此需要 MIP.sandbox.this() 方法将 window 和 document 替换掉:
MIPsandbox { return that === window ? MIPsandbox : that === document ? MIPsandboxdocument : that}
严格模式
在 mip-script 中,理论上只允许进行数据运算和发请求等等操作,不允许直接操作 DOM ,因此在 mip-script 中写的 js 将会以沙盒的严格模式进行全局变量替换,比如 window 会被替换成 MIP.sandbox.strict.window
、 this 将会替换成 MIP.sandbox.strict.this(this)
。
其中 MIP.sandbox.strict 是 MIP.sandbox 的子集。
可用全局变量
以下变量是 MIP sandbox 暴露给用户可直接使用的全局变量,后续会根据实际需要进行增加或减少。
原生安全的全局变量
以下全局变量被认为是安全的,在沙盒注入过程中不会加上任何沙盒前缀。
var ORIGINAL = 'Array' 'ArrayBuffer' 'Blob' 'Boolean' 'DOMError' 'DOMException' // https://github.com/mipengine/mip2/issues/336 'DataView' 'Date' 'Error' 'Float32Array' 'Float64Array' 'FormData' 'Headers' 'Infinity' 'Int16Array' 'Int32Array' 'Int8Array' 'JSON' 'Map' 'Math' 'NaN' 'Number' 'Object' 'Promise' 'Proxy' 'ReadableStream' 'ReferenceError' 'Reflect' 'RegExp' 'Request' 'Response' 'Set' 'String' 'Symbol' 'SyntaxError' // https://github.com/mipengine/mip2/issues/347 'TextDecoder' 'TextEncoder' 'TypeError' 'URIError' 'URL' 'URLSearchParams' 'Uint16Array' 'Uint32Array' 'Uint8Array' 'Uint8ClampedArray' // 1.0.17 新增 WebSocket 'WebSocket' 'WritableStream' // issue https://github.com/mipengine/mip2/issues/62 'crypto' 'console' 'decodeURI' 'decodeURIComponent' 'localStorage' 'navigator' 'sessionStorage' 'screen' 'undefined' 'devicePixelRatio' 'innerHeight' 'innerWidth' 'isSecureContext' 'length' 'outerHeight' 'outerWidth' 'screenLeft' 'screenTop' 'screenX' 'screenY' 'scrollX' 'scrollY' // mip-data ready status 'mipDataPromises' // https://github.com/mipengine/mip2/issues/347 'atob' 'clearInterval' 'clearTimeout' 'encodeURI' 'encodeURIComponent' 'escape' 'fetch' 'getComputedStyle' 'isFinite' 'isNaN' 'matchMedia' 'parseFloat' 'parseInt' 'setInterval' 'setTimeout' 'unescape' // mip1 polyfill 'fetchJsonp' var RESERVED = 'arguments' 'require' 'module' 'exports' 'define' 'import' // process.env.NODE_ENV 'process'
普通模式的沙盒安全变量
普通模式下的沙盒安全变量包含原生安全变量的全部,并且添加了以下变量:
// 全局安全变量var WHITELIST_ORIGINAL = ...ORIGINAL ...RESERVED // https://github.com/mipengine/mip2/issues/143 'CustomEvent' 'File' 'FileList' 'FileReader' 'Image' 'ImageBitmap' 'MutationObserver' 'Notification' // 待定 'history' // 待定 'location' 'scrollbars' 'addEventListener' 'cancelAnimationFrame' 'createImageBitmap' 'removeEventListener' 'requestAnimationFrame' 'scrollBy' 'scrollTo' 'scroll' 'webkitCancelAnimationFrame' 'webkitRequestAnimationFrame' // 自定义安全变量var WHITELIST_CUSTOM = name: 'document' // document 允许使用以下属性或方法 properties: // https://github.com/mipengine/mip2/issues/95 'domain' 'head' 'body' 'title' 'cookie' 'referrer' 'readyState' 'documentElement' 'createDocumentFragment' // https://github.com/mipengine/mip2/issues/470 'execCommand' 'getElementById' 'getElementsByClassName' 'getElementsByTagName' 'querySelector' 'querySelectorAll' // createElement 禁止创建 SCRIPT 标签 name: 'createElement' { return { if typeof nodename === 'string' && nodename console return document } } name: 'window' // window 指向 window.MIP.sandbox getter: 'MIP.sandbox' name: 'MIP' // MIP 指向 window.MIP getter: 'MIP'
因此,白名单的定义为,普通模式下的全局安全变量与自定义安全变量的集合:
var WHITELIST = WHITELIST_ORIGINALvar WHITELIST_RESERVED = WHITELIST_ORIGINAL
严格模式下的沙盒安全变量
严格模式下的沙盒安全变量包含原生安全变量的全部,并且添加了以下变量:
// 安全原生全局变量var WHITELIST_STRICT_ORIGINAL = ...ORIGINAL ...RESERVED // 安全自定义全局变量var WHITELIST_STRICT_CUSTOM = // 只允许访问 document.cookie name: 'document' properties: 'cookie' // https://github.com/mipengine/mip2/issues/95 'domain' // location 只放开读权限 name: 'location' access: 'readonly' properties: 'href' 'protocol' 'host' 'hostname' 'port' 'pathname' 'search' 'hash' 'origin' // MIP 对象只开放部分工具函数 name: 'MIP' access: 'readonly' properties: 'watch' 'setData' 'viewPort' 'util' 'sandbox' name: 'viewer' access: 'readonly' properties: 'isIframed' 'sendMessage' 'open' // 'viewer', 'MIP_ROOT_PAGE' // 严格模式下的 window 对象指向 MIP.sandbox.strict name: 'window' getter: 'MIP.sandbox.strict'
因此,严格模式下白名单的定义为,普通模式下的全局安全变量与自定义安全变量的集合:
var WHITELIST_STRICT = WHITELIST_STRICT_ORIGINALvar WHITELIST_STRICT_RESERVED = WHITELIST_STRICT_ORIGINAL