Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4226467

Browse files
authored
Merge pull request #4678 from tamasvajk/feature/external-api-untrusted-data
C#: Add queries to check untrusted data flow to external APIs
2 parents 2ea9b4a + aa3ae0f commit 4226467

12 files changed

Lines changed: 313 additions & 0 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Text;
3+
using System.Web;
4+
5+
public class UntrustedData : IHttpHandler
6+
{
7+
public void ProcessRequest(HttpContext ctx)
8+
{
9+
var name = ctx.Request.QueryString["name"];
10+
ctx.Response.Write(name);
11+
}
12+
13+
public bool IsReusable => true;
14+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
7+
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
8+
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
9+
may be relevant for security analysis of this application.</p>
10+
11+
<p>An external API is defined as a call to a callable (method/property accessor/operator/...) that is not defined
12+
in the source code, not overridden in the source code, and is not modeled as a taint step in the default taint library.
13+
External APIs may be from the .NET standard library, third-party dependencies or from internal dependencies. The query
14+
will report the signature with a fully qualified name, along with either <code>[param x]</code>, where <code>x</code>
15+
indicates the position of the parameter receiving the untrusted data or <code>[qualifier]</code> indicating the untrusted
16+
data is used as the qualifier to the call.</p>
17+
18+
</overview>
19+
<recommendation>
20+
21+
<p>For each result:</p>
22+
23+
<ul>
24+
<li>If the result highlights a known sink, no action is required.</li>
25+
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
26+
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
27+
re-run the query to determine what new results have appeared due to this additional modeling.</li>
28+
</ul>
29+
30+
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPICallable</code>
31+
class to exclude known safe external APIs from future analysis.</p>
32+
33+
</recommendation>
34+
<example>
35+
36+
<p>If the query were to return the API <code>System.Web.HttpResponse.Write(string) [param 0]</code>
37+
then we should first consider whether this a security relevant sink. In this case, this is writing to a HTTP response, so we should
38+
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
39+
40+
</example>
41+
<references>
42+
43+
</references>
44+
</qhelp>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @name Frequency counts for external APIs that are used with untrusted data
3+
* @description This reports the external APIs that are used with untrusted data, along with how
4+
* frequently the API is called, and how many unique sources of untrusted data flow
5+
* to it.
6+
* @id csharp/count-untrusted-data-external-api
7+
* @kind table
8+
* @tags security external/cwe/cwe-20
9+
*/
10+
11+
import csharp
12+
import semmle.code.csharp.security.dataflow.ExternalAPIs
13+
14+
from ExternalAPIUsedWithUntrustedData externalAPI
15+
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
16+
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
17+
numberOfUntrustedSources desc
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
7+
external APIs that use untrusted data. The results are not filtered so that you can audit all examples. The query
8+
provides data for security reviews of the application and you can also use it to identify external APIs that should
9+
be modeled as either taint steps, or sinks for specific problems.</p>
10+
11+
<p>An external API is defined as a call to a callable (method/property accessor/operator/...) that is not defined
12+
in the source code, not overridden in the source code, and is not modeled as a taint step in the default taint library.
13+
External APIs may be from the .NET standard library, third-party dependencies or from internal dependencies. The query
14+
reports uses of untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
15+
16+
</overview>
17+
<recommendation>
18+
19+
<p>For each result:</p>
20+
21+
<ul>
22+
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
23+
that the result is a false positive because this data is sanitized.</li>
24+
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
25+
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
26+
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
27+
re-run the query to determine what new results have appeared due to this additional modeling.</li>
28+
</ul>
29+
30+
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPICallable</code>
31+
class to exclude known safe external APIs from future analysis.</p>
32+
33+
</recommendation>
34+
<example>
35+
36+
<p>A query string parameter is read from <code>HttpRequest</code> and then ultimately used in a call to the
37+
<code>HttpResponse.Write</code> external API:</p>
38+
39+
<sample src="ExternalAPISinkExample.cs" />
40+
41+
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
42+
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
43+
some existing sanitization.</p>
44+
45+
</example>
46+
<references>
47+
48+
</references>
49+
</qhelp>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Untrusted data passed to external API
3+
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
4+
* @id csharp/untrusted-data-to-external-api
5+
* @kind path-problem
6+
* @precision low
7+
* @problem.severity error
8+
* @tags security external/cwe/cwe-20
9+
*/
10+
11+
import csharp
12+
import semmle.code.csharp.dataflow.TaintTracking
13+
import semmle.code.csharp.security.dataflow.ExternalAPIs
14+
import DataFlow::PathGraph
15+
16+
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
17+
where config.hasFlowPath(source, sink)
18+
select sink, source, sink,
19+
"Call to " + sink.getNode().(ExternalAPIDataNode).getCallableDescription() +
20+
" with untrusted data from $@.", source, source.toString()

csharp/ql/src/semmle/code/csharp/frameworks/System.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,15 @@ class SystemStringClass extends StringType {
444444
result.getNumberOfParameters() = 1 and
445445
result.getReturnType() instanceof BoolType
446446
}
447+
448+
/** Gets the `IsNullOrWhiteSpace(string)` method. */
449+
Method getIsNullOrWhiteSpaceMethod() {
450+
result.getDeclaringType() = this and
451+
result.hasName("IsNullOrWhiteSpace") and
452+
result.isStatic() and
453+
result.getNumberOfParameters() = 1 and
454+
result.getReturnType() instanceof BoolType
455+
}
447456
}
448457

