Skip to content

Commit 8160291

Browse files
committed
copy (and adjust) the path-injection QHelp from Java to C#
1 parent 9dfac3a commit 8160291

File tree

4 files changed

+82
-26
lines changed

4 files changed

+82
-26
lines changed

csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,54 @@
77
can result in sensitive information being revealed or deleted, or an attacker being able to influence
88
behavior by modifying unexpected files.</p>
99

10-
<p>Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
11-
such as "..". Such a path may potentially point to any directory on the file system.</p>
10+
<p>Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain
11+
unexpected special characters such as "..". Such a path could point anywhere on the file system.</p>
1212

1313
</overview>
1414
<recommendation>
1515

16-
<p>Validate user input before using it to construct a file path. Ideally, follow these rules:</p>
16+
<p>Validate user input before using it to construct a file path.</p>
1717

18-
<ul>
19-
<li>Do not allow more than a single "." character.</li>
20-
<li>Do not allow directory separators such as "/" or "\" (depending on the file system).</li>
21-
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to
22-
".../...//" the resulting string would still be "../".</li>
23-
<li>Use a whitelist of known good patterns.</li>
24-
<li>Sanitize potentially tainted paths using <code>HttpRequest.MapPath</code>.</li>
25-
</ul>
18+
<p>Common validation methods include checking that the normalized path is relative and does not contain
19+
any ".." components, or checking that the path is contained within a safe folder. The method you should use depends
20+
on how the path is used in the application, and whether the path should be a single path component.
21+
</p>
22+
23+
<p>If the path should be a single path component (such as a file name), you can check for the existence
24+
of any path separators ("/" or "\"), or ".." sequences in the input, and reject the input if any are found.
25+
</p>
26+
27+
<p>
28+
Note that removing "../" sequences is <i>not</i> sufficient, since the input could still contain a path separator
29+
followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences
30+
are removed.
31+
</p>
32+
33+
<p>Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that
34+
the user input matches one of these patterns.</p>
2635

2736
</recommendation>
2837
<example>
2938

30-
<p>In the first example, a file name is read from a <code>HttpRequest</code> and then used to access a file. However, a
31-
malicious user could enter a file name which is an absolute path - for example, "/etc/passwd". In the second example, it
32-
appears that the user is restricted to opening a file within the "user" home directory. However, a malicious user could
33-
enter a filename which contains special characters. For example, the string "../../etc/passwd" will result in the code
34-
reading the file located at "/home/[user]/../../etc/passwd", which is the system's password file. This file would then be
35-
sent back to the user, giving them access to all the system's passwords.</p>
39+
<p>In this example, a user-provided file name is read from a HTTP request and then used to access a file
40+
and send it back to the user. However, a malicious user could enter a file name anywhere on the file system,
41+
such as "/etc/passwd" or "../../../etc/passwd".</p>
3642

3743
<sample src="examples/TaintedPath.cs" />
3844

45+
<p>
46+
If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
47+
</p>
48+
49+
<sample src="examples/TaintedPathGoodNormalize.cs" />
50+
51+
<p>
52+
If the input should be within a specific directory, you can check that the resolved path
53+
is still contained within that directory.
54+
</p>
55+
56+
<sample src="examples/TaintedPathGoodFolder.cs" />
57+
3958
</example>
4059
<references>
4160

csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,8 @@ public class TaintedPathHandler : IHttpHandler
66
{
77
public void ProcessRequest(HttpContext ctx)
88
{
9-
String path = ctx.Request.QueryString["path"];
9+
String filename = ctx.Request.QueryString["path"];
1010
// BAD: This could read any file on the filesystem.
11-
ctx.Response.Write(File.ReadAllText(path));
12-
13-
// BAD: This could still read any file on the filesystem.
14-
ctx.Response.Write(File.ReadAllText("/home/user/" + path));
15-
16-
// GOOD: MapPath ensures the path is safe to read from.
17-
string safePath = ctx.Request.MapPath(path, ctx.Request.ApplicationPath, false);
18-
ctx.Response.Write(File.ReadAllText(safePath));
11+
ctx.Response.Write(File.ReadAllText(filename));
1912
}
2013
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.IO;
3+
using System.Web;
4+
5+
public class TaintedPathHandler : IHttpHandler
6+
{
7+
public void ProcessRequest(HttpContext ctx)
8+
{
9+
String filename = ctx.Request.QueryString["path"];
10+
11+
string publicFolder = Path.GetFullPath("/home/" + user + "/public");
12+
string filePath = Path.GetFullPath(Path.Combine(publicFolder, filename));
13+
14+
// GOOD: ensure that the path stays within the public folder
15+
if (!filePath.StartsWith(publicFolder + Path.DirectorySeparatorChar))
16+
{
17+
ctx.Response.StatusCode = 400;
18+
ctx.Response.StatusDescription = "Bad Request";
19+
ctx.Response.Write("Invalid path");
20+
return;
21+
}
22+
ctx.Response.Write(File.ReadAllText(filename));
23+
}
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.IO;
3+
using System.Web;
4+
5+
public class TaintedPathHandler : IHttpHandler
6+
{
7+
public void ProcessRequest(HttpContext ctx)
8+
{
9+
String filename = ctx.Request.QueryString["path"];
10+
// GOOD: ensure that the filename has no path separators or parent directory references
11+
if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
12+
{
13+
ctx.Response.StatusCode = 400;
14+
ctx.Response.StatusDescription = "Bad Request";
15+
ctx.Response.Write("Invalid path");
16+
return;
17+
}
18+
ctx.Response.Write(File.ReadAllText(filename));
19+
}
20+
}

0 commit comments

Comments
 (0)