删除用户输入字段中过多的空格

时间:2012-04-12 13:19:10

标签: c# asp.net-mvc-3

在用于处理(可能具有敌意的)用户输入字段的控制器方法中,我有以下代码:

string tmptext = comment.Replace(System.Environment.NewLine, "{break was here}"); //marks line breaks for later re-insertion
tmptext = Encoder.HtmlEncode(tmptext);
//other sanitizing goes in here 
tmptext = tmptext.Replace("{break was here}", "<br />");

var regex = new Regex("(<br /><br />)\\1+");
tmptext = regex.Replace(tmptext, "$1");

我的目标是保留典型非恶意使用的换行符,并在安全的htmlencoded字符串中显示用户输入。我接受用户输入,解析换行符并在换行符处放置分隔符。我执行HTML编码并重新插入休息符。 (我可能会改变这一点,将段落重新插入p标签而不是br,但现在我正在使用br)

现在实际插入真正的html中断让我有了一个微妙的漏洞:输入密钥。 regex.replace代码用于删除只是站在输入键上并用垃圾填充页面的恶意用户。

这是对白色大垃圾洪水的修复,但仍然让我容易被滥用,例如在页面上输入一个字符,两个换行符,一个字符,两个换行符。

我的问题是确定这是滥用并且在验证时失败的方法。我很害怕可能没有一个简单的程序方法来实现它,而是需要启发式技术或贝叶斯过滤器。希望有人有一个更容易,更好的方法。

编辑:也许我在问题描述中并不清楚,正则表达式处理连续看到多个换行符并将它们转换为一个或两个。那个问题解决了。真正的问题是将合法文本与废话洪水区分开来:

...想象其中的1000个......

5 个答案:

答案 0 :(得分:0)

听起来你很想用正则表达式尝试一些“聪明”的东西,但IMO最简单的方法是循环遍历字符串的字符,将它们复制到StringBuilder中,然后过滤。

任何未通过char.IsWhiteSpace()测试失败的测试都不会被复制。 (如果其中一个是换行符,则插入&lt; br /&gt;并且不再允许添加&lt; br /&gt;'s,直到您点击非空白字符为止。)

修改

如果您想阻止用户输入任何旧垃圾,请立即放弃。如果他们真的想要,你永远不会找到过滤用户无法在不到一分钟内找到解决方法的方法。

在输入中限制换行符数或总字符数时,你会好得多。

想想做一些聪明的事情来消除“糟糕的输入”需要多少努力,然后考虑这种情况发生的可能性。 Probbaly没有意义。您真正需要的所有卫生处理可能都是为了确保数据合法(对于您的系统来说不是太大而无法处理,所有危险的角色都被剥离或逃脱等)。 (这正是为什么论坛有人类主持人可以根据适当的标准过滤帖子的原因。)

答案 1 :(得分:0)

我会HttpUtility.HtmlEncode字符串,然后将换行符转换为<br/>

HttpUtility.HtmlEncode(subject).Replace("\r\n", "<br/>").Replace("\r", "<br/>").Replace("\n", "<br/>");

此外,您应该在输出给用户时执行此逻辑,而不是在保存在数据库中时。我对数据库进行的唯一验证是确保它已正确转义(除了正常的业务规则之外)。

编辑:但要解决实际问题,您可以使用正则表达式预先用一个换行符替换多个换行符。

subject = Regex.Replace(@"(\r\n|\r|\n)+", @"\n", RegexOptions.Singleline);

我不确定您是否需要RegexOptions.Singleline

答案 2 :(得分:0)

为什么在插入<br />标签之前不清理数据,而不是尝试用过滤后的文本替换换行符然后尝试使用正则表达式?不要忘记首先使用HttpUtility.HtmlEncode清理输入。

为了尝试连续多条短线,这是我最好的尝试:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class Program {
  static void Main() {
    // Arbirary cutoff used to join short strings.
    const int Cutoff = 6;

    string input =
      "\r\n\r\n\n\r\r\r\n\nthisisatest\r\nstring\r\nwith\nsome\r\n" + 
      "unsanatized\r\nbreaks\r\nand\ra\nsh\nor\nt\r\n\na\na\na\na" +
      "\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na";
    input = (input ?? String.Empty).Trim(); // Don't forget to HtmlEncode it.
    StringBuilder temp = new StringBuilder();
    List<string> result = new List<string>();
    var items = input.Split(
                        new[] { '\r', '\n' },
                        StringSplitOptions.RemoveEmptyEntries)
                     .Select(i => new { i.Length, Value = i });

    foreach (var item in items) {
      if (item.Length > Cutoff) {
        if (temp.Length > 0) {
          result.Add(temp.ToString());
          temp.Clear();
        }

        result.Add(item.Value);
        continue;
      }

      if (temp.Length > 0) { temp.Append(" "); }
      temp.Append(item.Value);
    }

    if (temp.Length > 0) {
      result.Add(temp.ToString());
    }

    Console.WriteLine(String.Join("<br />", result));
  }
}

产生以下输出:

thisisatest<br />string with some<br />unsanatized<br />breaks and a sh or t a a
 a a a a a a a a a a a a a a a a a a a

我相信你已经提出了这个解决方案,但不幸的是你所要求的并不是很直接。

对于那些感兴趣的人,这是我的第一次尝试:

using System;
using System.Text.RegularExpressions;

class Program {
  static void Main() {
    string input = "\r\n\r\n\n\r\r\r\n\nthisisatest\r\nstring\r\nwith\nsome" +
                   "\r\nunsanatized\r\nbreaks\r\n\r\n";
    input = (input ?? String.Empty).Trim().Replace("\r", String.Empty);
    string output = Regex.Replace(
                      input,
                      "\\\n+",
                      "<br />",
                      RegexOptions.Multiline);
    Console.WriteLine(output);
  }
}

产生以下输出:

thisisatest<br />string<br />with<br />some<br />unsanatized<br />breaks

答案 3 :(得分:0)

这不是处理此问题的最有效方式,也不是最聪明的(免责声明),
但是如果你的文字不是太大 那么它并不重要且缺少任何更智能的算法(请注意:尽管你很难检测char\nchar\nchar\n...之类的东西可以设置限制线len)

你可以Split使用白色字符(添加任何你能想到的,缺少\ n) - 然后Join只有一个空格然后拆分{{1} (获取行) - 加入\n。加入专线时,您可以测试<br />,例如或者其他的东西。

为了加快速度,您可以使用更高效的算法,char by char,使用IndexOf等进行迭代。

同样不是最有效或最完美的处理方法,但会给你一些快速的东西。

编辑:过滤“相同行” - 您可以使用例如line.Length > 2 - 来自DistinctUntilChanged(参见NuGet Ix-experimental I think),它应该过滤'相同行'连续+你可以为那些添加行测试。

答案 4 :(得分:0)

随机建议,受slashdot.org评论过滤器的启发:使用System.IO.Compression.DeflateStream压缩您的用户输入,如果它与原始文件相比太小(您将需要做一些实验)找到一个有用的截止点)拒绝它。