Skip to content

Commit 853765d

Browse files
authored
Merge pull request #90 from GitHubSecurityLab/cors-csharp
Add CORS query to C# pack
2 parents 2756b60 + ae4b59f commit 853765d

13 files changed

+419
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
9+
A server can send the
10+
<code>"Access-Control-Allow-Credentials"</code> CORS header to control
11+
when a browser may send user credentials in Cross-Origin HTTP
12+
requests.
13+
14+
</p>
15+
<p>
16+
17+
When the <code>Access-Control-Allow-Credentials</code> header
18+
is <code>"true"</code>, the <code>Access-Control-Allow-Origin</code>
19+
header must have a value different from <code>"*"</code> in order to
20+
make browsers accept the header. Therefore, to allow multiple origins
21+
for Cross-Origin requests with credentials, the server must
22+
dynamically compute the value of the
23+
<code>"Access-Control-Allow-Origin"</code> header. Computing this
24+
header value from information in the request to the server can
25+
therefore potentially allow an attacker to control the origins that
26+
the browser sends credentials to.
27+
28+
</p>
29+
30+
31+
32+
</overview>
33+
34+
<recommendation>
35+
<p>
36+
37+
When the <code>Access-Control-Allow-Credentials</code> header
38+
value is <code>"true"</code>, a dynamic computation of the
39+
<code>Access-Control-Allow-Origin</code> header must involve
40+
sanitization if it relies on user-controlled input.
41+
42+
43+
</p>
44+
<p>
45+
46+
Since the <code>"null"</code> origin is easy to obtain for an
47+
attacker, it is never safe to use <code>"null"</code> as the value of
48+
the <code>Access-Control-Allow-Origin</code> header when the
49+
<code>Access-Control-Allow-Credentials</code> header value is
50+
<code>"true"</code>.
51+
52+
</p>
53+
</recommendation>
54+
55+
<example>
56+
<p>
57+
58+
In the example below, the server allows the browser to send
59+
user credentials in a Cross-Origin request. The request header
60+
<code>origins</code> controls the allowed origins for such a
61+
Cross-Origin request.
62+
63+
</p>
64+
65+
<sample src="examples/CorsBad.cs"/>
66+
67+
<p>
68+
69+
This is not secure, since an attacker can choose the value of
70+
the <code>origin</code> request header to make the browser send
71+
credentials to their own server. The use of a allowlist containing
72+
allowed origins for the Cross-Origin request fixes the issue:
73+
74+
</p>
75+
76+
<sample src="examples/CorsGood.cs"/>
77+
</example>
78+
79+
<references>
80+
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin">CORS, Access-Control-Allow-Origin</a>.</li>
81+
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials">CORS, Access-Control-Allow-Credentials</a>.</li>
82+
<li>PortSwigger: <a href="http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html">Exploiting CORS Misconfigurations for Bitcoins and Bounties</a></li>
83+
<li>W3C: <a href="https://w3c.github.io/webappsec-cors-for-developers/#resources">CORS for developers, Advice for Resource Owners</a></li>
84+
</references>
85+
</qhelp>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @name Credentialed CORS Misconfiguration
3+
* @description Allowing any origin while allowing credentials may result in security issues as third party website may be able to
4+
* access private resources.
5+
* @kind problem
6+
* @problem.severity error
7+
* @security-severity 7.5
8+
* @precision high
9+
* @id cs/web/cors-misconfiguration
10+
* @tags security
11+
* external/cwe/cwe-942
12+
*/
13+
14+
import csharp
15+
import CorsMisconfigurationLib
16+
17+
/**
18+
* Holds if the application allows an origin using "*" origin.
19+
*/
20+
private predicate allowAnyOrigin(MethodCall m) {
21+
m.getTarget()
22+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
23+
"AllowAnyOrigin")
24+
}
25+
26+
/**
27+
* Holds if the application uses a vulnerable CORS policy.
28+
*/
29+
private predicate hasDangerousOrigins(MethodCall m) {
30+
m.getTarget()
31+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
32+
"WithOrigins") and
33+
exists(StringLiteral idStr |
34+
idStr.getValue().toLowerCase().matches(["null", "*"]) and
35+
TaintTracking::localExprTaint(idStr, m.getAnArgument())
36+
)
37+
}
38+
39+
from MethodCall add_policy, MethodCall child
40+
where
41+
(
42+
usedPolicy(add_policy) and
43+
// Misconfigured origin affects used policy
44+
getCallableFromExpr(add_policy.getArgument(1)).calls*(child.getTarget())
45+
or
46+
add_policy
47+
.getTarget()
48+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions",
49+
"AddDefaultPolicy") and
50+
// Misconfigured origin affects added default policy
51+
getCallableFromExpr(add_policy.getArgument(0)).calls*(child.getTarget())
52+
) and
53+
(hasDangerousOrigins(child) or allowAnyOrigin(child))
54+
select add_policy, "The following CORS policy may allow requests from 3rd party websites"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* @name Credentialed CORS Misconfiguration
3+
* @description Allowing any origin while allowing credentials may result in security issues as third party website may be able to
4+
* access private resources.
5+
* @kind problem
6+
* @problem.severity error
7+
* @security-severity 7.5
8+
* @precision high
9+
* @id cs/web/cors-misconfiguration-credentials
10+
* @tags security
11+
* external/cwe/cwe-942
12+
*/
13+
14+
import csharp
15+
import CorsMisconfigurationLib
16+
17+
/** A call to `CorsPolicyBuilder.AllowCredentials`. */
18+
class AllowsCredentials extends MethodCall {
19+
AllowsCredentials() {
20+
this.getTarget()
21+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
22+
"AllowCredentials")
23+
}
24+
}
25+
26+
from MethodCall add_policy, MethodCall setIsOriginAllowed, AllowsCredentials allowsCredentials
27+
where
28+
(
29+
getCallableFromExpr(add_policy.getArgument(1)).calls*(setIsOriginAllowed.getTarget()) and
30+
usedPolicy(add_policy) and
31+
getCallableFromExpr(add_policy.getArgument(1)).calls*(allowsCredentials.getTarget())
32+
or
33+
add_policy
34+
.getTarget()
35+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions",
36+
"AddDefaultPolicy") and
37+
getCallableFromExpr(add_policy.getArgument(0)).calls*(setIsOriginAllowed.getTarget()) and
38+
getCallableFromExpr(add_policy.getArgument(0)).calls*(allowsCredentials.getTarget())
39+
) and
40+
setIsOriginAllowedReturnsTrue(setIsOriginAllowed)
41+
select add_policy,
42+
"The following CORS policy may allow credentialed requests from 3rd party websites"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import csharp
2+
import DataFlow
3+
import security.JsonWebTokenHandler.JsonWebTokenHandlerLib
4+
5+
/**
6+
* Gets the actual callable corresponding to the expression `e`.
7+
*/
8+
Callable getCallableFromExpr(Expr e) {
9+
exists(Expr dcArg | dcArg = e.(DelegateCreation).getArgument() |
10+
result = dcArg.(CallableAccess).getTarget() or
11+
result = dcArg.(AnonymousFunctionExpr)
12+
)
13+
or
14+
result = e
15+
}
16+
17+
/**
18+
* Holds if SetIsOriginAllowed always returns true. This sets the Access-Control-Allow-Origin to the requester
19+
*/
20+
predicate setIsOriginAllowedReturnsTrue(MethodCall mc) {
21+
mc.getTarget()
22+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
23+
"SetIsOriginAllowed") and
24+
mc.getArgument(0) instanceof CallableAlwaysReturnsTrue
25+
}
26+
27+
/**
28+
* Holds if UseCors is called with the relevant cors policy
29+
*/
30+
predicate usedPolicy(MethodCall add_policy) {
31+
exists(MethodCall uc |
32+
uc.getTarget()
33+
.hasFullyQualifiedName("Microsoft.AspNetCore.Builder.CorsMiddlewareExtensions", "UseCors") and
34+
(
35+
// Same hardcoded name
36+
uc.getArgument(1).getValue() = add_policy.getArgument(0).getValue() or
37+
// Same variable access
38+
uc.getArgument(1).(VariableAccess).getTarget() =
39+
add_policy.getArgument(0).(VariableAccess).getTarget() or
40+
DataFlow::localExprFlow(add_policy.getArgument(0), uc.getArgument(1))
41+
)
42+
) and
43+
add_policy
44+
.getTarget()
45+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions", "AddPolicy")
46+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Leaf.Middlewares;
2+
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Hosting;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Hosting;
7+
8+
namespace Leaf
9+
{
10+
public class Startup
11+
{
12+
public Startup(IConfiguration configuration)
13+
{
14+
Configuration = configuration;
15+
}
16+
17+
public IConfiguration Configuration { get; }
18+
19+
// This method gets called by the runtime. Use this method to add services to the container.
20+
public void ConfigureServices(IServiceCollection services)
21+
{
22+
services.AddControllers();
23+
//services.AddTransient<MySqlConnection>(_ => new MySqlConnection(Configuration["ConnectionStrings:Default"]));
24+
services.AddControllersWithViews()
25+
.AddNewtonsoftJson(options =>
26+
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
27+
);
28+
29+
services.AddCors(options => {
30+
options.AddPolicy("AllowPolicy", builder => builder
31+
.WithOrigins("null")
32+
.AllowCredentials()
33+
.AllowAnyMethod()
34+
.AllowAnyHeader());
35+
});
36+
37+
}
38+
39+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
40+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
41+
{
42+
app.UseRouting();
43+
44+
app.UseCors("AllowPolicy");
45+
46+
app.UseRequestResponseLogging();
47+
48+
if (env.IsDevelopment())
49+
{
50+
app.UseDeveloperExceptionPage();
51+
}
52+
53+
app.UseHttpsRedirection();
54+
55+
app.UseAuthorization();
56+
57+
app.UseEndpoints(endpoints =>
58+
{
59+
endpoints.MapControllers();
60+
});
61+
62+
}
63+
}
64+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Leaf.Middlewares;
2+
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Hosting;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Hosting;
7+
8+
namespace Leaf
9+
{
10+
public class Startup
11+
{
12+
public Startup(IConfiguration configuration)
13+
{
14+
Configuration = configuration;
15+
}
16+
17+
public IConfiguration Configuration { get; }
18+
19+
// This method gets called by the runtime. Use this method to add services to the container.
20+
public void ConfigureServices(IServiceCollection services)
21+
{
22+
services.AddControllers();
23+
//services.AddTransient<MySqlConnection>(_ => new MySqlConnection(Configuration["ConnectionStrings:Default"]));
24+
services.AddControllersWithViews()
25+
.AddNewtonsoftJson(options =>
26+
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
27+
);
28+
29+
services.AddCors(options => {
30+
options.AddPolicy("AllowPolicy", builder => builder
31+
.WithOrigins("http://example.com")
32+
.AllowCredentials()
33+
.AllowAnyMethod()
34+
.AllowAnyHeader());
35+
});
36+
37+
}
38+
39+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
40+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
41+
{
42+
app.UseRouting();
43+
44+
app.UseCors("AllowPolicy");
45+
46+
app.UseRequestResponseLogging();
47+
48+
if (env.IsDevelopment())
49+
{
50+
app.UseDeveloperExceptionPage();
51+
}
52+
53+
app.UseHttpsRedirection();
54+
55+
app.UseAuthorization();
56+
57+
app.UseEndpoints(endpoints =>
58+
{
59+
endpoints.MapControllers();
60+
});
61+
62+
}
63+
}
64+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
public class Startup {
7+
public void ConfigureServices(string[] args) {
8+
var builder = WebApplication.CreateBuilder(args);
9+
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
10+
11+
builder.Services.AddCors(options => {
12+
options.AddPolicy(MyAllowSpecificOrigins,
13+
policy => {
14+
policy.SetIsOriginAllowed(test => true).AllowCredentials().AllowAnyHeader().AllowAnyMethod();
15+
});
16+
});
17+
18+
var app = builder.Build();
19+
20+
app.MapGet("/", () => "Hello World!");
21+
app.UseCors(MyAllowSpecificOrigins);
22+
23+
app.Run();
24+
}
25+
}

0 commit comments

Comments
 (0)