Javascript React:使用危险的SetInnerHTML插入时脚本标签不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35614809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
React: Script tag not working when inserted using dangerouslySetInnerHTML
提问by meteors
I'm trying to set html sent from my server to show inside a div using dangerouslySetInnerHTML property in React. I also have script tag inside it and use functions defined in same inside that html. I have made example of error in JSFiddle here.
我正在尝试使用 React 中的 risklySetInnerHTML 属性设置从我的服务器发送的 html 以显示在 div 内。我在里面也有脚本标签,并使用在该 html 中定义的函数。我在这里做了 JSFiddle 中的错误示例。
This is test code:
这是测试代码:
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
var Hello = React.createClass({
displayName: 'Hello',
render: function() {
return (<div dangerouslySetInnerHTML={{__html: x}} />);
}
});
I checked and the script tag is added to DOM, but cannot call the functions defined within that script tag. If this is not the correct way is there any other way by which I can inject the script tag's content.
我检查了脚本标签已添加到 DOM,但无法调用该脚本标签中定义的函数。如果这不是正确的方法,是否还有其他方法可以注入脚本标记的内容。
采纳答案by Dasith
Here's a bit of a dirty way of getting it done , A bit of an explanation as to whats happening here , you extract the script contents via a regex , and only render html using react , then after the component is mounted the content in script tag is run on a global scope.
这是完成它的一种肮脏的方式,对这里发生的事情的一些解释,您通过正则表达式提取脚本内容,并仅使用 react 呈现 html,然后在组件安装后脚本标记中的内容在全局范围内运行。
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
var extractscript=/<script>(.+)<\/script>/gi.exec(x);
x=x.replace(extractscript[0],"");
var Hello = React.createClass({
displayName: 'Hello',
componentDidMount: function() {
// this runs the contents in script tag on a window/global scope
window.eval(extractscript[1]);
},
render: function() {
return (<div dangerouslySetInnerHTML={{__html: x}} />);
}
});
ReactDOM.render(
React.createElement(Hello),
document.getElementById('container')
);
回答by emil.c
I don't think you need to use concatenation (+
) here.
我认为您不需要在+
此处使用连接 ( )。
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
I think you can just do:
我认为你可以这样做:
var x = '<html><script>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</script><body><p onClick="pClicked()">Hello</p></body></html>';
Since it's passed to dangerouslySetInnerHTML
anyway.
既然传给了dangerouslySetInnerHTML
反正。
But let's get back to the issue. You don't need to use regex to access the script tag's content. If you add id
attribute, for example <script id="myId">...</script>
, you can easily access the element.
但是让我们回到这个问题。您不需要使用正则表达式来访问脚本标签的内容。id
例如<script id="myId">...</script>
,如果添加属性,则可以轻松访问该元素。
Let's see an example of such implementation.
让我们看一个这种实现的例子。
const x = `
<html>
<script id="myScript">
alert("this.is.sparta");
function pClicked() {console.log("p is clicked");}
</script>
<body>
<p onClick="pClicked()">Hello</p>
</body>
</html>
`;
const Hello = React.createClass({
displayName: 'Hello',
componentDidMount() {
const script = document.getElementById('myScript').innerHTML;
window.eval(script);
}
render() {
return <div dangerouslySetInnerHTML={{__html: x}} />;
}
});
If you have multiple scripts, you can add a data attribute [data-my-script]
for example, and then access it using jQuery:
如果您有多个脚本,您可以添加一个数据属性[data-my-script]
,例如,然后使用 jQuery 访问它:
const x = `
<html>
<script data-my-script="">
alert("this.is.sparta");
function pClicked() {console.log("p is clicked");}
</script>
<script data-my-script="">
alert("another script");
</script>
<body>
<p onClick="pClicked()">Hello</p>
</body>
</html>
`;
const Hello = React.createClass({
constructor(props) {
super(props);
this.helloElement = null;
}
displayName: 'Hello',
componentDidMount() {
$(this.helloElement).find('[data-my-script]').each(function forEachScript() {
const script = $(this).text();
window.eval(script);
});
}
render() {
return (
<div
ref={helloElement => (this.helloElement = helloElement)}
dangerouslySetInnerHTML={{__html: x}}
/>
);
}
});
In any case, it's always good to avoid using eval
, so another option is to get the text and append a new script tag with the original's script contents instead of calling eval
. This answer suggests such approach
在任何情况下,避免使用 总是好的eval
,因此另一种选择是获取文本并使用原始脚本内容附加一个新的脚本标记,而不是调用eval
. 这个答案表明了这种方法
回答by Tal Joffe
a little extension for Dasith's answer for future views...
Dasith 对未来观点的回答的一点扩展......
I had a very similar issue but the in my case I got the HTML from the server side and it took a while (part of reporting solution where backend will render report to html)
我有一个非常相似的问题,但在我的情况下,我从服务器端获取了 HTML,这花了一段时间(报告解决方案的一部分,其中后端将报告呈现为 html)
so what I did was very similar only that I handled the script running in the componentWillMount() function:
所以我所做的非常相似,只是我处理了在 componentWillMount() 函数中运行的脚本:
import React from 'react';
import jsreport from 'jsreport-browser-client-dist'
import logo from './logo.svg';
import './App.css';
class App extends React.Component {
constructor() {
super()
this.state = {
report: "",
reportScript: ""
}
}
componentWillMount() {
jsreport.serverUrl = 'http://localhost:5488';
let reportRequest = {template: {shortid: 'HJH11D83ce'}}
// let temp = "this is temp"
jsreport.renderAsync(reportRequest)
.then(res => {
let htmlResponse = res.toString()
let extractedScript = /<script>[\s\S]*<\/script>/g.exec(htmlResponse)[0];
// console.log('html is: ',htmlResponse)
// console.log('script is: ',extractedScript)
this.setState({report: htmlResponse})
this.setState({reportScript: extractedScript})
})
}
render() {
let report = this.state.report
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<h2>Welcome to React</h2>
</div>
<div id="reportPlaceholder">
<div dangerouslySetInnerHTML={{__html: report}}/>
</div>
</div>
);
}
componentDidUpdate() {
// this runs the contents in script tag on a window/global scope
let scriptToRun = this.state.reportScript
if (scriptToRun !== undefined) {
//remove <script> and </script> tags since eval expects only code without html tags
let scriptLines = scriptToRun.split("\n")
scriptLines.pop()
scriptLines.shift()
let cleanScript = scriptLines.join("\n")
console.log('running script ',cleanScript)
window.eval(cleanScript)
}
}
}
export default App;
hope this is helpful...
希望这是有帮助的...
回答by Cristofer Flores
I created a React component that works pretty much like dangerouslySetInnerHtml
but additionally it executes all the js code that it finds on the html string, check it out, it might help you:
我创建了一个 React 组件,它的工作原理非常相似,dangerouslySetInnerHtml
但另外它会执行它在 html 字符串上找到的所有 js 代码,检查一下,它可能对你有帮助:
回答by Firat Oezcan
Just use some known XSS tricks. We just had a case where we had to inject a script and couldn't wait for the release so here goes our loader:
只需使用一些已知的 XSS 技巧即可。我们刚刚遇到了一个案例,我们不得不注入一个脚本并且迫不及待地等待发布,所以我们的加载器开始了:
<img src onerror="var script = document.createElement('script');script.src = 'http:';document.body.appendChild(script);"/>