安全/安全的方式将插件加载到Web应用程序

时间:2018-07-15 05:44:23

标签: javascript web browser google-chrome-extension

我有一个接受JS插件的网络应用。也就是说,其他人编写的JavaScript代码表明用户希望将其加载到我的应用中。

当前,我正在使用eval()评估其JS代码进入运行时,但是我知道那并不安全。有比eval()更好的方法吗?

要清楚,用户将我指向一个干燥的文本文件(一个URL),并且文件中的JS需要以某种方式生效。

我只知道两种动态导入JS脚本的方法:

  1. 使用AJAX,获取JS代码,然后在其上运行eval()。
  2. 动态地向DOM添加<script>标签

这个问题的目的是弄清楚一个方法是否比另一个方法更安全,或者是否有比上述两个方法更好的方法。

2 个答案:

答案 0 :(得分:5)

对于Chrome扩展程序,我将专门使用sandboxingEval,它允许加载沙盒文件,这些文件可以在扩展程序托管的iframe中访问。唯一的消息传递将是通过普通的iframe消息传递。

例如,在<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.datatables.net/v/dt/jszip-2.5.0/dt-1.10.18/b-1.5.4/b-html5-1.5.4/b-print-1.5.4/fh-3.1.4/r-2.2.2/sc-1.5.0/datatables.min.js"></script> <!-- https://datatables.net/ --> <table id="myInvoicesTable" class="data-table" data-order='[[ 1, "desc" ]]'> <thead> <tr> <th class="table-th--type">Type</th> <th class="table-th--date" data-type="date" data-format="YYYY/MM/DD">Created</th> <th class="table-th--date" data-type="date" data-format="YYYY/MM/DD">Due</th> <th class="table-th--total" data-type="number">Total</th> <th class="table-th--status" data-type="number">Status</th> <th class="table-th--action">Action</th> </tr> </thead> <tbody> <!-- Invoices **************************** --> <tr class="table-tr--status status--invoice-overdue"> <td class="table-td--type"> <div> <i class="material-icons">description</i> <span>Invoice</span> <span>#00001</span> </div> </td> <td class="table-td--date">11/11/2018</td> <td class="table-td--date">11/14/2018</td> <td class="table-td--total"> <div> <span class="total-usd">$5.27</span> <span class="total-btc">0.00100000</span> </div> </td> <td class="table-td--status">Paid</td> <td class="table-td--action"> <a href="#" class="mdc-button mdc-button--unelevated button--action-viewinvoice">View</a> </td> </tr> <tr class="table-tr--status status--invoice-due"> <!-- Type --> <td class="table-td--type"> <div> <i class="material-icons">description</i> <span>Invoice</span> <span>#00001</span> </div> </td> <td class="table-td--date">11/11/2018</td> <td class="table-td--date">11/14/2018</td> <td class="table-td--total"> <div> <span class="total-usd">$5.27</span> <span class="total-btc">0.00100000</span> </div> </td> <td class="table-td--status">Paid</td> <td class="table-td--action"> <a href="#" class="mdc-button mdc-button--unelevated button--action-viewinvoice">View</a> </td> </tr> <tr class="table-tr--status status--invoice-paid"> <!-- Type --> <td class="table-td--type"> <div> <i class="material-icons">description</i> <span>Invoice</span> <span>#00001</span> </div> </td> <td class="table-td--date">11/11/2018</td> <td class="table-td--date">11/14/2018</td> <td class="table-td--total"> <div> <span class="total-usd">$5.27</span> <span class="total-btc">0.00100000</span> </div> </td> <td class="table-td--status">Paid</td> <td class="table-td--action"> <a href="#" class="mdc-button mdc-button--unelevated button--action-viewinvoice">View</a> </td> </tr> <!-- Credits **************************** --> <tr class="table-tr--status status--prepay-paid"> <!-- Type --> <td class="table-td--type"> <div> <i class="material-icons mdi mdi-coin"></i> <span>PrePay</span> <span>#00004</span> </div> </td> <td class="table-td--date">11/11/2018</td> <td class="table-td--date">11/14/2018</td> <td class="table-td--total"> <div> <span class="total-usd">$5.27</span> <span class="total-btc">0.00100000</span> </div> </td> <td class="table-td--status">Paid</td> <td class="table-td--action"> <a href="#" class="mdc-button mdc-button--unelevated button--action-viewinvoice">View</a> </td> </tr> <tr class="table-tr--status status--prepay-canceled"> <!-- Type --> <td class="table-td--type"> <div> <i class="material-icons mdi mdi-coin"></i> <span>PrePay</span> <span>#00005</span> </div> </td> <td class="table-td--date">11/11/2018</td> <td class="table-td--date">11/14/2018</td> <td class="table-td--total"> <div> <span class="total-usd">$5.27</span> <span class="total-btc">0.00100000</span> </div> </td> <td class="table-td--status">Paid</td> <td class="table-td--action"> <a href="#" class="mdc-button mdc-button--unelevated button--action-viewinvoice">View</a> </td> </tr> </tbody> </table>中声明要沙箱的页面:

