web-dev-qa-db-ja.com

特定のホワイトリストを除くすべてのHTMLタグをフィルタリングするにはどうすればよいですか?

これは.NET用です。 IgnoreCaseが設定され、MultiLineが設定されていません。

通常、私は正規表現でまともです、多分私はカフェインが不足しています...

ユーザーは、HTMLでエンコードされたエンティティ(<lt;、<amp;など)を入力して、次のHTMLタグを使用できます。

u, i, b, h3, h4, br, a, img

自己閉鎖の<br/>および<img />は、余分なスペースの有無にかかわらず許可されていますが、必須ではありません。

したい:

  1. 上記以外のすべての開始および終了HTMLタグを削除します。
  2. 残りのタグから属性を削除します。exceptアンカーはhrefを持つことができます。

これまでの私の検索パターン(空の文字列に置き換えられました):

<(?!i|b|h3|h4|a|img|/i|/b|/h3|/h4|/a|/img)[^>]+>

これは、開始タグと終了タグ以外のすべてを取り除いているようですが、3つの問題があります。

  1. 許可された各タグの終了タグバージョンを含める必要があるのは醜いです。
  2. 属性は存続します。これは1回の交換で発生しますか?
  3. タグで始まる許可されたタグ名が抜け落ちます。例:「<abbrev>」および「<iframe>」。

次の推奨パターンでは、属性のないタグは削除されません。

</?(?!i|b|h3|h4|a|img)\b[^>]*>

後述するように、「」は属性値で合法ですが、私はそれをサポートしないと言っても安全です。また、気になるCDATAブロックなどはありません。ほんの少しのHTML。

抜け穴の答えは今のところ最高です、ありがとう!これが彼のパターンです(PREが私にとってうまくいくことを願っています):

static string SanitizeHtml(string html)
{
    string acceptable = "script|link|title";
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>";
    return Regex.Replace(html, stringPattern, "sausage");
}

私はこの答えにまだできるかもしれないと思ういくつかの小さな調整:

  1. これを変更して、「acceptable」変数に「!-」を追加し、式の最後に小さな変更を加えてオプションの末尾を許可することで、単純なHTMLコメント(それ自体はタグを含まないコメント)をキャプチャできると思います「\ s--」。

  2. 属性間に複数の空白文字がある場合、これは壊れると思います(例:改行と属性間のタブを含む、高度にフォーマットされたHTML)。

編集2009-07-23:これが私が(VB.NETで)行った最終的な解決策です:

 Dim AcceptableTags As String = "i|b|u|sup|sub|ol|ul|li|br|h2|h3|h4|h5|span|div|p|a|img|blockquote"
 Dim WhiteListPattern As String = "</?(?(?=" & AcceptableTags & _
      ")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>"
 html = Regex.Replace(html, WhiteListPattern, "", RegExOptions.Compiled)

注意点は、AタグのHREF属性が依然としてスクラブされることであり、これは理想的ではありません。

35
richardtallent

このタスク用に私が書いた関数は次のとおりです。

static string SanitizeHtml(string html)
{
    string acceptable = "script|link|title";
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>";
    return Regex.Replace(html, stringPattern, "sausage");
}

編集:何らかの理由で、以前の回答の修正を別の回答として投稿したので、ここにまとめます。

少し長いので、正規表現について少し説明します。

最初の部分は、開き括弧と0または1のスラッシュに一致します(終了タグの場合)。

次に、先読みしたif-thenコンストラクトが表示されます。 (?(?= SomeTag)then | else)文字列の次の部分が受け入れ可能なタグの1つであるかどうかを確認しています。正規表現文字列を許容可能な変数と連結していることがわかります。これは、バーティクルバーで区切られた許容可能なタグ名であり、いずれかの用語が一致します。一致する場合は、「notag」という単語を入力したことがわかります。これは、タグが一致しないためです。許容できる場合は、そのままにしておきます。それ以外の場合は、else部分に移動します。ここで、任意のタグ名[a-z、A-Z、0-9] +に一致します

