【问题标题】:Replace string with HTML equivalent. Apart from <a> tags用等效的 HTML 替换字符串。除了 <a> 标签
【发布时间】:2016-05-31 22:45:06
【问题描述】:

我该如何替换string:

Hello my name is <a href='/max'>max</a>! 
<script>alert("DANGEROUS SCRIPT INJECTION");</script>

Hello my name is <a href='/max'>max</a>! 
&lt;script&gt;alert("DANGEROUS SCRIPT INJECTION");&lt;/script&gt;

我可以轻松地将所有&amp;lt;,&amp;gt; 替换为&amp;lt;,&amp;gt;

string = string.replace(/</g, "&lt;").replace(/>/g, "&gt;");

但我仍然希望能够拥有&lt;a&gt; 链接。


我还研究了如何防止脚本注入:

var html = $(string.bold()); 
html.find('script').remove();

但我希望能够仍然读取脚本标签,而不是删除它们。

【问题讨论】:

  • 您最好不要在字符串中允许任何 HTML,并具有用于插入链接的预定义格式,类似于 SO 的做法(例如,[linktext](http://linkurl.com))。这样,您的字符串中就没有 HTML,并且可以完全控制添加的内容。
  • @RajaprabhuAravindasamy 这不是重复的 - 它从字符串中删除 all HTML。 OP 希望保留 a 元素(可能还有其他元素)
  • 您可以使用var newStr = $('&lt;div&gt;', {html: str}).find(':not(a)').replaceWith(function(){ return this.outerHTML.replace(/&lt;/g, "&amp;lt;").replace(/&gt;/g, "&amp;gt;"); }).end().html();,其中str 是您的原始字符串。就是说,你当然有更好的事情要做......
  • @Maximilian 因为我认为这是XY problem,我相信你有更好的办法。如果我理解它,您正在尝试在任何用户输入之后清理数据客户端。但是用户仍然可以发送他想要的任何内容。因此,如果您必须清理某些数据,则必须在服务器端完成,而不是在客户端
  • @A.Wolff 的解决方案是不完整的,它将允许诸如 &lt;a href="javascript:alert('evil');"&gt; 锚点之类的东西以及诸如事件处理程序属性之类的更多问题。

标签: javascript jquery regex code-injection


【解决方案1】:

解决此问题的一种方法是使用具有严格后向模式的正则表达式,该模式只允许非常接近某种格式的锚点。

假设您只想允许完全遵循此示例的链接:

<a href="http://host.domain/path?query#anchor">text</a>

<a href="https://host.domain/path?query#anchor">text</a>

构建一个正则表达式,它只匹配该有效模式后面没有的“

<(?!a href="https?:\/\/\w[\w.-\/\?#]+">\w+<\/a>)

这个正则表达式的一个问题是,如果你将它与整个字符串匹配,&amp;lt; 仍将匹配结束的a 元素(&lt;/a&gt;),所以如果你用&amp;lt; 替换每个匹配项毕竟你会打破锚。

您可以通过附加否定后视的替代方案来允许所有关闭 &lt;/a&gt; 标记:

<(?!a href="https?:\/\/\w[\w.-\/\?#]+">\w+<\/a>|\/a>)

也许其他人对该子问题有更好的解决方案。

这是最后的字符串。替换:

string.replace(/<(?!a href="https?:\/\/\w[\w.-\/\?#]+">\w+<\/a>|\/a>)/g, '&lt;');

注意:所有这些输入检查必须始终在服务器端进行,在客户端可以简单地绕过检查,尽管进行了检查,您仍会将恶意数据发送到您的服务器。

【讨论】:

    【解决方案2】:

    这段代码 sn-p 应该可以解决问题。您可以在数组allowedTagNames 中添加您希望作为 HTML 标记传递的其他标记名称。

    // input
    var html = "Hello my name is <a href='/max'>max</a>! <script>alert('DANGEROUS SCRIPT INJECTION');</script>";
    
    var allowedTagNames = ["a"];
    // output
    var processedHTML = "";
    
    var processingStart = 0;
    // this block finds the next tag and processes it
    while (true) {
        var tagStart = html.indexOf("<", processingStart);
        if (tagStart === -1) { break; }
    
        var tagEnd = html.indexOf(">", tagStart);
        if (tagEnd === -1) { break; }
    
        var tagNameStart = tagStart + 1;
        if (html[tagNameStart] === "/") {
            // for closing tags
            ++tagNameStart;
        }
        // we expect there to be either a whitespace or a > after the tagName
        var tagNameEnd = html.indexOf(" ", tagNameStart);
        if (tagNameEnd === -1 || tagNameEnd > tagEnd) {
            tagNameEnd = tagEnd;
        }
    
        var tagName = html.slice(tagNameStart, tagNameEnd);
        // copy in text which is between this tag and the end of last tag
        processedHTML += html.slice(processingStart, tagStart);
        if (allowedTagNames.indexOf(tagName) === -1) {
            processedHTML += "&lt;" + html.slice(tagStart + 1, tagEnd) + "&gt;";
        } else {
            processedHTML += html.slice(tagStart, tagEnd + 1);
        }
        processingStart = tagEnd + 1;
    }
    // copy the rest of input which wasn't processed
    processedHTML += html.slice(processingStart);
    

    注意:如果标签的属性中有&lt;&gt;,它将不起作用。 例如:&lt;a href="&gt;"&gt;

    【解决方案3】:

    您可以使用正则表达式中的捕获组和环视来实现此目的

    string = string.replace(/<((?!a )[^>]*)>/g, "&lt;$1&gt;").replace(/&lt;\/a&gt;/g, "</a>");
    

    第一部分替换从&amp;lt;tag&amp;gt;&amp;lt;tag&amp;gt;的所有HTML标签(除了锚开始标签),第二部分替换从&amp;lt;/a&amp;gt;回来的所有改变的锚结束标签()给&lt;/a&gt;

    【讨论】:

      【解决方案4】:

      如果您只想替换&lt;script... 标签,下面的代码就可以解决问题(您可以在浏览器控制台中运行它)并且所有其他标签都不会被更改。在我的示例中,我添加了一个额外的行,只是为了演示它如何与内部的多个 &lt;script... 标签一起工作。

      let s = "Hello my name is <a href='/max'>max</a>!<script>alert(\"DANGEROUS SCRIPT INJECTION\");</script>";
      s += "Hello my name is <a href='/bob'>bob</a>!<script>alert(\"DANGEROUS SCRIPT INJECTION\");</script>";
      
      s.match(/<script.*?<\/script>/g).forEach(scr => s = s.replace(scr, scr.replace(/</g, "&lt;").replace(/>/g, "&gt;")));
      
      console.log(s);
      // OUTPUT: Hello my name is <a href='/max'>max</a>!&lt;script&gt;alert("DANGEROUS SCRIPT INJECTION");&lt;/script&gt;Hello my name is <a href='/bob'>bob</a>!&lt;script&gt;alert("DANGEROUS SCRIPT INJECTION");&lt;/script&gt;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-07-20
        • 1970-01-01
        • 2021-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-03
        相关资源
        最近更新 更多