manifest.json

确保将外部域列入白名单,以便可以将其嵌入。在CSP策略中,如果需要嵌入{ ... "sandbox": { "pages": [ "plugin.html" ] "content_security_policy": "sandbox allow-scripts; script-src 'self' https://plugin.com/" ], ... } ,则allow-scripts在此处。

现在在沙盒页面<scripts>中,使用外部脚本执行任何操作。在这种情况下,将下载外部插件,并将消息通过消息传递回扩展过程。

plugin.html

现在在弹出窗口中或任何位置,只需嵌入<!doctype html> <html> <head> <script src="https://plugin.com/mohamedmansour/plugin.js"></script> </head> <body> <script> // Whatever my plugin contract is, lets send something back to our extension // that the plugin initialized. Plugin.do.something.here(() => { window.postMessage({ name: 'CustomInitEvent', data: 'initializing' }, *); }); // Listen from your extension plugin.html page some events. window.addEventListener('message', (event) => { var command = event.data.command; switch(command) { case 'CustomCommandA': event.source.postMessage({ command: 'CustomCommandHello', data: 'pong command a' }, event.origin); break; } }); </script> </body> </html> 。在这种情况下,popup.html看起来像这样

plugin.html

然后您的<html> <head> <script src="plugin-manager.js"></script> </head> <body> <iframe id="theFrame" src="plugin.html"></iframe> </body> </html> 负责控制插件。

plugin-manager.js

遵循这些原则。如果需要动态插件,只需将查询参数添加到iframe。在const iframe = document.getElementById('theFrame'); window.addEventListener('message', function(event) { switch(event.name) { case 'CustomInitEvent': console.log('Plugin Initialized'); break; case 'CustomCommandHello': console.log('Hey!'); break; } }); iframe.contentWindow.postMessage({ command: 'CustomCommandA' }); iframe.contentWindow.postMessage({ command: 'CustomCommandB' }); 中,只需动态添加脚本元素,然后以这种方式调用它即可:

plugin.html

答案 1 :(得分:4)

  

这两个中的一个比另一个安全吗?

不,从安全角度来看,它们同样不好(好)。

它们的细节有所不同,因此会导致采用不同的方法来提高它们的安全性,但是最终两者都可以运行环境中不受信任的第三方编写的代码,并具有所有特权。这基本上是persisted XSS的问题。

  

有没有比以上两个选项更好的方法?

很多。这主要取决于这些插件在您的应用程序中应该做什么,谁编写它们以及谁安装(启用)它们。您既不是应用程序提供者,也不是用户想要对用户数据造成任何破坏的代码。如果插件需要访问数据,则需要采取管理措施以确保仅运行可信代码,例如插件代码审核。至少您需要通知用户,在启用插件之前,他们必须信任插件作者,这给他们带来了负担。另外,如果出现问题,您还应该确保拥有usable logs

如果您确实想运行任意的,不受信任的代码而不授予其访问用户数据的权限,则可以考虑使用沙箱。本质上,有多种方法可以在虚拟机中执行代码无法破解的执行。