次に、0以上の属性に一致させたいのですが、attribute = "value"の形式であると想定しています。ここで、属性を表すこの部分をグループ化しますが、?:を使用して、このグループが高速でキャプチャされないようにします:(?:\ s [az、AZ、0-9、-] + =?(?:([" "、 ']?)?\ 1?))

ここでは、タグ名と属性名の間にある空白文字から始めて、属性名に一致させます:[a-z、A-Z、0-9、-] +

次に、等号と一致させてから、どちらかを引用します。引用がキャプチャされるように引用をグループ化し、後で同じタイプの引用と一致するように後方参照\ 1を行うことができます。これら2つの引用符の間に、何とでも一致させるためにピリオドを使用していますが、遅延バージョンを使用しています*?貪欲なバージョン*の代わりに、この値を終了する次の引用までしか一致しません。

次に、グループを括弧で閉じた後、*を入れて、複数の属性/値の組み合わせ(またはなし)に一致するようにします。最後に、空白と\ s、およびxmlスタイルの自己終了タグのタグ内の0または1の末尾スラッシュを照合します。

お腹が空いているので、タグをソーセージに置き換えているのがわかりますが、タグを空の文字列に置き換えて、単に消去することもできます。

30
Jason Kelley

これは、htmlタグフィルタリングの良い動作例です。

HTMLをサニタイズ

11
CMS

属性は、正規表現を使用してHTMLを操作しようとする場合の主な問題です。潜在的な属性の数が非常に多いこと、それらのほとんどがオプションであること、それらが任意の順序で出現できること、および「>」が引用符で囲まれた属性値の正当な文字であることを考慮してください。これらすべてを考慮に入れ始めると、すべてを処理する必要がある正規表現はすぐに管理できなくなります。

代わりに、イベントベースのHTMLパーサー、またはウォークスルー可能なDOMツリーを提供するパーサーを使用します。

2
Sherm Pendley

私は、現在のソリューションが次で始まるのタグを許可するタグを許可していることに気づきました。したがって、「b」が受け入れ可能なタグであれば、「blink」も可能です。たいしたことではありませんが、HTMLのフィルタリング方法に厳しい場合は考慮すべき点があります。 「s」が「スクリプト」を許可するので、「s」を受け入れ可能なタグとして許可したくないのは確かです。

2
richardtallent

Word境界の追加\ bが機能しなかった理由は、先読み内に配置しなかったためです。したがって、<の後に\ bが試行され、<がHTMLタグを開始した場合は常に一致します。

次のように先読みの中に入れます。

<(?!/?(i|b|h3|h4|a|img)\b)[^>]+>

これは、各タグではなく、タグのリストの前に/を置く方法も示しています。

1
Jan Goyvaerts
    /// <summary>
    /// Trims the ignoring spacified tags
    /// </summary>
    /// <param name="text">the text from which html is to be removed</param>
    /// <param name="isRemoveScript">specify if you want to remove scripts</param>
    /// <param name="ignorableTags">specify the tags that are to be ignored while stripping</param>
    /// <returns>Stripped Text</returns>
    public static string StripHtml(string text, bool isRemoveScript, params string[] ignorableTags)
    {
        if (!string.IsNullOrEmpty(text))
        {
            text = text.Replace("&lt;", "<");
            text = text.Replace("&gt;", ">");
            string ignorePattern = null;

            if (isRemoveScript)
            {
                text = Regex.Replace(text, "<script[^<]*</script>", string.Empty, RegexOptions.IgnoreCase);
            }
            if (!ignorableTags.Contains("style"))
            {
                text = Regex.Replace(text, "<style[^<]*</style>", string.Empty, RegexOptions.IgnoreCase);
            }
            foreach (string tag in ignorableTags)
            {
                //the character b spoils the regex so replace it with strong
                if (tag.Equals("b"))
                {
                    text = text.Replace("<b>", "<strong>");
                    text = text.Replace("</b>", "</strong>");
                    if (ignorableTags.Contains("strong"))
                    {
                        ignorePattern = string.Format("{0}(?!strong)(?!/strong)", ignorePattern);
                    }
                }
                else
                {
                    //Create ignore pattern fo the tags to ignore
                    ignorePattern = string.Format("{0}(?!{1})(?!/{1})", ignorePattern, tag);
                }

            }
            //finally add the ignore pattern into regex <[^<]*> which is used to match all html tags
            ignorePattern = string.Format(@"<{0}[^<]*>", ignorePattern);
            text = Regex.Replace(text, ignorePattern, "", RegexOptions.IgnoreCase);
        }

        return text;
    }
0
Chirag

元々は値をオプションにすることを意図していたと思いますが、等号の後に?を追加し、一致の値の部分をグループ化したことがわかるので、実行しませんでした。そのグループの後に?を追加して(キャロットのマークが付けられています)、マッチでもオプションにするようにします。私は今コンパイラにいませんが、これが機能するかどうかを確認してください:

@"</?(?(?=" + acceptable + @")notag|[a-z,A-Z,0-9]+)(?:\s[a-z,A-Z,0-9,\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>";
                                                                                             ^
0
Jason Kelley