استفاده از CSP Header در ASP.NET Core 2.0

دوشنبه 28 خرداد 1397

جلوگیری از حملات به برنامه بسیار مهم است و باید برای آن راهکاری را تدبیر کرد. در این مقاله ما قصد داریم تا نحوه استفاده از CSP Header (Content-Security-Policy) را در ASP.NET Core برای جلوگیری از حملات XSS بررسی کنیم.

استفاده از CSP Header در ASP.NET Core 2.0

یک پروژه خالی ایجاد کرده و Startup آن را با قرار دادن سرویس و میان‌افزار برای MVC آپدیت کنید.

public void ConfigureServices(  
        IServiceCollection services)  
    {  
        services.AddMvc();  
    }  
  
    public void Configure(  
        IApplicationBuilder app,   
        IHostingEnvironment env)  
    {  
        app.UseDeveloperExceptionPage();  
  
        app.Use(async (context, next) =>  
        {  
            context.Response.Headers.Add(  
                "Content-Security-Policy",  
                "script-src 'self'; " +  
                "style-src 'self'; " +  
                "img-src 'self'");  
  
            await next();  
        });  
  
        app.UseStaticFiles();  
        app.UseMvcWithDefaultRoute();  
    }  
}  

یک صفحه Layout_ اضافه کرده و اسکریپت‌ها، استایل‌ها و تصاویر را از دامنه محلی (wwwroot) و دامنه راه دور در آن قرار دهید.

    <!DOCTYPE html>  
       
    <html>  
    <head>  
        <meta name="viewport" content="width=device-width" />  
        <title>@ViewBag.Title</title>  
       
        <link rel="stylesheet" href="~/css/site.css" />  
        <link rel="stylesheet"   
              href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/css/bootstrap.min.css" />  
    </head>  
    <body>  
        <img src="~/img/site-banner.jpg" />  
        <img src="https://media-www-asp.azureedge.net/media/5245130/home-hero-2.png" />  
       
        <div>  
            @RenderBody()  
        </div>  
       
        <script src="~/js/site.js"></script>  
        <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>  
    </body>  
    </html>  

آن را اجرا کرده و خطاها را مشاهده کنید. هنگام بارگذاری صفحه، مرورگر بارگیری بخش‌هایی که از منابع دور است را رد می‌کند.

CSP یک سطح امنیتی اضافی است که می‌تواند به جلوگیری از حملات تزریق کد یا XSS کمک کند. در این حملات اسکریپت‌های مخرب بر روی مرورگر کاربر اجرا می‌شوند، زیرا مرورگر نمی‌داند که آیا منبع اسکریپت قابل اعتماد است یا نه.

CSP به توسعه‌دهندگان اجازه می‌دهد تا منابعی (دامنه‌ها) که قابل اعتماد هستند را مشخص کنند و بتوانند اسکریپت‌های قابل اجرا را ارائه دهند. این لیست سفید دامنه‌ها با استفاده از Content-Security-Type HTTP header انجام می‌شود، مثل:

Content-Security-Policy: [policy]

