Hi @stephans, and welcome to the community!
You’re right to question this, as your understanding of how GetTempFileName works internally is correct. It does atomically create a zero-byte file using FileMode.CreateNew in Path.Windows.cs#L252.
However, what the SonarQube rule wants to point out is not the creation of that initial file; it’s in the gap between that creation and when your application re-opens the file to use it. I think the problem here is our explanation of the security issue within the text. I am going to take a look at the implementation of this rule and on changing the text.
Here is an explanation of what the rule wanted to point out:
The (insecure) workflow that Path.GetTempFileName() encourages is a non-atomic, two-step operation:
-
Step 1 (Atomic): Path.GetTempFileName() is called. It securely creates an empty, zero-byte file (e.g., C:\Temp\tmpA1B2.tmp). You get the path back as a string.
-
Step 2 (The Gap): Your code then takes that string and opens the file again to write to it, for example, with new StreamWriter(tempPath).
The security risk the race condition exists in the tiny window of time between Step 1 and Step 2. This is called a TOCTOU:
TOCTOU (time-of-check to time-of-use) is a type of race condition where the state of a system or resource is checked, and then used, but the state changes between those two actions, allowing for an exploit. This vulnerability allows attackers to change a resource, such as a file or configuration, between the time a program (1) creates/validates it and (2) when it actually uses it.
The compliant solution avoids this two-step process entirely. It performs the file creation and opening in a single, atomic operation.
// This generates a NAME ONLY. No file is created yet.
var randomPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
// This is the key:
using (var fileStream = new FileStream(randomPath,
FileMode.CreateNew, // Fails if file exists
FileAccess.Write,
FileShare.None, // Lock the file
4096,
FileOptions.DeleteOnClose))
{
// You get a ready-to-use stream from the one and only file operation.
// There is NO gap for an attacker to exploit.
using (var writer = new StreamWriter(fileStream))
{
writer.WriteLine("content");
}
}
Thanks a lot for your feedback!
Loris