Skip to content

Commit 062f16e

Browse files
authored
Merge pull request github#15519 from erik-krogh/cs-path
C#: Improve the `cs/path-injection` QHelp
2 parents de13ff6 + 4e17623 commit 062f16e

File tree

8 files changed

+97
-40
lines changed

8 files changed

+97
-40
lines changed

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

Lines changed: 0 additions & 20 deletions
This file was deleted.

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

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,53 @@
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>
42+
43+
<sample src="examples/TaintedPath.cs" />
44+
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>
3655

37-
<sample src="TaintedPath.cs" />
56+
<sample src="examples/TaintedPathGoodFolder.cs" />
3857

3958
</example>
4059
<references>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ the result is within the destination directory. If provided with a zip file cont
5050
path like <code>..\sneaky-file</code>, then this file would be written outside the destination
5151
directory.</p>
5252

53-
<sample src="ZipSlipBad.cs" />
53+
<sample src="examples/ZipSlipBad.cs" />
5454

5555
<p>To fix this vulnerability, we need to make three changes. Firstly, we need to resolve any
5656
directory traversal or other special characters in the path by using <code>Path.GetFullPath</code>.
@@ -59,7 +59,7 @@ Secondly, we need to identify the destination output directory, again using
5959
the resolved output starts with the resolved destination directory, and throw an exception if this
6060
is not the case.</p>
6161

62-
<sample src="ZipSlipGood.cs" />
62+
<sample src="examples/ZipSlipGood.cs" />
6363

6464
</example>
6565
<references>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
// BAD: This could read any file on the filesystem.
11+
ctx.Response.Write(File.ReadAllText(filename));
12+
}
13+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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 user = ctx.User.Identity.Name;
12+
string publicFolder = Path.GetFullPath("/home/" + user + "/public");
13+
string filePath = Path.GetFullPath(Path.Combine(publicFolder, filename));
14+
15+
// GOOD: ensure that the path stays within the public folder
16+
if (!filePath.StartsWith(publicFolder + Path.DirectorySeparatorChar))
17+
{
18+
ctx.Response.StatusCode = 400;
19+
ctx.Response.StatusDescription = "Bad Request";
20+
ctx.Response.Write("Invalid path");
21+
return;
22+
}
23+
ctx.Response.Write(File.ReadAllText(filename));
24+
}
25+
}
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)