我有一个接受JS插件的网络应用。也就是说,其他人编写的JavaScript代码表明用户希望将其加载到我的应用中。
当前,我正在使用eval()评估其JS代码进入运行时,但是我知道那并不安全。有比eval()更好的方法吗?
要清楚,用户将我指向一个干燥的文本文件(一个URL),并且文件中的JS需要以某种方式生效。
我只知道两种动态导入JS脚本的方法:
<script>
标签这个问题的目的是弄清楚一个方法是否比另一个方法更安全,或者是否有比上述两个方法更好的方法。
答案 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。
如果您确实想运行任意的,不受信任的代码而不授予其访问用户数据的权限,则可以考虑使用沙箱。本质上,有多种方法可以在虚拟机中执行代码无法破解的执行。