در اینجا [policy] از دستورالعمل‌هایی ساخته شده است که نوع محدودیت‌ها و دامنه‌ها را برای لیست سفید توصیف می‌کند. رشته‌ای است که مجموعه‌ای از دستورالعمل‌ها و منابعی که توسط سمیکالون جدا شده‌اند را تعریف می‌کند.

]دستورالعمل[ >منبع< >منبع...;< ] دستورالعمل [ >منبع< >منبع...;< ...

[directive] <source> <…source>; [directive] <source> <…source>; …

منابع

منابع ، دامنه‌های مورد اعتماد هستند؛ آن‌هایی که معمولا استفاده می‌شوند عبارتند از:

*: هر URLای مجاز است.

self’: مبدأ صفحه‌ای که سرویس‌رسانی می‌کند مجاز است. توجه کنید که تک کوتیشن‌ها الزامی است.

none’: هیچ منبعی مجاز نیست. توجه کنید که تک کوتیشن‌ها الزامی است.

Host: هاست مشخص شده مجاز است (توسط نام یا آدرس IP آن). می‌توان از یک علامت ستاره استفاده کرد تا همه زیردامنه‌ها را شامل شود، مثل http://*.foo.com

unsafe-line’: اسکریپت‌های درون صفحه‌ای مجاز هستند.

nonce-[base64-value]’: اسکریپت‌های درون صفحه‌ای با یک nonce خاص مجاز هستند (تعداد استفاده یک بار). nonce باید برای هر درخواست/پاسخ HTTP منحصربه‌فرد و رمزگذاری شده باشد.

نکته: منابع دیگر را می‌توانید در مستندات موزیلا برای توسعه‌دهندگان بیابید.

دستورالعمل‌ها

دستورالعمل‌ها انواع منابع را برای گذاشتن محدودیت روی آن‌ها مشخص می‌کنند. آن‌هایی که معمولا استفاده می‌شوند عبارتند از:

script-src: منابع معتبر جاوااسکریپت را تعریف می‌کند.

style-src: منابع معتبر استایل‌ها را تعریف می‌کند.

img-src: منابع معتبر تصاویر را تعریف می‌کند.

connect-src: منابع معتبری را که فراخوانی‌های AJAX می‌تواند آن‌ها را بسازد را تعریف می‌کند.

font-src: منابع معتبر فونت‌ها را تعریف می‌کند.

object-src: منابع معتبر برای عناصر <applet>، <object> و <embed> را تعریف می‌کند.

media-src: منابع معتبر برای صوت و تصویر را تعریف می‌کند.

form-action: منابع معتبری را که می‌تواند به عنوان اکشن HTML <form> استفاده شود را تعریف می‌کند.

default-src: سیاست‌های پیش‌فرض برای بارگذاری محتوا را مشخص می‌کند.

نکته: دستورالعمل‌های دیگر را می‌توانید در مستندات موزیلا برای توسعه‌دهندگان بیابید.

میان‌افزار (Middleware)

ما می‌توانیم ساختار را کپسوله‌سازی کنیم و CSP header را در میان‌افزارهایی با قابلیت استفاده مجدد اضافه کنیم. در ادامه نمونه‌ای برای شروع کار ایجاد کرده‌ایم. می‌توانید در صورت لزوم آن را گسترش دهید. ابتدا، یک کلاس برای نگهداری منابع ایجاد کنید.

    public sealed class CspOptions  
      {  
          public List<string> Defaults { get; set; } = new List<string>();  
          public List<string> Scripts { get; set; } = new List<string>();  
          public List<string> Styles { get; set; } = new List<string>();  
          public List<string> Images { get; set; } = new List<string>();  
          public List<string> Fonts { get; set; } = new List<string>();  
          public List<string> Media { get; set; } = new List<string>();  
      }  

یک سازنده (builder) بسازید. از آن هنگام تنظیم میان‌افزار استفاده می‌کنیم.

    public sealed class CspOptionsBuilder  
     {  
         private readonly CspOptions options = new CspOptions();  
           
         internal CspOptionsBuilder() { }  
      
         public CspDirectiveBuilder Defaults { get; set; } = new CspDirectiveBuilder();  
         public CspDirectiveBuilder Scripts { get; set; } = new CspDirectiveBuilder();  
         public CspDirectiveBuilder Styles { get; set; } = new CspDirectiveBuilder();  
         public CspDirectiveBuilder Images { get; set; } = new CspDirectiveBuilder();  
         public CspDirectiveBuilder Fonts { get; set; } = new CspDirectiveBuilder();  
         public CspDirectiveBuilder Media { get; set; } = new CspDirectiveBuilder();  
      
         internal CspOptions Build()  
         {  
             this.options.Defaults = this.Defaults.Sources;  
             this.options.Scripts = this.Scripts.Sources;  
             this.options.Styles = this.Styles.Sources;  
             this.options.Images = this.Images.Sources;  
             this.options.Fonts = this.Fonts.Sources;  
             this.options.Media = this.Media.Sources;  
             return this.options;  
         }  
     }  
      
     public sealed class CspDirectiveBuilder  
     {  
         internal CspDirectiveBuilder() { }  
      
         internal List<string> Sources { get; set; } = new List<string>();  
      
         public CspDirectiveBuilder AllowSelf() => Allow("'self'");  
         public CspDirectiveBuilder AllowNone() => Allow("none");  
         public CspDirectiveBuilder AllowAny() => Allow("*");  
      
         public CspDirectiveBuilder Allow(string source)  
         {  
             this.Sources.Add(source);  
             return this;  
         }  
     }  

حالا می‌توانیم یک میان‌افزار ایجاد کنیم.

    public sealed class CspMiddleware  
     {  
         private const string HEADER = "Content-Security-Policy";  
         private readonly RequestDelegate next;  
         private readonly CspOptions options;  
      
         public CspMiddleware(  
             RequestDelegate next, CspOptions options)  
         {  
             this.next = next;  
             this.options = options;  
         }  
      
         public async Task Invoke(HttpContext context)  
         {  
             context.Response.Headers.Add(HEADER, GetHeaderValue());  
             await this.next(context);  
         }  
      
         private string GetHeaderValue()  
         {  
             var value = "";  
             value += GetDirective("default-src", this.options.Defaults);  
             value += GetDirective("script-src", this.options.Scripts);  
             value += GetDirective("style-src", this.options.Styles);  
             value += GetDirective("img-src", this.options.Images);  
             value += GetDirective("font-src", this.options.Fonts);  
             value += GetDirective("media-src", this.options.Media);  
             return value;  
         }  
      
         private string GetDirective(string directive, List<string> sources)  
             => sources.Count > 0 ? $"{directive} {string.Join(" ", sources)}; " : "";  
     }  

و یک extension method را برای آن تنظیم کنیم.

    public static class CspMiddlewareExtensions  
      {  
          public static IApplicationBuilder UseCsp(  
              this IApplicationBuilder app, Action<CspOptionsBuilder> builder)  
          {  
              var newBuilder = new CspOptionsBuilder();  
              builder(newBuilder);  
      
              var options = newBuilder.Build();  
              return app.UseMiddleware<CspMiddleware>(options);  
          }  
      }  

حالا می‌توانیم میان‌افزار را در کلاس Startup تنظیم کنیم.

    app.UseCsp(builder =>  
      {  
          builder.Defaults  
                 .AllowSelf();  
      
          builder.Scripts  
                 .AllowSelf()  
                 .Allow("https://ajax.aspnetcdn.com");  
      
          builder.Styles  
                 .AllowSelf()  
                 .Allow("https://ajax.aspnetcdn.com");  
      
          builder.Fonts  
                 .AllowSelf()  
                 .Allow("https://ajax.aspnetcdn.com");  
      
          builder.Images  
                 .AllowSelf()  
                 .Allow("https://media-www-asp.azureedge.net/");  
      });  

آن را اجرا کرده و ترافیک شبکه را مشاهده کنید. مرورگر به منابع محلی و از راه دور اجازه دسترسی می‌دهد.

سورس کد را می‌توانید از اینجا دانلود کنید.

ایمان مدائنی

نویسنده 1299 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید