首页 > 解决方案 > 解决 XSS 问题

问题描述

我正在尝试构建一个 MVC 应用程序,但被告知这不是检索数据的好方法,并且容易受到跨站点脚本的影响。我从来没有做过安全工作,也一直在努力学习,但我无法绕开它。

我猜这里有几个缺陷。我可以使用任何特定的编码吗?

我没有在这里粘贴整个代码,但试图找出可以阻止 XSS 攻击的地方。

模型和视图模型

namespace ThePeopleSearchApplication.Models
{
    public class UserViewModel
    {
        public string UID{ get; set; }
        public string FName{ get; set; }
        public string LName{ get; set; }
        public string Email { get; set; }
        public string Status{ get; set; }

    }

    public class UserModel
    {
        public string UID{ get; set; }
        public string FName{ get; set; }
        public string LName{ get; set; }
        public string Email { get; set; }
        public string Status{ get; set; }
    }

}

控制器

    namespace ThePeopleSearchApplication.Controllers
{
    public class MyController : Controller
    {
        // GET: My
        public ActionResult Index()
        {
            return View();
        }

        [ValidateInput(false)] //has been added to understand XSS better
        public ActionResult SearchUserAjax(string userId)
        {
            UserModel myUser = fetchUserFromLdap(userId);
            return Content("{\"message\": \"search for userId: " +
                           userId + " result\", \"result\": " + convertToJson(myUser) + " }");
        }

        private string convertToJson(UserModel myUser)
        {
            return "{ \"userId\": \"" + myUser.UserId + "\", \"FirstName\": \"" +
                   myUser.FirstName + "\", \"LastName\": \"" + myUser.LastName + "\", \"Email\": \"" +
                   myUser.Email + "\", \"Status\": \"" + myUser.Status + "\"}";
        }

        [ValidateInput(false)] //has been added to understand XSS better
        public ActionResult SearchUser(string userId)
        {
            UserModel myUser = fetchUserFromLdap(userId);
            var viewModel = new UserViewModel
            {
                UID = userId,
                FName = myUser.FirstName,
                LName = myUser.LastName,
                Email = myUser.Email,
                Status = myUser.Status,
            };
            return this.View(viewModel);
        }

        private UserModel fetchUserFromLdap(string userId)
        {
            var retVal = new UserModel();
            if (String.IsNullOrEmpty(userId))
            {
                retVal.UID = "N/A";
                retVal.FName = "N/A";
                retVal.LName = "N/A";
                retVal.Email = "N/A";
                retVal.Status = "N/A";
            }
            else
            {
                retVal.UID = userId;
                retVal.FName = "FirstName";
                retVal.LName = "LastName";
                retVal.Email = "email@example.com";
                retVal.Status = "<div style=background-color:#F00800>My Status</div>";
            }

            return retVal;

        }
    }
}

看法

    @model ThePeopleSearchApplication.Models.UserViewModel
@{
    ViewBag.Title = "Search result for user: " + Model.UserId;
    var ulId = "ul-id" + Model.UserId;
    var formId = "form" + Model.UserId;
}
<html>
<head>
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
</head>
<body>
<h1>Search result for user: @Model.UserId</h1>
<ul id="@Html.Raw(ulId)">
    <li>@Model.FirstName</li>
    <li>@Model.LastName</li>
    <li>@Model.Email</li>
    <li>@Html.Raw(Model.Status)</li>
</ul>
<form id=@formId name=@formId action=/My/SearchUser enctype="multipart/form-data">
    <input type="text" name="userId" />
    <input type="submit" />
</form>
<script type="text/javascript">
    var theForm = document.@formId;
    $(theForm).submit(function() {
        alert('Valid form');
        return true;
    });
// just to demonstrate potential usage $(theForm).submit();
</script>
<div>
    Ajax search:
    <form id="ajax-search" name="ajax-search">
        <input type="text" name="userId" />
        <input type="submit" />
    </form>
    <script>
        $("#ajax-search").submit(function() {
            var url = "/My/SearchUserAjax"; // the script where you handle the form input.
            $.ajax({
                type: "POST",
                url: url,
                data: $("#ajax-search").serialize(), // serializes the form's elements.
                success: function(data)
                {
                    var obj = JSON.parse(data);
                    $('#ajax-search').append('<hr/>');
                    $('#ajax-search').append(obj.message); // show response from the php script.
                    $('#ajax-search').append('<hr/>');
                    $('#ajax-search').append(obj.result.userId);
                    $('#ajax-search').append('<hr/>');
                    $('#ajax-search').append(obj.result.FirstName);
                    $('#ajax-search').append('<hr/>');
                    $('#ajax-search').append(obj.result.LastName);
                    $('#ajax-search').append('<hr/>');
                    $('#ajax-search').append(obj.result.Status);
                    $('#ajax-search').append('<hr/>');
                }
            });
            return false; // avoid to execute the actual submit of the form.
        });
    </script>
</div>
</body>
</html>

标签: securityasp.net-mvc-4xsssql-injection

解决方案


主要问题是如果用户以不安全的方式控制您呈现给页面的某些数据。无论是来自他们的名字(我的名字是<script>function() { nasty stuff is happening here... }</script>)还是任何其他内容。

我采用以下方法,查看您的输出(或更好地考虑一下),看看每个阶段是否存在问题:

  1. IHtmlString让 Razor 来做,默认情况下,Razor 处理页面上所有 HTML 字符的编码,如果你使用 an所以避免这个Type(或返回它的方法,如 Html.Raw())则不适用, 所以将@("<script>nastyThings()</script>")string编码,脚本不会运行
  2. 如果它坏了,这意味着你的字符串中有一些你真正想要渲染的 HTML/JS。因此,请尝试将其直接移动到 Razor 模板 (HTML/JS) 或通过链接 (JS) 获取
    • 而不是整个string由用户"<element onclick="javascript:alert('trouble')"></element>"使用模板控制@Html.Raw(Model.UserBadString)
    • 制作模板<element onclick="mySafeJsFunction()">@Model.UserSafeString</element>“,这将 JS 函数的控制权从用户手中夺走,并留给他们无法进行 XSS 的 Razor 编码参数
  3. 您希望用户可以控制 HTML,然后您必须使用类似 ( https://github.com/mgans/HtmlSanitizer ) 来清理输出到页面的字符串
    • 所以模板可能是@Html.Raw(sanitizer.Sanitize(Model.UserBadString)),但您可能想做一些比这更好的事情,良好的编码实践等。重点是string经过消毒

顺便说一句,确保您密切关注属性的用法,例如在 JS(或调用.innerHTMLjQuery 的可怕的 jQuery )中,就好像它们包含用户控制的内容一样,您将遇到完全相同的问题。但是可以应用相同的步骤,(1) 使用,或 (3) 在将字符串提供给 JS ( https://github.com/cure53/DOMPurify ) 之前,在字符串上使用净化库,如 DOMPurify。不幸的是,在这种情况下不推荐使用选项 (2),因为剩下的任何东西都必须由你或我确保安全,我宁愿相信 DOMPurify 会这样做:).html()eval().innerText


推荐阅读