在当今的网络世界中,Web应用的安全性至关重要。其中,跨站脚本攻击(XSS)是一种常见的攻击方式,它允许攻击者在用户的浏览器上执行恶意脚本。为了防范此类攻击,内容安全策略(Content Security Policy,简称CSP)提供了一种额外的安全层。CSP可以帮助开发者指定可信的源(域名),以限制可执行脚本的来源。通过使用CSP,可以有效地防止恶意脚本在用户浏览器上的执行。
CSP通过设置HTTP头中的Content-Security-Policy字段来实现。这个字段由一系列的指令和源组成,用分号分隔。指令定义了资源类型的限制,而源则指定了可信的域名。例如,可以设置script-src指令来限制JavaScript脚本的来源,只允许来自特定域名的脚本执行。
CSP的指令用于指定资源类型的限制,常见的指令包括:
CSP的源包括:
开发者可以根据需要选择合适的指令和源来构建CSP策略。
为了简化CSP策略的设置,可以创建一个可重用的中间件。以下是一个简单的示例,展示了如何创建CSP中间件:
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>();
}
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)};" : "";
}
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类中配置CSP中间件,从而实现CSP策略的设置。