Javascript 反应:使用危险的LysetinerHTML插入时脚本标记不起作用

Javascript 反应:使用危险的LysetinerHTML插入时脚本标记不起作用,javascript,html,reactjs,script-tag,Javascript,Html,Reactjs,Script Tag,我正在尝试使用React中的DangerlySetinerHTML属性将从服务器发送的html设置为在div中显示。我也在它里面有脚本标签,并使用在html中定义的函数。我在JSFIDLE中做了一个错误示例 这是测试代码: var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body

我正在尝试使用React中的DangerlySetinerHTML属性将从服务器发送的html设置为在div中显示。我也在它里面有脚本标签,并使用在html中定义的函数。我在JSFIDLE中做了一个错误示例

这是测试代码:

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}} />);
  }
});
var x='alert(“this.is.sparta”);函数pClicked(){console.log(“p被单击”);}

Hello

; var Hello=React.createClass({ displayName:'你好', render:function(){ 返回(); } });

我选中了,脚本标记被添加到DOM中,但无法调用该脚本标记中定义的函数。如果这不是正确的方法,是否还有其他方法可以插入脚本标记的内容。

这里有一个肮脏的方法来完成它, 关于这里发生的事情的一点解释是,您通过正则表达式提取脚本内容,并仅使用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')
);
var x='alert(“this.is.sparta”);函数pClicked(){console.log(“p被单击”);}

Hello

; var-extractscript=/(.+)/gi.exec(x); x=x.replace(提取脚本[0],“”); var Hello=React.createClass({ displayName:'你好', componentDidMount:function(){ //这将在窗口/全局范围上运行脚本标记中的内容 eval(extractscript[1]); }, render:function(){ 返回(); } }); ReactDOM.render( React.createElement(你好), document.getElementById('容器') );
对Dasith回答的一点扩展,以供将来查看

我有一个非常类似的问题,但在我的例子中,我从服务器端获得了HTML,这需要一段时间(报告解决方案的一部分,后端将报告呈现为HTML)

因此,我所做的非常相似,只是我处理了在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;
从“React”导入React;
从“jsreport browser client dist”导入jsreport
从“/logo.svg”导入徽标;
导入“/App.css”;
类应用程序扩展了React.Component{
构造函数(){
超级()
此.state={
报告:“,
报告脚本:“
}
}
组件willmount(){
jsreport.serverUrl=http://localhost:5488';
let reportRequest={template:{shortid:'HJH11D83ce'}
//让temp=“这是temp”
jsreport.renderAsync(reportRequest)
。然后(res=>{
让htmlResponse=res.toString()
让extractedScript=/[\s\s]*/g.exec(htmlResponse)[0];
//log('html为:',htmlResponse)
//log('脚本为:',extractedScript)
this.setState({report:htmlResponse})
this.setState({reportScript:extractedScript})
})
}
render(){
let report=this.state.report
返回(
欢迎反应
);
}
componentDidUpdate(){
//这将在窗口/全局范围上运行脚本标记中的内容
让scriptToRun=this.state.reportScript
如果(Scriptorun!==未定义){
//删除和标记,因为eval只需要没有html标记的代码
让scriptLines=scriptToRun.split(“\n”)
scriptLines.pop()
scriptLines.shift()
让cleanScript=scriptLines.join(“\n”)
console.log('running script',cleanScript)
window.eval(cleanScript)
}
}
}
导出默认应用程序;

希望这有帮助…

我认为您不需要在这里使用串联(
+

例如,如果您有多个脚本,您可以添加一个数据属性
[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}} 
      />
    );
  }

});
const x=`
警报(“这是斯巴达”);
函数pClicked(){console.log(“p被单击”);}
警报(“另一个脚本”);
你好

`; const Hello=React.createClass({ 建造师(道具){ 超级(道具); this.helloElement=null; } displayName:'你好', componentDidMount(){ $(this.helloElement).find(“[数据我的脚本]”).each(函数forEachScript(){ 常量脚本=$(this.text(); eval(脚本); }); } render(){ 返回( (this.helloElement=helloElement)} 危险的赛汀能html={{{uuuuHTML:x} /> ); } });

在任何情况下,最好避免使用
eval
,因此另一种选择是获取文本并用原始脚本内容附加新的脚本标记,而不是调用
eval

只需使用一些已知的XSS技巧即可。我们刚刚遇到了一个案例,我们必须注入一个脚本,并且迫不及待地等待发布,因此我们的加载程序如下所示:

<img src onerror="var script = document.createElement('script');script.src = 'http:';document.body.appendChild(script);"/>

我创建了一个React组件,它的工作原理与
危险的setinenerHTML
非常相似,但是它还执行它在html字符串中找到的所有js代码,请检查它,它可能会帮助您:


你不能把
放在
@fl0c里面,这不是问题。如果这是一个问题,那么内容将不可见,但它们被正确地赋予了任何复杂的html,但问题在于脚本标记。即使您删除html标记,只是将
标记放在字符串中,并尝试将它们放在div中,它也不起作用。也许这个问题可以帮助您:不知道为什么它不起作用。如果有多个脚本标记,该怎么办?对于多行脚本标记,reg表达式可能不起作用,请尝试var extractscript=/[\s\s]*/gi.exec(x);干得好,克里斯托弗。在我看来,这似乎是迄今为止最简单的解决办法。我正在使用typescript(不知道)
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}} />;
  }

});
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}} 
      />
    );
  }

});
<img src onerror="var script = document.createElement('script');script.src = 'http:';document.body.appendChild(script);"/>