将HTML插入用户字符串并在React中呈现(并避免XSS威胁)

add*_*itt 5 html javascript reactjs

用户正在向我们的React应用提供一个字符串,并正在向其他用户显示。我想搜索一些字符,并用一些HTML替换它们,例如,如果要搜索“特殊”一词,我会将其转换为:

My <span class="special-formatting">special</span> word in a user string

以前,我是执行此替换操作,然后将结果通过危险地插入到HTML中。现在,这当然给了我一个问题,用户可以在应用程序中键入并输入他们喜欢的HTML / Javascript,然后将其呈现给所有人查看。

我试图将HTML字符转义到它们的实体,但是危险地SetInnerHTML似乎正确地呈现了HTML实体,而不是将其呈现为实际的字符串。(编辑:见下文,这是实际的解决方案)

有什么方法可以将其消息转换为纯字符串,同时仍保留这些特殊字符的显示,还可以在字符串中插入我自己的HTML?试图避免在将每个字符串插入DOM后运行脚本。


这是有关当前流量的更多信息。所有示例都经过了优化,仅显示相关代码。

使用以下功能将用户文本提交到数据库:

handleSubmit(event) {
        event.preventDefault();

        var messageText = this.state.messageValue;

        //bold font is missing some common characters, fake way of making the normal font look bold
        if (this.state.bold == true) {
            messageText = messageText.replace(/\'/g, "<span class='bold-apostrophe'>'</span>");
            messageText = messageText.replace(/\"/g, "<span class='bold-quote'>&quot;</span>");
            messageText = messageText.replace(/\?/g, "<span class='bold-question'>?</span>");
            messageText = messageText.replace(/\*/g, "<span class='bold-asterisk'>*</span>");
            messageText = messageText.replace(/\+/g, "<span class='bold-plus'>+</span>");
            messageText = messageText.replace(/\./g, "<span class='bold-period'>.</span>");
            messageText = messageText.replace(/\,/g, "<span class='bold-comma'>,</span>");
        }

        Messages.insert({
            text: messageText,
            createdAt: new Date(),
            userId: user._id,
            bold: this.state.bold,
        });

    }
Run Code Online (Sandbox Code Playgroud)

因此,我进行了替换,没有问题,但是在这一点上,messageText字符串仍可能包含不需要的用户输入HTML代码。

然后,带有消息列表的主应用程序尝试呈现所有用户消息:

render() {
    return (
        <div ref="messagesList">
            {this.renderMessages()}
        </div>
    );
}
Run Code Online (Sandbox Code Playgroud)
renderMessages() {
    return [].concat(this.props.messages).reverse().map((message) => {
        return <Message
            key={message._id}
            message={message} />;
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

在Message.jsx中,我要对消息字符串进行最后修改(某些更改我不想保存到消息数据库中)并将其插入到元素中以返回:

export default class Message extends React.Component {
    render() {

        var processedMessageText = this.props.message.text;

        //another find and replace to insert images for :image_name: strings, similar to how Discord inputs its emoji
        processedMessageText = processedMessageText.replace(/:([\w]+):/g, function (text) {
            text = text.replace(/:/g, "");
            if (text.indexOf("_s") !== -1) {
                text = text.replace(/_s/g, "");
                text = "<img class='small-smiley' src='/smileys/small/" + text + ".png'>";
                return text;
            }
            else {
                text = "<img class='smiley' src='/smileys/" + text + ".png'>";
                return text;
            }
        });

        return (
            <div>
                <div className='username'>{this.props.message.username}: </div>
                <div className='text' dangerouslySetInnerHTML={{ __html: processedMessageText }}></div>
            </div>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

同样,如果用户在其输入字符串中包含恶意HTML,它将遍历所有这些内容并将其输出到消息列表,这确实很糟糕。我希望能以某种方式在字符串中执行这些所需的HTML插入,同时又不将它们可能输入的HTML呈现为实际HTML。我还想展示HTML中常用的字符,例如尖括号(<>),因此我要避免直接剥离它们的常见HTML字符输入字符串。


由于接受的答案没有太多细节,因此我将在此处发布最终的操作。在添加我自己的HTML并将其呈现到HTML元素的内容中之前,我对OWASP建议的字符进行了HTML编码。我想避免使用另一个库,所以我这样做了:

messageText = messageText.replace(/\&/g, "&amp;");
messageText = messageText.replace(/</g, "&lt;");
messageText = messageText.replace(/>/g, "&gt;");
messageText = messageText.replace(/\//g, "&#x2F;");
messageText = messageText.replace(/\'/g, "&#x27;");
messageText = messageText.replace(/\"/g, "&quot;");
Run Code Online (Sandbox Code Playgroud)

这样做之后,我不再能够插入任何恶意文件,并且使用OWASP中的各种测试字符串进行了测试,没有出现问题。

Nic*_*rey 2

你就不能

  1. 对来自用户的受污染的字符串进行 HTML 编码。
  2. 进行搜索/替换并插入 HTML。
  3. 然后执行dangerouslySetInnerHTML().

这应该可以安全地转义用户输入的任何内容并保留插入的 HTML 元素,不是吗?