搜索文本框的XSS脚本

时间:2014-11-13 07:28:21

标签: javascript c# asp.net xss server-side

我有一个文本框,用于搜索站点内的数据。我的客户想要什么,

1)在搜索字段中输入任何文本,然后单击搜索符号。

2)请求使用Web代理工具进入服务器,例如" Burp"

3)附加参数,脚本为

test<~script>confirm(123)<~/script>

这里发生的是

advesary输入的XSS脚本会在没有任何输入的情况下反映在响应中。请看下面的图片,你会得到一个想法: -

![在此处输入图片说明] [1]

伙计们,如果您需要更多相关信息,请与我们联系。请帮助伙计们,任何帮助将不胜感激。我想从服务器端停止攻击。

HTML和JS代码: -

  <asp:TextBox ID="txtSearch" runat="server" class="txtfld-search" oncopy="return false" oncut="return false" onpaste="return false"></asp:TextBox>

JS代码: -

<script type="text/javascript">
$(document).ready(function () {
    $('#ctl00_topNavigation_txtSearch').keyup(function () {
        var $th = $(this);
        $th.val($th.val().replace(/[^.%a-zA-Z0-9 ]/g,
        function (str) {
            alert('Special characters not allowed except %');
            return '';
        }));
    });
});

另见背后的代码: -

protected void btnSearch_Click(object sender, ImageClickEventArgs e)
{
    Response.Redirect("search.aspx?tx=" + txtSearch.Text);
}

另外,请参阅搜索部分的代码: -

private void SearchResult()
{
    DataTable dt;

    if (Session["Search"] == null)
    {
        ResXResourceReader reader = new ResXResourceReader(Server.MapPath("~/App_GlobalResources/Strings.en-US.resx"));
        IDictionaryEnumerator id = reader.GetEnumerator();
        string sResourceFile = Server.MapPath("~/App_GlobalResources/Strings.en-US.resx");
        XmlDocument xmlResource = new XmlDocument();
        xmlResource.Load(sResourceFile);

        XmlNodeList elmData = xmlResource.SelectNodes("//root/data");

        dt = new DataTable();
        dt.Columns.Add(new DataColumn("ID", System.Type.GetType("System.String")));
        dt.Columns.Add(new DataColumn("Title", System.Type.GetType("System.String")));
        dt.Columns.Add(new DataColumn("Description", System.Type.GetType("System.String")));
        dt.Columns.Add(new DataColumn("Url", System.Type.GetType("System.String")));
        dt.Columns.Add(new DataColumn("Link", System.Type.GetType("System.String")));

        foreach (XmlElement element in elmData)
        {
            DataRow dr = dt.NewRow();
            dr["ID"] = element.GetAttribute("name");
            //dr["Title"] = element.GetAttribute("name");
            XmlNodeList sDescription = element.SelectNodes("value");
            dr["Title"] = sDescription.Count > 0 ? sDescription.Item(0).InnerText : string.Empty; ;
            dr["Description"] = string.Empty;
            XmlNodeList sUrl = element.SelectNodes("comment");
            if (sUrl.Count > 0)
            {
                Int32 sPgTitle = sUrl.Item(0).InnerText.LastIndexOf(".") + 1;
                if (sPgTitle > 0)
                {
                    dr["Url"] = sUrl.Item(0).InnerText;
                    //dr["Url"] = Request.Url.Host.ToLower() + "/rbank/" + sUrl.Item(0).InnerText;
                    dr["Link"] = string.Empty;
                }
                else
                {
                    dr["Link"] = sUrl.Item(0).InnerText;
                }
                dt.Rows.Add(dr);
            }
        }
        //foreach (DataRow dr in dt.Rows)
        //{
        //    DataRow[] rDesc = dt.Select("Link <> ''");
        //    for (int i = 0; i < rDesc.Length; i++)
        //    {
        //        DataRow[] rTitle = dt.Select("ID = '" + rDesc[i]["Link"] + "'");
        //        if (rTitle.Count() > 0)
        //        {
        //            rTitle[0]["Description"] = rDesc[i]["Title"];
        //        }
        //    }
        //}

        DataRow[] drDelete = dt.Select("Link <> ''");
        foreach (DataRow drCheck in drDelete)
        {
            dt.Rows.Remove(drCheck);
        }
        dt.TableName = "FilterValues";
        reader.Close();
        Session["Search"] = dt;
    }
    else
    {
        dt = Session["Search"] as DataTable;
    }
    DataView dv = new DataView();
    dv.Table = dt;
    **dv.RowFilter = "Description LIKE ('%" + Request.QueryString["tx"].Trim().ToLower() + "%') or Title LIKE ('%" + Request.QueryString["tx"].Trim().ToLower() + "%')";**
    dv.Sort = "Title ASC";

    dgrdPages.DataSource = dv;
    dgrdPages.DataBind();

    lblSearchWords.Text = Request.QueryString["tx"].Trim();
    lblFilesFound.Text = dv.Count.ToString();
}

我发现 dv.RowFilter 可以像某些SQL注入一样给出。我想防止这种情况。请帮忙。

5 个答案:

答案 0 :(得分:3)

正如其他朋友所说,客户端代码很容易被忽略。因此,我们可以将您使用javascript完成的工作转换为c#,添加了我的更多空间并将它们合并为一个:

if (Regex.IsMatch(txtSearch.Text, "[^a-zA-Z0-9 %]"))
            {
                //error
                Response.Redirect("Error.aspx?tx=It's a Shame Dude!");
            }
            else
            {
                //Remove multiple spaces
                String ClearSpaces = Regex.Replace(txtSearch.Text, @"\s+", " ");
                Response.Redirect("search?tx=" + HttpUtility.UrlEncode(ClearSpaces));
            }

