-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for content security policy #6001
Comments
We'll need a design for this guys. |
cc @shirhatti |
I created a policy based Content Security Policy library to use on my own sites while waiting for this support in the core libraries. Since content security policy support is now planned for 3.0, it might help you with the design discussion. I also followed your standards on the off chance you could use any of the code for this issue. CodeMicrosoft.AspNetCore.Csp Samplespublic void ConfigureServices(IServiceCollection services)
{
services.AddCsp(options =>
{
options.AddPolicy("Policy1", policy => policy
.AddDefaultSrc(src =>
{
src.AllowSchema(CspDirectiveSchemas.Http);
src.AllowSchema(CspDirectiveSchemas.Https);
src.AllowSelf();
src.AllowEval();
src.AllowHash("sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=");
})
.AddScriptSrc(src => src.AddNonce())
.AddStyleSrc(src => src.AddNonce())
.ReportUri("/csp-reports")
.ReportTo("csp-reports")
.AddManifestSrc(src =>
{
src.AllowHost("http://*.example.com");
})
.ReportOnly()
);
options.AddPolicy("Policy2", policy =>
policy.AddDefaultSrc(src => src.AllowNone().AddNonce())
);
options.AddPolicy("BetaUsers", policy =>
policy.AddDefaultSrc(src => src.AllowHost("beta-testers.example.org"))
);
});
services.AddMvc()
.AddCspReportMediaType();
services.AddMvcCore().AddCsp();
services.AddScoped<IConfigureOptions<CspOptions>, TrialUserSrc>();
}
public class TrialUserSrc : IConfigureOptions<CspOptions>
{
public void Configure (CspOptions options)
{
if (context.User.HasClaim(c => c.Type == ClaimTypes.TrialUser))
{
var currentPolicy = options.GetPolicy("Policy1");
currentPolicy.Append(policy =>
policy.AddDefaultSrc(src => src.AllowHost("trial.company.com"))
);
}
}
} Configuration with MVC Attributes
[EnableCsp("Policy1", "Policy2")]
[AppendCsp("BetaUsers", Targets = "Policy1, Policy2")]
[OverrideCsp("BetaUsers")]
public IActionResult EnablePolicy1And2()
{
...
}
[DisableCsp]
public IActionResult Disabled()
{
...
} Default Content-Security-PolicyThis is the default policy I'm using: public class CspOptions
{
// Default Content-Security-Policy:
// default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:;
// object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
private static readonly ContentSecurityPolicy DefaultContentSecurityPolicy Tag HelpersCspMetaTagHelper - Adds the content security policies as meta tags to the page. <meta http-equiv="Content-Security-Policy" /> CspNonceTagHelper - Adds a nonce to script or style tags and automatically ensures a nonce is returned in the HTTP header. CspGenerateHashTagHelper - Automatically generates hashes for inline scripts and styles and ensures the hashes are in the HTTP header. <style asp-add-nonce="true" asp-generate-hash="true">
p {
color: #0000ff;
}
</style>
<script asp-add-nonce="true" asp-generate-hash="true" asp-generate-hash-algorithms="HashAlgorithms.SHA256 | HashAlgorithms.SHA512">
console.log("I'm a script that will be hashed dynamically.");
</script> CspPluginTypeTagHelper - Automatically adds the used plugin types to the content security policy. <embed asp-type="application/testplugintype2" /> CspFallBackTagHelper - Generates CSP hashes for the inline scripts that are generated when using asp-fallback-href or asp-fallback-src. CspSubresourceIntegrityTagHelper - Automatically generates SRI hashes for the remote scripts using a local fallback script. <link href="https://ajax.aspnetcdn.com/ajax/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"
asp-fallback-href="~/lib/bootstrap/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
asp-subresource-integrity="true"
asp-subresource-integrity-algorithms="HashAlgorithms.SHA256 | HashAlgorithms.SHA384 | HashAlgorithms.SHA512" /> CSP Report Support
/// <summary>
/// Adds the 'application/csp-report' media type to the JSON input formatter so that csp reports can be received.
/// </summary>
/// <param name="builder">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IMvcBuilder AddCspReportMediaType(this IMvcBuilder builder) cc @sebastienros - When this feature lands in 3.0 I'll implement CSP for Orchard Core 🎉 |
@jrestall Your approach is pretty solid. It's pretty much what I had in mind. I have some ideas on how to better integrate this with Razor, but the foundation is solid. The way I see this, there will be a core library (similar to what we have for CORS) and then some MVC specific glue/enhancements/opinions. In particular, I think it might be better to specify the policy as a Razor directive as its very resource specific. In many actions you don't have the ability to know what's going to be rendered. By doing it as a razor directive it can be something that you put on a _viewStart, _viewImports (not really sure which one of them yet) or simply at the start of your page or view. That way the policy lives close to the resources, regarding hashes and things like that I would have to think more about this. One thing that I'm concern is the size of the policy on the header growing to a significant size, so I'm more inclined to use/recommend a different approach where you hoist the inline-scripts into js files for most cases. I think that you are on point with the need to append to an initial policy and to completely override it in some cases. Having a default policy is also great, but the default for the system should be to simply report so that you can start changing the code base to enforce the policy. |
@mkArtakMSFT @javiercn Is this still happening in 3.0? |
Please don't forget about the validation summary (inline |
Interesting that no one has mentioned NWebSec. This library started life adding much needed security enhancements to ASP.NET MVC, and now support ASP.NET Core as well. There's already a pretty complete CSP implementation here, so at the very least, it might be worth referencing. |
I'm looking forward to this being part of the aspnet core stack as well. I've experimented a bit with NWebSec but it seems it has not been updated in over a year, issues go unanswered and even their SSL cert for their website expired in Feb 2019 and they have not done anything about it. Seems like an abandoned project to me. https://www.nwebsec.com/ |
NWebSec is solid, but as @joeaudette said it's under-maintained right now. I would love to see something like @jrestall's approach built in. NWebSec puts everything into |
Hello .NET community! This PR adds Content Security Policy support for ASP.NET as middleware. CSP is a popular security mitigation against XSS and other injection vulnerabilities. CSP comes in many flavours, but we've chosen to add support for the most robust of them: nonce-based, strict-dynamic CSP. Summary of the changes (Less than 80 chars) * Allow configuration of whether CSP enabled in reporting or enforcement modes. * Allows configuration of a report URI, for violation reports sent by the browser. * CSP middleware generates a nonce-based, strict-dynamic policy. * Middleware adds thepolicy to HTTP responses according to the configuration. * Custom <script> TagHelper to set nonce attribute on script blocks automatically. * Provides a default implementation of a CSP violation report collection endpoint. * Example app that uses our CSP middleware and corresponding basic unit tests. * With these tools, developers can enable CSP in reporting mode, collect reports and identify and refactor existing code that is incompatible with CSP from these reports. Finally, developers will be able to switch CSP to enforcing mode, which will provide a very robust defense against XSS. Addresses dotnet/aspnetcore#6001 Co-authored with: Aaron Shim - [email protected]
Is this issue included: validation summary emitting style="display:none".
|
@javiercn , I do not see a real benefit of having a csp as a directive. Rather, I think it is better to make the templates more Csp compliant for those who already are implementing Csp (with or without a library) and who may find themselves in using unsafe keywords in the Csp directive set: One example is the style in the default generator as mentioned above
Another one is the injection of javascript during development, see @damienbod here #34428 Regarding the library, from my own experience with Csp at least, I see most benefit in having enable/disable attributes, filters and a global Csp/ReportOnly. For Spa's, I don't know yet, so probably meta tags with hashes (?). |
Narrator: They did forget about it. |
I created a library for CSP because we needed it in production apps and existing libraries including NWebsec did not fully fulfill our conditions. It is based on a pragmatic and performance-oriented design philosophy which I believe is worth sharing after a brief exchange with @damienbod here #34428. This lead us to introduce the concept of a CSP Policy Group. This class encompasses the So I go along by copy pasting some text, and you can get extensive details here Get StartedAll terminology is explained after. The library does not use app.UseContentSecurityPolicy(); It can be placed before You configure CSP via appsettings.json or via an When using appsetting.json: using Galebra.Security.Headers.Csp;
builder.Services.AddContentSecurityPolicy(builder.Configuration.GetSection("Csp")); There are two services registered, a In the following, three policy groups are registered: "Csp": {
"IsDisabled": false,//default: Will apply the default policy everywhere until overriden by attributes or filters
"PolicyGroups": {
"PolicyGroup1": {
"Csp": {
"Fixed": "default-src 'none' 'sha256-RFWPLDbv2BY+rCkDzsE+0fr8ylGr2R2faWMhq4lfEQc=';script-src 'self'"
},
"IsDefault": false,
"NumberOfNonceBytes": 16//default
},
"PolicyGroup2": {
"Csp": {
"Fixed": "default-src 'self';base-uri 'self';form-action 'self';object-src;frame-ancestors;connect-src ws://localhost:65412",
"Nonceable": [
"style-src 'self'"
]
},
"CspReportOnly": {
"Fixed": "default-src;form-action 'self';base-uri;object-src;frame-ancestors;sandbox",
"Nonceable": [
"style-src",
"script-src"
]
},
"IsDefault": true,//default
"NumberOfNonceBytes": 8
},
"PolicyGroup3": {
"Csp": {
"Nonceable": [
"style-src"
]
},
"IsDefault": false,
"NumberOfNonceBytes": 3
}
}
}, The first policy group does not require nonces (hence fixed, see below) and only requires the Alternatively, you can configure everything in code: builder.Services.AddContentSecurityPolicy(c =>
{
c.IsDisabled = false;
c.Add("Policy1", g =>
{
g.Csp.Fixed = "default-src 'self';connect-src ws://localhost:65412";
g.Csp.Nonceable.Add("style-src 'self'");
g.CspReportOnly.Nonceable.Add("script-src");
g.IsDefault = true;
g.NumberOfNonceBytes = 32;
});
c.Add("Policy2", g =>
{
g.Csp.Fixed = "default-src 'self';connect-src ws://localhost:65412";
g.CspReportOnly.Fixed="default-src";
g.IsDefault = false;
});
}); Add nonces to the body by importing the TagHelpers @addTagHelper *, Galebra.Security.Headers.Csp And add to styles, scripts or link tags the TagHelper: nonce-add=true For example, for <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" nonce-add=true/> As you may agree, this logical structure actually is powerful. Design Philosophy and introductionThe following design principles, detailed below, have been followed:
Consider the following arguments. CSP is a set of two headers, whose names can be:
Each policy in those headers is defined by an ordered set of directives.
which is equivalent to the shorter version
In theory, you can have multiple CSP policies for the same header name So, with this library, you cannot have the following:
The CspPolicyGroup classHowever, you can have both a CSP and a CSP-Report-Only policy, and usually this is recommended.
In this scenario, the website would run without issues if, loosely speaking, all styles and scripts are loaded from the server Allowing for both CSP and CSP-Report-Only headers to coexist introduces the The CspPolicy class and Nonce TagHelperThe usual route to library design is to use the so-called fluent-api. This gives elegant code, This led us to the following observation. A CSP header value is always divided into two groups. For example, the policy:
would be set as a "Fixed": "default-src;style-src 'self'", A If, now, you want to have this policy, but in addition produce a nonce for styles, then you would need to split your string "Fixed": "default-src",
"Nonceable": [
"style-src 'self'"
] The To use the nonce on the style in this example, you would invoke the Tag Helper <style nonce-add=true>
.myclass{
background:lightgreen;
}
</style>
<h4 class="myclass">I am lightgreen, thank you nonce!</h4> When you use a "NumberOfNonceBytes": 8 Multiple Policies, Attributes, Filters and default CspPolicyGroupWhen you implement CSP on a website, often you need several When you use the library, unless you override the default described below, the default You can apply a given policy to an entire Folder in Razor Pages, e.g. in the Movies folder: //Apply on specific folders
builder.Services.AddRazorPages(options =>
{
options.Conventions.AddFolderApplicationModelConvention(
"/Movies",
model => model.Filters.Add(new EnableCspPageFilter { PolicyGroupName = "PolicyGroup1" }));
}); You can also disable CSP in a folder:
Where In areas, you could do something like this: options.Conventions.AddAreaFolderApplicationModelConvention("Identity", "/Account",
model => model.Filters.Add(new EnableCspPageFilter { PolicyGroupName = "PolicyGroup1" }));
options.Conventions.AddAreaPageApplicationModelConvention("Identity", "/Account/Manage/ChangePassword",
model => model.Filters.Add(new EnableCspPageFilter { PolicyGroupName = "PolicyGroup3" })); In a Razor Page or Action or Controller, you can override the default [EnableCsp(PolicyGroupName="PolicyGroup1")] [DisableCsp] The builder.Services.AddRazorPages(options =>
{
options.Conventions.AddFolderApplicationModelConvention(
"/Movies",
model => model.Filters.Add(new DisableCspPageFilter { EnforceMode = false }));
}); Because we have set [DisableCsp(EnforceMode = false)]
public class BooksController : Controller
{
public ActionResult Index()
{
//CSP is disabled here
return View();
}
// GET: BooksController/Details/5
[EnableCsp(PolicyGroupName ="PolicyGroup3")]
public ActionResult Details(int id)
{
//CSP works here owing to EnforceMode=false
return View();
}
} IsDisabled global booleanBy default, the library applies the default |
This would be really nice to be built in. Two other libraries support this as well https://github.com/andrewlock/NetEscapades.AspNetCore.SecurityHeaders and https://github.com/juunas11/aspnetcore-security-headers |
This is a placeholder issue.
Similar to the support that we have for CORS in APIs, we should have support for Content Security Policy to make sites safer by default.
Support for CSP would be policy based, similar to the one we offer for CORS.
Usage from middleware
Usage from MVC
We will provide a default policy that limits content to your domain, defines best practices for HTTPS and will be set to report-only. This behavior can be switched per endpoint so that you can progressively enforce the policy one endpoint at a time.
References
https://en.wikipedia.org/wiki/Content_Security_Policy
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
https://www.w3.org/TR/CSP2/
http://caniuse.com/#search=content%20security%20policy
The text was updated successfully, but these errors were encountered: