了解C# 中的集合(包括泛型和非泛型)

发布于:2024-06-30 ⋅ 阅读:(19) ⋅ 点赞:(0)

跨站点脚本 (XSS) 攻击是一种严重的安全威胁,恶意脚本会注入其他用户查看的网页中。本文演示了如何在 ASP.NET Core MVC 中构建一个简单的博客应用程序,同时使用内置安全功能和最佳实践来防止 XSS 攻击。

步骤 1.创建 ASP.NET Core MVC 项目

创建新的 ASP.NET Core MVC 项目

dotnet new mvc -n BlogApp
cd BlogApp

添加实体框架核心包

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.Security.Application

步骤 2. 设置 Entity Framework Core

创建数据库上下文和模型

创建数据文件夹并添加ApplicationDbContext.cs

using Microsoft.EntityFrameworkCore;
using XSSAttackInAspNetCoreMVC.Model;
namespace XSSAttackInAspNetCoreMVC.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
        public DbSet<BlogPost> BlogPosts { get; set; }
    }
}

在 Startup.cs 中配置数据库上下文

using Microsoft.EntityFrameworkCore;
using XSSAttackInAspNetCoreMVC.Data;

namespace XSSAttackInAspNetCoreMVC
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);
            // 创建Web应用程序构建器

            // 向容器中添加服务
            builder.Services.AddRazorPages(); 
            // 添加Razor页面服务
            builder.Services.AddControllersWithViews(); 
            // 添加控制器和视图服务
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
            // 添加数据库上下文服务,并配置使用SQL Server连接字符串

            var app = builder.Build();
            // 构建Web应用程序

            // 配置HTTP请求管道
            if (!app.Environment.IsDevelopment())
            {
                app.UseExceptionHandler("/Error");
                // 非开发环境使用异常处理程序,重定向到 /Error 页面
                // 默认HSTS值为30天。在生产环境中可能需要更改此值,参考https://aka.ms/aspnetcore-hsts
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            // 使用HTTPS重定向
            app.UseStaticFiles();
            // 使用静态文件
            app.UseRouting();
            // 启用路由功能
            app.UseAuthorization();
            // 启用授权功能

            app.MapRazorPages();
            // 映射Razor页面
            app.UseEndpoints(routes =>
            {
                routes.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                // 设置默认的控制器路由
            });

            app.Run();
            // 运行应用程序
        }
    }
}

在 appsettings.json 中添加连接字符串

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=BlogAppDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

创建初始迁移并更新数据库

dotnet ef migrations add InitialCreate
dotnet ef database update

步骤 3.创建控制器和视图

创建 BlogPost 模型

创建 Models 文件夹并添加 BlogPost.cs

using System.ComponentModel.DataAnnotations;

namespace XSSAttackInAspNetCoreMVC.Model
{
    public class BlogPost
    {
        // 博客文章的唯一标识符
        public int Id { get; set; }
        
        // 标题属性,必填并限制长度为100个字符
        [Required]
        [StringLength(100)]
        public string? Title { get; set; }
        
        // 内容属性,必填
        [Required]
        public string? Content { get; set; }
        
        // 创建日期时间
        public DateTime Created { get; set; }
    }
}

创建 BlogController

创建 Controllers 文件夹并添加 BlogController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Security.Application;
using XSSAttackInAspNetCoreMVC.Data;
using XSSAttackInAspNetCoreMVC.Model;

namespace XSSAttackInAspNetCoreMVC.Controllers
{
    public class BlogController : Controller
    {
        private readonly ApplicationDbContext _context;
        // 依赖注入的数据库上下文,用于访问数据库

        public BlogController(ApplicationDbContext context)
        {
            _context = context;
        }

        [HttpGet]
        public IActionResult Index()
        {
            var posts = _context.BlogPosts.ToList();
            // 从数据库中获取所有博客文章
            return View(posts);
            // 将博客文章列表传递给视图
        }

        [HttpGet]
        public IActionResult Create()
        {
            return View();
            // 返回创建博客文章的视图
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(BlogPost model)
        {
            if (ModelState.IsValid)
            {
                // 如果模型状态有效
                // 在保存到数据库之前对输入进行清理
                model.Title = Sanitizer.GetSafeHtmlFragment(model.Title);
                model.Content = Sanitizer.GetSafeHtmlFragment(model.Content);
                _context.BlogPosts.Add(model);
                // 将新的博客文章添加到数据库上下文
                _context.SaveChanges();
                // 保存更改到数据库
                return RedirectToAction(nameof(Index));
                // 重定向到博客文章列表页
            }
            return View(model);
            // 如果模型状态无效,返回创建视图并显示错误信息
        }
    }
}

创建视图

创建 Views/Blog 文件夹并添加以下视图

Index.cshtml

@model IEnumerable<XSSAttackInAspNetCoreMVC.Model.BlogPost>
<!DOCTYPE html>
<html>
<head>
    <title>Blog Posts</title>
</head>
<body>
    <h1>Blog Posts</h1>
    <a asp-controller="Blog" asp-action="Create">Create New Post</a>
    <ul>
        @foreach (var post in Model)
        {
            <li>
                <h2>@post.Title</h2>
                <p>@Html.Raw(@post.Content)</p>
                <p><small>@post.Created</small></p>
            </li>
        }
    </ul>
</body>
</html>

Create.cshtml

@model XSSAttackInAspNetCoreMVC.Model.BlogPost
<!DOCTYPE html>
<html>
<head>
    <title>Create Blog Post</title>
</head>
<body>
    <h1>Create Blog Post</h1>
    <form asp-action="Create" method="post" asp-antiforgery="true">
        <div class="form-group">
            <label asp-for="Title"></label>
            <input asp-for="Title" class="form-control" />
            <span asp-validation-for="Title" class="text-danger"></span>
        </div>
        <div class="form-group">
            <label asp-for="Content"></label>
            <textarea asp-for="Content" class="form-control"></textarea>
            <span asp-validation-for="Content" class="text-danger"></span>
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
    </form>
    <a asp-controller="Blog" asp-action="Index">Back to List</a>
</body>
</html>

步骤 4.添加客户端验证

启用客户端验证

将必要的脚本添加到_Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - XSSAttackInAspNetCoreMVC</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/XSSAttackInAspNetCoreMVC.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">XSSAttackInAspNetCoreMVC</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-action="Index" asp-controller="Blog">Blog</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2024 - XSSAttackInAspNetCoreMVC - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

步骤 5. 运行应用程序

dotnet run

输出

如果我们想在完成上述步骤后在表单中提交脚本,网站将清理输入,如果发现脚本,网站将不会接受数据。


提交这些数据后,我们会调试代码中的输入,我的网站就不会受到脚本攻击。

在输出中,您可以看到,经过清理后,我们没有收到与视图中的脚本相关的任何输出

结论

按照这些步骤,您已创建了一个简单的 ASP.NET Core MVC 应用程序,该应用程序允许用户创建和查看博客文章,同时防止 XSS 攻击。该应用程序在将用户输入保存到数据库之前对其进行清理,并使用内置编码功能安全地显示内容。这种方法可确保您的应用程序免受常见的 XSS 漏洞攻击。