449458
/** A `ToString()` method. */
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Definitions for reasoning about untrusted data used in APIs defined outside the
3+
* database.
4+
*/
5+
6+
import csharp
7+
import semmle.code.csharp.dataflow.flowsources.Remote
8+
import semmle.code.csharp.dataflow.TaintTracking
9+
import semmle.code.csharp.frameworks.System
10+
import semmle.code.csharp.dataflow.FlowSummary
11+
12+
/**
13+
* A callable that is considered a "safe" external API from a security perspective.
14+
*/
15+
abstract class SafeExternalAPICallable extends Callable { }
16+
17+
private class SummarizedCallableSafe extends SafeExternalAPICallable {
18+
SummarizedCallableSafe() { this instanceof SummarizedCallable }
19+
}
20+
21+
/** The default set of "safe" external APIs. */
22+
private class DefaultSafeExternalAPICallable extends SafeExternalAPICallable {
23+
DefaultSafeExternalAPICallable() {
24+
this instanceof EqualsMethod or
25+
this instanceof IEquatableEqualsMethod or
26+
this = any(SystemObjectClass s).getEqualsMethod() or
27+
this = any(SystemObjectClass s).getReferenceEqualsMethod() or
28+
this = any(SystemObjectClass s).getStaticEqualsMethod() or
29+
this = any(SystemObjectClass s).getGetTypeMethod() or
30+
this = any(SystemStringClass s).getEqualsMethod() or
31+
this = any(SystemStringClass s).getEqualsOperator() or
32+
this = any(SystemStringClass s).getIsNullOrEmptyMethod() or
33+
this = any(SystemStringClass s).getIsNullOrWhiteSpaceMethod() or
34+
this.getName().regexpMatch("Count|get_Count|get_Length")
35+
}
36+
}
37+
38+
/** A node representing data being passed to an external API. */
39+
class ExternalAPIDataNode extends DataFlow::Node {
40+
Call call;
41+
int i;
42+
43+
ExternalAPIDataNode() {
44+
(
45+
// Argument to call
46+
this.asExpr() = call.getArgument(i)
47+
or
48+
// Qualifier to call
49+
this.asExpr() = call.getChild(i) and
50+
i = -1 and
51+
// extension methods are covered above
52+
not call.getTarget().(Method).isExtensionMethod()
53+
) and
54+
// Defined outside the source archive
55+
not call.getTarget().fromSource() and
56+
// Not a call to a method which is overridden in source
57+
not exists(Virtualizable m |
58+
m.overridesOrImplementsOrEquals(call.getTarget().getUnboundDeclaration()) and
59+
m.fromSource()
60+
) and
61+
// Not a call to a known safe external API
62+
not call.getTarget().getUnboundDeclaration() instanceof SafeExternalAPICallable
63+
}
64+
65+
/** Gets the called API callable. */
66+
Callable getCallable() { result = call.getTarget().getUnboundDeclaration() }
67+
68+
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
69+
int getIndex() { result = i }
70+
71+
/** Gets the description of the callable being called. */
72+
string getCallableDescription() { result = getCallable().getQualifiedName() }
73+
}
74+
75+
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
76+
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
77+
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
78+
79+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
80+
81+
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
82+
}
83+
84+
/** A node representing untrusted data being passed to an external API. */
85+
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
86+
private UntrustedDataToExternalAPIConfig c;
87+
88+
UntrustedExternalAPIDataNode() { c.hasFlow(_, this) }
89+
90+
/** Gets a source of untrusted data which is passed to this external API data node. */
91+
DataFlow::Node getAnUntrustedSource() { c.hasFlow(result, this) }
92+
}
93+
94+
private newtype TExternalAPI =
95+
TExternalAPIParameter(Callable m, int index) {
96+
exists(UntrustedExternalAPIDataNode n |
97+
m = n.getCallable().getUnboundDeclaration() and
98+
index = n.getIndex()
99+
)
100+
}
101+
102+
/** An external API which is used with untrusted data. */
103+
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
104+
/** Gets a possibly untrusted use of this external API. */
105+
UntrustedExternalAPIDataNode getUntrustedDataNode() {
106+
this = TExternalAPIParameter(result.getCallable().getUnboundDeclaration(), result.getIndex())
107+
}
108+
109+
/** Gets the number of untrusted sources used with this external API. */
110+
int getNumberOfUntrustedSources() {
111+
result = count(getUntrustedDataNode().getAnUntrustedSource())
112+
}
113+
114+
/** Gets a textual representation of this element. */
115+
string toString() {
116+
exists(Callable m, int index, string indexString |
117+
if index = -1 then indexString = "qualifier" else indexString = "param " + index
118+
|
119+
this = TExternalAPIParameter(m, index) and
120+
result =
121+
m.getDeclaringType().getQualifiedName() + "." + m.toStringWithTypes() + " [" + indexString +
122+
"]"
123+
)
124+
}
125+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| System.Collections.Specialized.NameValueCollection.get_Item(string) [qualifier] | 1 | 1 |
2+
| System.Web.HttpRequest.get_QueryString() [qualifier] | 1 | 1 |
3+
| System.Web.HttpResponse.Write(string) [param 0] | 1 | 1 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security Features/CWE-020/ExternalAPIsUsedWithUntrustedData.ql
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// semmle-extractor-options: /r:${testdir}/../../../resources/assemblies/System.Web.dll /r:${testdir}/../../../resources/assemblies/System.Web.ApplicationServices.dll /r:${testdir}/../../../resources/assemblies/System.Data.dll /r:System.Text.RegularExpressions.dll /r:System.Collections.Specialized.dll /r:System.Data.Common.dll /r:System.Security.Cryptography.X509Certificates.dll /r:System.Runtime.InteropServices.dll
2+
3+
using System;
4+
using System.Text;
5+
using System.Web;
6+
7+
public class UntrustedData : IHttpHandler
8+
{
9+
public void ProcessRequest(HttpContext ctx)
10+
{
11+
var name = ctx.Request.QueryString["name"];
12+
var len = name.Length;
13+
14+
var myEncodedString = HttpUtility.HtmlEncode(name);
15+
ctx.Response.Write(name);
16+
}
17+
18+
public bool IsReusable => true;
19+
}

0 commit comments

Comments
 (0)