Return to
HomePage
Creating a temporary file which avoids canonicalization and race conditions (VB.NET)
Applies To
* ASP.NET 2.0
* VB.NET
Summary
The purpose of this code sample is to demonstrate a technique to securely create files by a web application while avoiding canonicalization and race conditions
Objectives
* Secure generation of temporary files within a web application which does not expose other files on the file system.
* Secure creation and storage of temporary files which mitigate the risk of file enumeration
* Mitigate the likelihood of race conditions through use of strong randomly generated filenames
* Demonstrate secure retrieval of a temporary file which resides outside of the webroot upon successful authorization of user request
Scenarios
* A web-based application generates reports or other temporary files to a file which may be subsequently retrieved by an authorized user
* A web application needs to periodically create temporary files which are later processed by a batch process, the temporary filenames should be unique and non-user influenced
* Files are retrieved indirectly for users upon request, brokered by the application (e.g. files reside outside of the webroot to prevent unauthorized access).
Solution Example
The following solution is broken up into two cases, the first in which the file is created, and the second which demonstrates how to properly retrieve and serve the file back to the user.
Secure File Creation
Protected Sub [CreateFile()]
Try
' Generate a guid value for a strong, random and unpredictable filename
Dim guidstr As String = [Guid.NewGuid.ToString()]
' Ensure that file which is written resides in a directory outside of the web root.
' Ideally this file should be on a filesystem separate from the OS.
' None of the generated filename arguments are user influenced values
Dim sw As [StreamWriter] = New StreamWriter("E:\\account-reports\\" + guidstr + ".pdf")
' Fetch some data and generate the file
'
' ...
[sw.Write(strFileContents)]
sw.Flush()
sw.Close()
' Insert GUID value in database associated with current user
'
' ...
Catch ex As Exception
' User supplied exception handling code: Log exception and perform graceful error handling
End Try
End Sub
Secure File Retrieval
Protected Sub [RetrieveFile()]
Try
' Retrieve file. We call a custom method to perform a database lookup which gets the
' GUID value for the current user.
' Replace the [RetrieveGuidForUser] method below with your own method which returns the
' guid for the current user.
Dim userguid As String = [RetrieveGuidForUser(Context.User.Identity)]
Dim fullreportpath As String = "E:\\account-reports\\" + userguid + ".pdf"
' Retrieve file length
Dim fi As [FileInfo] = New [FileInfo(fullreportpath)]
Dim fsize As Long = fi.Length
' Serve file to user. No values in response headers should be user influenced.
' We read the file from a directory outside of the web root to protect unauthorized
' file access.
Response.AddHeader("Content-Disposition", "attachment;filename=report.pdf")
' Don't allow a user to influence the content disposition or content type otherwise
' there may be possibility of header injection
[Response.ContentType] = "application/pdf"
[Response.WriteFile(fullreportpath,] 0, fsize)
Catch ex As Exception
' User supplied exception handling code: Log exception and perform graceful error handling
End Try
End Sub
Problem Example
The following examples demonstrate the danger of relying on user supplied inputs to create and retrieve temporary files on the host:
File Creation
' None of the generated filename arguments are user influenced values
[StreamWriter] sw = New [StreamWriter(HttpContext.Current.Request.PhysicalApplicationPath] +
Dim +".pdf")("cid") As Request.Cookie
' Fetch some data and generate the file
'
' ...
[sw.Write(strFileContents)]
sw.Flush()
sw.Close()
* This insecure file creation routine creates temporary files in the webroot as a result any user who has access to the web server may retrieve PDF reports associated with another user
* Files created directly or indirectly due to an action performed by a user must be created securely in which the filenames are random, and not user influenced, and reside outside of the webserver root.
* The Request.Cookie
"cid" value is used to choose a filename to be written, because this value is user influenced an attacker may leverage directory traversal attacks to overwrite arbitrary files on the web server host.
* Using a GUID we can generate a strong random value for the filename instead of relying on a user supplied cookie.
File Retrieval Dim fid As String = Request.QueryString("fid").ToString()
Dim ftype As String = hdnFtype.Value
Dim fullreportpath As String = "E:\\account-reports\\" + fid + "." + ftype
' Retrieve file length
Dim fi As [FileInfo] = New [FileInfo(fullpath)]
Dim fsize As Long = fi.Length
' Serve file to user. No values in response headers should be user influenced.
' We read the file from a directory outside of the web root to protect unauthorized
' file access.
Response.AddHeader("Content-Disposition", "attachment;filename="+ fid + "."+ ftype)
[Response.ContentType] = "application/"+ftype
[Response.WriteFile(fullpath,] 0, fsize)
* Simply changing the value of the fid request querystring parameter allows us to retrieve files of other users. If the values are sequential it becomes trivial to enumerate files of other users.
* Users can circumvent this file retrieval routine as files are stored within the web server root
* The user supplied querystring parameter and ftype hidden form field allow users to retrieve arbitrary files from the file system by performing directory traversal attacks
* The user supplied querystring parameter and ftype hidden form field allow an attacker to create specially formed hyperlinks which introduce response splitting and other HTTP header injection issues (e.g. HTTP Cache poisoning).
* User supplied values should never be used to identify files for manipulation, rather server-side session details which do not allow user tampering should be used instead
* Hidden form fields do not mitigate the risk of parameter tampering; client side controls are easily circumvented
* The content disposition filename need not necessarily be the same filename that a user created.
Test Case
The following class must be included in any project making use of the sample code provided above:
using System.IO;
Running code similar to that shown below will demonstrate the uniqueness of System generated
GUIDs which coincides with our goal to create temporary files which avoid canonicalization and race conditions.
Dim i As Integer
For i = 0 To 10- 1 Step i + 1
Response.Output.WriteLine("GUID: " + [Guid.NewGuid())]
Next
Expected Result
GUID: 1a40e34d-9985-4b2e-9c6a-8be88cfcc26b
GUID: 1b7bd6d5-057e-4681-b213-a193170fc5d5
GUID: b063f09d-d393-4fbc-b704-e5cefd708703
GUID: 088101b3-dc2e-474b-a898-53508b4a9ac5
GUID: bffc841c-e6e8-4562-8ece-8d1ace19fd4d
GUID: 21ff1e43-0d10-46d7-9df6-a9053f3aabc9
GUID: 36f49e30-c81d-484a-94a0-cc09197839a0
GUID: a6d32483-bc6e-4828-b3cd-423ab1e1389e
GUID: d8ed1bf8-413e-4be9-97d8-ad9337030310
GUID: eba93b92-384d-41d2-8db3-6028932fee4d
More Information
The file retrieval function demonstrates the ability to securely serve files back to users avoiding common attacks such as HTTP header injection, directory traversal and race conditions as filenames are controlled entirely by the server. In the event that the application needs to preserve original filenames, these values may be stored in the database (after data validation for known good characters) along with the system generated Guid. The original filename may then be set within the Content-Disposition header.
Additional Resources
* Guid Structure: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemguidclasstopic.asp
* Wikipedia Directory Traversal Attacks: http://en.wikipedia.org/wiki/Directory_traversal
* Wikipedia Race Conditions: http://en.wikipedia.org/wiki/Race_condition
Attributes
*
Applies To: .NET Framework 2.0, VB
*
Category: I/O
*
Author: George Gal
Return to
HomePage