非Chrome扩展页中不违反CSP模拟点击javascript:链接的方法
- 需要在第三方商业站点的普通网页(非Chrome扩展自有页面)模拟元素点击,站点CSP配置无法修改
- 目标元素为分页下一页按钮,代码如下:
<a id="pagerBottomNew_nextButton" title="下一页" class="Search_page-cut" href="javascript:__doPostBack('pagerBottomNew$nextButton','')"><i class="Common_icon Common_icon_caret_right_large"></i></a>
当前使用自定义构造鼠标事件派发的方式触发点击,实现代码如下:
let event = new MouseEvent("click", { "bubbles": true, "cancelable": true }); let ele = document.querySelector(request.args.target); if (ele != null) ele.dispatchEvent(event); sendResponse({ type: 'done' });
执行到ele.dispatchEvent(event)时触发CSP拦截,报错如下:
Refused to run the JavaScript URL because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present.
手动通过new MouseEvent构造的事件属于脚本生成的合成事件,通过dispatchEvent触发a标签的javascript:伪协议导航时,浏览器会判定该行为属于脚本主动执行内联代码,正好命中站点CSP的拦截规则:站点CSP仅允许加载自身域名下的可信脚本,未开启unsafe-inline/unsafe-hashes配置,因此该执行路径被直接阻断。
方案1:替换为元素原生click方法(优先使用)
不要自行构造事件派发,直接调用DOM元素内置的click()方法即可。该方法触发的点击行为属于浏览器原生交互级别的触发,不会触发javascript:伪协议的CSP拦截,替换后的代码如下:
let ele = document.querySelector(request.args.target); if (ele != null) ele.click(); sendResponse({ type: 'done' });
方案2:直接调用目标业务逻辑(方案1失效时兜底)
目标a标签的点击本质就是执行ASP.NET WebForm自带的分页回发逻辑,__doPostBack是站点自身合法脚本定义的全局函数,属于CSP信任的自身源脚本,直接调用完全不会触发CSP拦截,代码如下:
// 直接执行目标回发逻辑,绕开元素点击流程 __doPostBack('pagerBottomNew$nextButton',''); sendResponse({ type: 'done' });
如果需要做通用适配,不想硬编码参数,可以自动提取a标签href中的执行逻辑:
let ele = document.querySelector(request.args.target); if (ele != null && ele.href.startsWith('javascript:')) { const targetCode = ele.href.slice(11); // 若运行在扩展内容脚本的隔离世界,需将代码注入页面主世界执行 new Function(targetCode)(); } sendResponse({ type: 'done' });
内容的提问来源于stack exchange,提问作者myfeing

