Is it possible to tell browsers to not run JavaScript from specific parts of an HTML document?
有没有可能告诉浏览器不要从HTML文档的特定部分运行JavaScript ?
Like:
如:
<div script="false"> ...
It could be useful as an additional security feature. All the scripts I want are loaded in a specific part of the document. There should be no scripts in other parts of the document and if there are they should not be run.
它可以作为一个附加的安全特性。我想要的所有脚本都加载在文档的特定部分。文档的其他部分不应该有脚本,如果有脚本,就不应该运行它们。
7 个解决方案
#1
89
YES, you can :-) The answer is: Content Security Policy (CSP).
是的,您可以:-)答案是:内容安全策略(CSP)。
Most modern browsers support this flag, which tells the browser only to load JavaScript code from a trusted external file and disallow all internal JavaScript code! The only downside is, you can not use any inline JavaScript in your whole page (not only for a single <div>
). Although there could be a workaround by dynamically including the div from an external file with a different security policy, but I'm not sure about that.
大多数现代浏览器都支持这个标志,它告诉浏览器只能从受信任的外部文件加载JavaScript代码,并且不允许所有内部JavaScript代码!唯一的缺点是,您不能在整个页面中使用任何内联JavaScript(不仅仅是一个
But if you can change your site to load all JavaScript from external JavaScript files then you can disable inline JavaScript altogether with this header!
但是,如果您可以更改您的站点以从外部JavaScript文件加载所有JavaScript,那么您可以使用这个header禁用内联JavaScript !
Here is a nice tutorial with example: HTML5Rocks Tutorial
这里有一个很好的例子:HTML5Rocks教程
If you can configure the server to send this HTTP-Header flag the world will be a better place!
如果您可以配置服务器发送这个HTTP-Header标记,那么世界将会变得更好!
#2
13
You can block JavaScript loaded by <script>
, using beforescriptexecute
event:
可以使用beprecriptexecute事件阻止
<script>
// Run this as early as possible, it isn't retroactive
window.addEventListener('beforescriptexecute', function(e) {
var el = e.target;
while(el = el.parentElement)
if(el.hasAttribute('data-no-js'))
return e.preventDefault(); // Block script
}, true);
</script>
<script>console.log('Allowed. Console is expected to show this');</script>
<div data-no-js>
<script>console.log('Blocked. Console is expected to NOT show this');</script>
</div>
Note that beforescriptexecute
was defined in HTML 5.0 but has been removed in HTML 5.1. Firefox is the only major browser that implemented it.
注意,在HTML 5.0中定义了beprecriptexecute,但在HTML 5.1中已经删除了。Firefox是唯一实现它的主要浏览器。
In case you are inserting an untrusted bunch of HTML in your page, be aware blocking scripts inside that element won't provide more security, because the untrusted HTML can close the sandboxed element, and thus the script will be placed outside and run.
如果您在页面中插入了一堆不受信任的HTML,请注意,在该元素内部的阻塞脚本不会提供更多的安全性,因为不受信任的HTML可以关闭沙盒元素,因此脚本将放置在外部并运行。
And this won't block things like <img onerror="javascript:alert('foo')" src="//" />
.
这不会阻塞诸如。
#3
8
Interesting question, I don't think it's possible. But even if it is, it sounds like it would be a hack.
有趣的问题,我认为这是不可能的。但即使是这样,听起来也像是一个黑客。
If the contents of that div are untrusted, then you need to escape the data on the server side before it is sent in the HTTP response and rendered in the browser.
如果该div的内容不受信任,那么您需要在将数据发送到HTTP响应并在浏览器中呈现之前转义服务器端上的数据。
If you only want to remove <script>
tags and allow other html tags, then just strip them out of the content and leave the rest.
如果您只想删除
Look into XSS prevention.
看着XSS的预防。
https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
#4
7
JavaScript is executed "inline", i.e. in the order in which it appears in the DOM (if that wasn't the case, you could never be sure that some variable defined in a different script was visible when you used it for the first time).
JavaScript是“内联”执行的,即按照它在DOM中出现的顺序执行(如果不是这样,第一次使用它时,您永远无法确定在不同脚本中定义的某个变量是否可见)。
So that means in theory you could have a script at the beginning of the page (i.e. first <script>
element) which looks through the DOM and removed all <script>
elements and event handlers inside of your <div>
.
这意味着在理论上,您可以在页面的开头有一个脚本(例如,第一个
But the reality is more complex: DOM and script loading happens asynchronously. This means that the browser only guarantees that a script can see the part of the DOM which is before it (i.e. the header so far in our example). There are no guarantees for anything beyond (this is related to document.write()
). So you might see the next script tag or maybe, you don't.
但实际情况更为复杂:DOM和脚本加载是异步进行的。这意味着浏览器只保证脚本可以看到DOM前面的部分(例如,在我们的示例中到目前为止的头)。除了(这与document.write()有关)之外,没有任何保证。你可能会看到下一个脚本标签,或者你不会。
You could latch to the onload
event of the document - which would make sure you got the whole DOM - but at that time, malicious code could have already executed. Things get worse when other scripts manipulate the DOM, adding scripts there. So you would have to check for every change of the DOM, too.
您可以锁定文档的onload事件(这将确保您获得整个DOM),但那时,恶意代码可能已经执行。当其他脚本操作DOM并在其中添加脚本时,情况会变得更糟。因此,您还必须检查DOM的每个更改。
So @cowls solution (filtering on the server) is the only solution which can be made to work in all situations.
因此,@cowls解决方案(在服务器上进行过滤)是在所有情况下都可以使用的唯一解决方案。
#5
1
If you're looking to display JavaScript code in your browser:
如果您希望在浏览器中显示JavaScript代码:
Using JavaScript and HTML, you'll have to use HTML entities to display the JavaScript code and avoiding this code to be executed. Here you can find the list of HTML entities:
使用JavaScript和HTML,您必须使用HTML实体来显示JavaScript代码,并避免执行此代码。在这里你可以找到HTML实体的列表:
- http://dev.w3.org/html5/html-author/charref
- http://dev.w3.org/html5/html-author/charref
- http://www.w3schools.com/html/html_entities.asp
- http://www.w3schools.com/html/html_entities.asp
If you're using a server-side scripting language (PHP, ASP.NET, etc.), most probably, there's a function which would escape a string and convert the special characters into HTML entities. In PHP, you would use "htmlspecialchars()" or "htmlentities()". The latter covers all the HTML characters.
如果您正在使用服务器端脚本语言(PHP、ASP)。最可能的是,有一个函数可以转义字符串,并将特殊字符转换为HTML实体。在PHP中,您将使用“htmlspecialchars()”或“htmlentities()”。后者包含所有HTML字符。
If you're looking to display your JavaScript code in a nice way, then try one of the code highlighters:
如果您希望以一种良好的方式显示JavaScript代码,那么尝试使用其中一个代码高亮显示器:
- http://prismjs.com/
- http://prismjs.com/
- http://alexgorbatchev.com/SyntaxHighlighter/
- http://alexgorbatchev.com/SyntaxHighlighter/
- https://highlightjs.org/
- https://highlightjs.org/
#6
1
I've got a theory:
我有一个理论:
- Wrap the specific part of the document inside a
noscript
tag. - 在noscript标签中包装文档的特定部分。
- Use DOM functions to discard all
script
tags inside thenoscript
tag then unwrap its contents. - 使用DOM函数丢弃noscript标记中的所有脚本标记,然后展开其内容。
Proof of concept example:
概念证明的例子:
window.onload = function() {
var noscripts = /* _live_ list */ document.getElementsByTagName("noscript"),
memorydiv = document.createElement("div"),
scripts = /* _live_ list */ memorydiv.getElementsByTagName("script"),
i,
j;
for (i = noscripts.length - 1; i >= 0; --i) {
memorydiv.innerHTML = noscripts[i].textContent || noscripts[i].innerText;
for (j = scripts.length - 1; j >= 0; --j) {
memorydiv.removeChild(scripts[j]);
}
while (memorydiv.firstChild) {
noscripts[i].parentNode.insertBefore(memorydiv.firstChild, noscripts[i]);
}
noscripts[i].parentNode.removeChild(noscripts[i]);
}
};
body { font: medium/1.5 monospace; }
p, h1 { margin: 0; }
<h1>Sample Content</h1>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
<h1>Sample Content in No-JavaScript Zone</h1>
<noscript>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
</noscript>
<noscript>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
</noscript>
#7
0
If you want to re-enable script tags later on, my solution was to break the browser environment so that any script that runs will throw an error fairly early. However, it's not totally reliable, so you can't use it as a security feature.
如果您希望稍后重新启用脚本标记,我的解决方案是破坏浏览器环境,这样运行的任何脚本都会提前抛出错误。但是,它并不完全可靠,所以您不能将它用作安全特性。
If you try to access global properties Chrome will throw an exception.
如果你试图访问全局属性,Chrome会抛出一个异常。
setTimeout("Math.random()")
// => VM116:25 Uncaught Error: JavaScript Execution Inhibited
I'm overwriting all overwritable properties on window
, but you could also expand it to break other functionality.
我正在覆盖窗口上所有可重写的属性,但是您也可以扩展它来破坏其他功能。
window.allowJSExecution = inhibitJavaScriptExecution();
function inhibitJavaScriptExecution(){
var windowProperties = {};
var Object = window.Object
var console = window.console
var Error = window.Error
function getPropertyDescriptor(object, propertyName){
var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
if (!descriptor) {
return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
}
return descriptor;
}
for (var propName in window){
try {
windowProperties[propName] = getPropertyDescriptor(window, propName)
Object.defineProperty(window, propName, {
get: function(){
throw Error("JavaScript Execution Inhibited")
},
set: function(){
throw Error("JavaScript Execution Inhibited")
},
configurable: true
})
} catch (err) {}
}
return function allowJSExecution(){
for (var propName in window){
if (!(propName in windowProperties)) {
delete windowProperties[propName]
}
}
for (var propName in windowProperties){
try {
Object.defineProperty(window, propName, windowProperties[propName])
} catch (err) {}
}
}
}
#1
89
YES, you can :-) The answer is: Content Security Policy (CSP).
是的,您可以:-)答案是:内容安全策略(CSP)。
Most modern browsers support this flag, which tells the browser only to load JavaScript code from a trusted external file and disallow all internal JavaScript code! The only downside is, you can not use any inline JavaScript in your whole page (not only for a single <div>
). Although there could be a workaround by dynamically including the div from an external file with a different security policy, but I'm not sure about that.
大多数现代浏览器都支持这个标志,它告诉浏览器只能从受信任的外部文件加载JavaScript代码,并且不允许所有内部JavaScript代码!唯一的缺点是,您不能在整个页面中使用任何内联JavaScript(不仅仅是一个
But if you can change your site to load all JavaScript from external JavaScript files then you can disable inline JavaScript altogether with this header!
但是,如果您可以更改您的站点以从外部JavaScript文件加载所有JavaScript,那么您可以使用这个header禁用内联JavaScript !
Here is a nice tutorial with example: HTML5Rocks Tutorial
这里有一个很好的例子:HTML5Rocks教程
If you can configure the server to send this HTTP-Header flag the world will be a better place!
如果您可以配置服务器发送这个HTTP-Header标记,那么世界将会变得更好!
#2
13
You can block JavaScript loaded by <script>
, using beforescriptexecute
event:
可以使用beprecriptexecute事件阻止
<script>
// Run this as early as possible, it isn't retroactive
window.addEventListener('beforescriptexecute', function(e) {
var el = e.target;
while(el = el.parentElement)
if(el.hasAttribute('data-no-js'))
return e.preventDefault(); // Block script
}, true);
</script>
<script>console.log('Allowed. Console is expected to show this');</script>
<div data-no-js>
<script>console.log('Blocked. Console is expected to NOT show this');</script>
</div>
Note that beforescriptexecute
was defined in HTML 5.0 but has been removed in HTML 5.1. Firefox is the only major browser that implemented it.
注意,在HTML 5.0中定义了beprecriptexecute,但在HTML 5.1中已经删除了。Firefox是唯一实现它的主要浏览器。
In case you are inserting an untrusted bunch of HTML in your page, be aware blocking scripts inside that element won't provide more security, because the untrusted HTML can close the sandboxed element, and thus the script will be placed outside and run.
如果您在页面中插入了一堆不受信任的HTML,请注意,在该元素内部的阻塞脚本不会提供更多的安全性,因为不受信任的HTML可以关闭沙盒元素,因此脚本将放置在外部并运行。
And this won't block things like <img onerror="javascript:alert('foo')" src="//" />
.
这不会阻塞诸如。
#3
8
Interesting question, I don't think it's possible. But even if it is, it sounds like it would be a hack.
有趣的问题,我认为这是不可能的。但即使是这样,听起来也像是一个黑客。
If the contents of that div are untrusted, then you need to escape the data on the server side before it is sent in the HTTP response and rendered in the browser.
如果该div的内容不受信任,那么您需要在将数据发送到HTTP响应并在浏览器中呈现之前转义服务器端上的数据。
If you only want to remove <script>
tags and allow other html tags, then just strip them out of the content and leave the rest.
如果您只想删除
Look into XSS prevention.
看着XSS的预防。
https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
#4
7
JavaScript is executed "inline", i.e. in the order in which it appears in the DOM (if that wasn't the case, you could never be sure that some variable defined in a different script was visible when you used it for the first time).
JavaScript是“内联”执行的,即按照它在DOM中出现的顺序执行(如果不是这样,第一次使用它时,您永远无法确定在不同脚本中定义的某个变量是否可见)。
So that means in theory you could have a script at the beginning of the page (i.e. first <script>
element) which looks through the DOM and removed all <script>
elements and event handlers inside of your <div>
.
这意味着在理论上,您可以在页面的开头有一个脚本(例如,第一个
But the reality is more complex: DOM and script loading happens asynchronously. This means that the browser only guarantees that a script can see the part of the DOM which is before it (i.e. the header so far in our example). There are no guarantees for anything beyond (this is related to document.write()
). So you might see the next script tag or maybe, you don't.
但实际情况更为复杂:DOM和脚本加载是异步进行的。这意味着浏览器只保证脚本可以看到DOM前面的部分(例如,在我们的示例中到目前为止的头)。除了(这与document.write()有关)之外,没有任何保证。你可能会看到下一个脚本标签,或者你不会。
You could latch to the onload
event of the document - which would make sure you got the whole DOM - but at that time, malicious code could have already executed. Things get worse when other scripts manipulate the DOM, adding scripts there. So you would have to check for every change of the DOM, too.
您可以锁定文档的onload事件(这将确保您获得整个DOM),但那时,恶意代码可能已经执行。当其他脚本操作DOM并在其中添加脚本时,情况会变得更糟。因此,您还必须检查DOM的每个更改。
So @cowls solution (filtering on the server) is the only solution which can be made to work in all situations.
因此,@cowls解决方案(在服务器上进行过滤)是在所有情况下都可以使用的唯一解决方案。
#5
1
If you're looking to display JavaScript code in your browser:
如果您希望在浏览器中显示JavaScript代码:
Using JavaScript and HTML, you'll have to use HTML entities to display the JavaScript code and avoiding this code to be executed. Here you can find the list of HTML entities:
使用JavaScript和HTML,您必须使用HTML实体来显示JavaScript代码,并避免执行此代码。在这里你可以找到HTML实体的列表:
- http://dev.w3.org/html5/html-author/charref
- http://dev.w3.org/html5/html-author/charref
- http://www.w3schools.com/html/html_entities.asp
- http://www.w3schools.com/html/html_entities.asp
If you're using a server-side scripting language (PHP, ASP.NET, etc.), most probably, there's a function which would escape a string and convert the special characters into HTML entities. In PHP, you would use "htmlspecialchars()" or "htmlentities()". The latter covers all the HTML characters.
如果您正在使用服务器端脚本语言(PHP、ASP)。最可能的是,有一个函数可以转义字符串,并将特殊字符转换为HTML实体。在PHP中,您将使用“htmlspecialchars()”或“htmlentities()”。后者包含所有HTML字符。
If you're looking to display your JavaScript code in a nice way, then try one of the code highlighters:
如果您希望以一种良好的方式显示JavaScript代码,那么尝试使用其中一个代码高亮显示器:
- http://prismjs.com/
- http://prismjs.com/
- http://alexgorbatchev.com/SyntaxHighlighter/
- http://alexgorbatchev.com/SyntaxHighlighter/
- https://highlightjs.org/
- https://highlightjs.org/
#6
1
I've got a theory:
我有一个理论:
- Wrap the specific part of the document inside a
noscript
tag. - 在noscript标签中包装文档的特定部分。
- Use DOM functions to discard all
script
tags inside thenoscript
tag then unwrap its contents. - 使用DOM函数丢弃noscript标记中的所有脚本标记,然后展开其内容。
Proof of concept example:
概念证明的例子:
window.onload = function() {
var noscripts = /* _live_ list */ document.getElementsByTagName("noscript"),
memorydiv = document.createElement("div"),
scripts = /* _live_ list */ memorydiv.getElementsByTagName("script"),
i,
j;
for (i = noscripts.length - 1; i >= 0; --i) {
memorydiv.innerHTML = noscripts[i].textContent || noscripts[i].innerText;
for (j = scripts.length - 1; j >= 0; --j) {
memorydiv.removeChild(scripts[j]);
}
while (memorydiv.firstChild) {
noscripts[i].parentNode.insertBefore(memorydiv.firstChild, noscripts[i]);
}
noscripts[i].parentNode.removeChild(noscripts[i]);
}
};
body { font: medium/1.5 monospace; }
p, h1 { margin: 0; }
<h1>Sample Content</h1>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
<h1>Sample Content in No-JavaScript Zone</h1>
<noscript>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
</noscript>
<noscript>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
</noscript>
#7
0
If you want to re-enable script tags later on, my solution was to break the browser environment so that any script that runs will throw an error fairly early. However, it's not totally reliable, so you can't use it as a security feature.
如果您希望稍后重新启用脚本标记,我的解决方案是破坏浏览器环境,这样运行的任何脚本都会提前抛出错误。但是,它并不完全可靠,所以您不能将它用作安全特性。
If you try to access global properties Chrome will throw an exception.
如果你试图访问全局属性,Chrome会抛出一个异常。
setTimeout("Math.random()")
// => VM116:25 Uncaught Error: JavaScript Execution Inhibited
I'm overwriting all overwritable properties on window
, but you could also expand it to break other functionality.
我正在覆盖窗口上所有可重写的属性,但是您也可以扩展它来破坏其他功能。
window.allowJSExecution = inhibitJavaScriptExecution();
function inhibitJavaScriptExecution(){
var windowProperties = {};
var Object = window.Object
var console = window.console
var Error = window.Error
function getPropertyDescriptor(object, propertyName){
var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
if (!descriptor) {
return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
}
return descriptor;
}
for (var propName in window){
try {
windowProperties[propName] = getPropertyDescriptor(window, propName)
Object.defineProperty(window, propName, {
get: function(){
throw Error("JavaScript Execution Inhibited")
},
set: function(){
throw Error("JavaScript Execution Inhibited")
},
configurable: true
})
} catch (err) {}
}
return function allowJSExecution(){
for (var propName in window){
if (!(propName in windowProperties)) {
delete windowProperties[propName]
}
}
for (var propName in windowProperties){
try {
Object.defineProperty(window, propName, windowProperties[propName])
} catch (err) {}
}
}
}