不要忘记,正则表达式来自:this answer。替换多个空格的正则表达式来自this answer

答案 1 :(得分:1)

您的代码存在一些问题:缺少输入验证,缺少输出编码,行过滤器注入以及缺乏对客户端\ Javascript验证概念的理解。

让我们逐一讨论:

  1. 缺少输入验证:我上面提到的所有攻击和问题都可以使用正确的输入验证来修复。根据我的意思,我指的是 white-list ,而不是黑名单。你永远不应该搜索撇号或LT \ GT之类的特殊字符,总是像你在Javascript中那样查看正面的白名单方式,并查找允许的值而不是非允许的值。这样做是因为攻击者总是可以超越程序员并以不同的方式编码他的漏洞。 正如其他人所提到的,ValidateRequest确实执行某种级别的输入验证,但它永远不会被视为单一解决方案,因为有办法绕过它。 请不要像其他人建议的那样尝试搜索<script等不同的漏洞攻击,这可以轻松绕过(例如:<img onmouseover=alert(1)>而不是您的有效负载),这是一种不好的做法。
  2. 缺少输出编码:您的代码实际上容易受到攻击的原因是:

    lblSearchWords.Text = Request.QueryString["tx"].Trim();
    

    在将标签集成到标签之前,您需要HTMLEncode来自用户的任何值。例如:

    lblSearchWords.Text = HttpUtility.HtmlEncode(Request.QueryString["tx"].ToString());
    

    这可确保将任何与HTML相关的字符编码为其不可执行的值。

  3. 行过滤器注入:此行实际上容易受到行过滤器注入,而不是SQL注入:

    dv.RowFilter = "Description LIKE ('%" + Request.QueryString["tx"].Trim().ToLower() + "%') or Title LIKE ('%" + Request.QueryString["tx"].Trim().ToLower() + "%')";
    

    您可能知道,语法是基于SQL的,但在这种情况下没有DB,只是容易受到注入攻击的行过滤机制。此注入是一个低严重性问题,因为它不允许攻击者做很多事情(不像在实际SQL注入中执行命令),但如果您的最终用户不能看到所有行在表中,这是一个安全问题。 如我所知,没有参数化查询,例如在SQL注入中防止注入的机制,但是如第1节和下一节所述,强制执行彻底的输入验证,也可以解决这个问题。

    < / LI>
  4. 缺乏对客户端概念的理解\ Javascript验证:任何类型的客户端验证都很有用,无法信任。无论是谁向你展示了这种技术,都说明了原因。 任何客户端验证都可以通过任何代理实用程序(Burp,Fiddler,Paros等)轻松绕过始终,甚至可以使用浏览器调试器或插件(按F12激活在调试器中,Firefox的Firebug就是这种增强默认调试器的插件的一个例子。 只相信cliet-side验证就像给你的房子钥匙给陌生人要求他检查是否有人怀疑是否试图闯入,或者将你的钥匙隐藏在欢迎垫下。你不能相信这是你唯一的防守。客户端验证的唯一实际目的是防止往返服务器并使用户的体验更好。您应该在服务器端代码中实现在JS中创建的相同的基于白名单的验证。永远记住:服务器端验证 - 强制性,客户端验证 - 很高兴。

  5. 希望有所帮助。

答案 2 :(得分:0)

与用户提供的任何值一样,您需要在页面上显示该值时转义该值。

更新search.aspx以在发出作为tx参数传递的值的任何位置使用HttpUtility.HtmlEncode(tx)

OWASP提供了一些有关如何防范此类XSS漏洞的良好指导。 https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

答案 3 :(得分:0)

如果您热衷于保护您的网站,那么最好的办法是在页面上启用请求验证:

<@ Page validateRequest="true" %>

如果要在您的网站上启用,请点击链接:

http://msdn.microsoft.com/en-us/library/hh882339(v=vs.110).aspx

如果您想继续使用更便捷的支票,那么在网页上显示之前对数据进行编码就可以了。

您也可以从http://www.microsoft.com/en-us/download/details.aspx?id=28589下载微软AntiXss库。这样,您就可以根据需要保护自己的网站。

希望这有帮助。

答案 4 :(得分:-1)

在我看来,你有两种选择。第一种是在将此文本输入发送到服务器之前,在jQuery代码中解析危险搜索客户端。我不确定你到底要找什么,但我想你想阻止用户在你的搜索栏中发送特定内容。

防止这种情况的好方法是在搜索中搜索特定内容。例如,您可以将函数包装在if语句中将搜索发送到服务器(对于伪代码抱歉):

if(txtinput.indexOf('<script>') === -1 && ...more checks...){
   // send input to server
}

这些if语句可以链接,因此您可以在实际将其发送到服务器之前检查是否存在任何XSS问题。也就是说,从系统设计的角度来看,这种方法并不是很吸引人。就像你说的,更好的方法是做这个服务器端。我对.NET后端并不是特别熟悉,但根据你所包含的代码,我想你可以解析你的txtSearch.text服务器端。这看起来与上面的答案类似。基本上,如果文本输入符合特定条件,您只会重定向到该链接。

不幸的是,我不是非常熟悉.NET中的字符串解析和验证,所以我不能给你代码,但它应该看起来像这样,假设你已经写了和isValid函数。

if(isValid(txtSearch.txt)){
   Response.Redirect("search.aspx?tx=" + txtSearch.Text);
} else {
   // Send Error code in response
}

我希望这有点帮助!