Subject: How do I validate file upload?
Posted By: grstad Post Date: 2/17/2007 3:19:12 PM
Hei!

How do I validate file upload? I want clients to only upload .gif or .jpeg.

Mvh
grstad
Reply By: Dj Kat Reply Date: 2/17/2007 6:44:09 PM
Hi,

Why don't u do this serverside? You can always disable javascript so its not the most secure method.



__________________________________________________________
I am DJ Kat...that's my name. Its a D and a J and a Kat with a K.
Reply By: Greg Griffiths Reply Date: 2/17/2007 9:16:53 PM
it does seem to be possible (http://www.cs.tut.fi/~jkorpela/forms/file.html#filter) gives some guidance on this, but I would also validate it on the server side just to be sure as I guess browser support for this will be limited and if you use the JS approach is can always be turned off.
Reply By: mat41 Reply Date: 2/18/2007 7:44:38 PM
I mean really; how many of you people reading this post have javascript completley disabled?  If so, is your web site also free of JS?  This forum we all spend so much time on uses it along with most of the computing world, why is this?  This is why we hire security experts and pay senior network guys so much money, they put things in place to stop JS reated intrusions.  

For anybody who would like a client side solution, this one works a treat:

    function validate(formName,fieldName)
    {
       if ((/.(gif|jpe?g)$/i.test(document.[formName].[fieldName].value))==false)
       {
           alert('You may only upload .jpg, .jpeg, or .gif images (in case sensitive)');
           return (false);
       }
           return(true);
    }

Wind is your friend
Matt
Reply By: Imar Reply Date: 2/19/2007 1:31:25 PM
Hi Matt,

I don't think this is about the user's experience and whether they have JavaScript enabled or not. I agree that most people have that, so you should be comfortable in using it.

However, this is much more about security. I'd be a little nervous if people could just upload any file. As a malicious user, it's very easy to bypass JavaScript validation and upload other kind of files.

Consider this ASP file:

<%
  dim fs
  Set fs=Server.CreateObject("Scripting.FileSystemObject")
  fs.DeleteFile("c:\SomeImportantFile.txt")
%>

Next, I upload this to a folder called Uploads that only checks the extension with JavaScript. I disable script, and upload the file as Test.asp.

Now, guess what happens when I request:

www.yourdomain.com/Uploads/Test.asp

Gone is your precious file SomeImportantFile.txt

This is just a simple example but I have seen entire script libraries that do crazy stuff, like:

1. Use FTP.exe to FTP files away
2. Move import system files under the webroot so they can be downloaded
3. Delete important files so you get error info that may lead to other information.

You can do anything that ASP allows you to do under the current credentials.

Point is: don't trust user input. It's nice to use client validation as a courtesy to users so they get immediate feedback ("sorry this file extension is not allowed", even before they upload it), but ALWAYS check stuff at the server as well. CONSIDER ALL USER INPUT AS EVIL (and you know I usually don't shout in this forum).

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: mat41 Reply Date: 2/19/2007 8:15:31 PM
Hello there Imar - point understood and taken.  A usual your input is brilliance...

Wind is your friend
Matt
Reply By: Imar Reply Date: 2/20/2007 12:40:56 PM
;-)   Thank you.... and you're welcome....

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 2/20/2007 3:59:00 PM
...is it possible to set restrictions (gif/jpeg or what ever) on directory level? I do guess the answer is no, but then it is all this odd questions of mine!

What are all the pro sites do to handle file upload ol? Is it an easy task when programming, not scripting?

Mvh
grstad
Reply By: Imar Reply Date: 2/20/2007 4:46:46 PM
You can limit the access rights of the application / virtual directory in IIS to read only.

That way, you can avoid files from being executed. But obviously, you should still validate the files to some extend when they are uploaded.

Cheers,

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 2/21/2007 8:20:03 AM
...but Imar, regarding your post 02/19/2007 1:31:25, how can you decide which folder (uploads) to upload the bad files into?

"Next, I upload this to a folder called Uploads that only checks the extension with JavaScript. I disable script, and upload the file as Test.asp."

Is it that easy to get access to any folder on disks across the net? How can you find any foldernames on the current server?

Mvh
grstad

Let me know if my questions are irrelevant...
Reply By: dparsons Reply Date: 2/21/2007 8:33:11 AM
That statement is an example.

What he is saying is that, if a user comes to your site with the intent on executing malicious scripts and all you are doing to is checking the file exetension of the file with JavaScript to ensure it is ok to upload, this user could disable javascript in their browser and now could upload an ASP file to your upload directory.

If that code contained this script:

<%
  dim fs
  Set fs=Server.CreateObject("Scripting.FileSystemObject")
  fs.DeleteFile("c:\SomeImportantFile.txt")
%>

It would potentially delete a file off of your C drive because all the user would have to do is navigate to the directory you upload files to, and execte the ASP file that he just uploaded and you are none the wiser that an asp script was uploaded.  (Until you go looking for the important file that just got deleted off of your c drive.)

===========================================================
Read this if you want to know how to get a correct reply for your question:
http://www.catb.org/~esr/faqs/smart-questions.html
^^Took that from planoie's profile^^
^^Modified text taken from gbianchi profile^^
===========================================================
Technical Editor for: Professional Search Engine Optimization with ASP.NET
http://www.wiley.com/WileyCDA/WileyTitle/productCd-0470131470.html

Discussion:
http://p2p.wrox.com/topic.asp?TOPIC_ID=56429
Reply By: Imar Reply Date: 2/21/2007 12:32:38 PM
Yes, that was exactly what I was saying... Thanks... ;-)

Imar
Reply By: dparsons Reply Date: 2/21/2007 1:06:15 PM
No problem resident guru of everything that is Dreamweaver and ASP.NET 2.0 =]

===========================================================
Read this if you want to know how to get a correct reply for your question:
http://www.catb.org/~esr/faqs/smart-questions.html
^^Took that from planoie's profile^^
^^Modified text taken from gbianchi profile^^
===========================================================
Technical Editor for: Professional Search Engine Optimization with ASP.NET
http://www.wiley.com/WileyCDA/WileyTitle/productCd-0470131470.html

Discussion:
http://p2p.wrox.com/topic.asp?TOPIC_ID=56429
Reply By: grstad Reply Date: 2/24/2007 8:15:54 AM
...but then I need a server side test to test if the client browser have enablede JavaScript on each step (page) of my web-app. At least after the file upload, but before the upload-response-page... puuuh !? And if not, the client is dismissed...

Mvh
grstad
Reply By: Imar Reply Date: 2/24/2007 8:38:35 AM
No, you don't have to do that. What you need to do is check the uploaded file at the server before you save it. How you do this depends on the server side technology you're using. For example, in ASP.NET with C# you could so something like this:
if (FileUpload1.PostedFile.FileName.EndsWith(".jpg"))
{
  FileUpload1.SaveAs(SomePath);
}
else
{
  throw new Exception("Can't save files other than .jpg");
}
Instead of throwing an exception you could explain the user what went wrong.

If you're only allowing images, you could even try to load the image into an Image object wit GDI+ to ensure that the file really contains a readable image.

Cheers,

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
While typing this post, I was listening to: Tragedy (For You) [12" Vox] by Front 242 (Track 1 from the album: Tragedy (For You)) What's This?
Reply By: grstad Reply Date: 2/24/2007 3:48:57 PM
...Imar, With ASP 3.0

Can you comment this;

<%
dim a
a = File.Filename

      if instr(a,".gif") Then
    else
      'some err msg
      End if
%>

Mvh
grstad
Reply By: Imar Reply Date: 2/24/2007 7:25:49 PM
What do you mean with "comment this"? Are you asking me if this is secure? I don't know what File is but I know this isn't secure:

   if instr(a,".gif") Then

as it allows a file called SomeFile.gif.asp to pass.....

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 3/10/2007 8:22:41 AM
...Imar, can your C# validation be incorporated into this (which is part of the upload-prosedure cut/paste from the net);


<%
    Public Sub SaveToDisk(sPath)
        Dim oFS, oFile
        Dim nIndex
    
        If sPath = "" Or FileName = "" Then Exit Sub
        If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\"
    
        Set oFS = Server.CreateObject("Scripting.FileSystemObject")
        If Not oFS.FolderExists(sPath) Then Exit Sub
        
        Set oFile = oFS.CreateTextFile(sPath & FileName, True)
        
        For nIndex = 1 to LenB(FileData)
            oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
        Next

        oFile.Close
    End Sub
    
    Public Sub SaveToDatabase(ByRef oField)
        If LenB(FileData) = 0 Then Exit Sub
        
        If IsObject(oField) Then
            oField.AppendChunk FileData
        End If
    End Sub
%>

Mvh
grstad
Reply By: Imar Reply Date: 3/10/2007 8:27:14 AM
Again, I don't understand what you're asking.

Obviously, you can't incorporate C# in a VB Script, but I don't think that's what you're asking....

Can you elaborate?

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 3/10/2007 8:40:45 AM
...this code;

<%
    Public Sub SaveToDisk(sPath)
        Dim oFS, oFile
        Dim nIndex
    
        If sPath = "" Or FileName = "" Then Exit Sub
        If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\"
    
        Set oFS = Server.CreateObject("Scripting.FileSystemObject")
        If Not oFS.FolderExists(sPath) Then Exit Sub
        
        Set oFile = oFS.CreateTextFile(sPath & FileName, True)
        
        For nIndex = 1 to LenB(FileData)
            oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
        Next

        oFile.Close
    End Sub
    
    Public Sub SaveToDatabase(ByRef oField)
        If LenB(FileData) = 0 Then Exit Sub
        
        If IsObject(oField) Then
            oField.AppendChunk FileData
        End If
    End Sub
%>


...is part of a file I have included into my app. The file is all taken from the net.

Would it not be possible to validate the file extension (.jpeg, .gif) within the script-line or in corporation with;

        If sPath = "" Or FileName = "" Then Exit Sub

..?

Mvh
grstad
Reply By: Imar Reply Date: 3/10/2007 8:44:49 AM
Assuming sPath contains a full file path, you can indeed check if it ends on .jpeg or .gif to make sure you only allow images to be uploaded.

However, since you're creating a text file:

Set oFile = oFS.CreateTextFile(sPath & FileName, True)

I doubt you'll ever get an image on the server....

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 3/10/2007 8:53:08 AM
... but the txtfile is an binary-file, is it not? May be the validation schould be incorporated with the "save to database"-line...

<%
    Public Sub SaveToDisk(sPath)
        Dim oFS, oFile
        Dim nIndex
    
        If sPath = "" Or FileName = "" Then Exit Sub
        If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\"
    
        Set oFS = Server.CreateObject("Scripting.FileSystemObject")
        If Not oFS.FolderExists(sPath) Then Exit Sub
        
        Set oFile = oFS.CreateTextFile(sPath & FileName, True)
        
        For nIndex = 1 to LenB(FileData)
            oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
        Next

        oFile.Close
    End Sub
    
    Public Sub SaveToDatabase(ByRef oField)
        If LenB(FileData) = 0 Then Exit Sub        
        If IsObject(oField) Then
            oField.AppendChunk FileData
        End If
    End Sub
%>


instead? Does this matter?

Mvh
grstad

Reply By: Imar Reply Date: 3/10/2007 9:08:58 AM
?????? Completely lost....

Can you explain in detail what it is that you're trying to do and what exactly you're asking?

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 3/10/2007 9:14:09 AM
... to do a server-side validation of an image-upload enabeled for the clients of my web-app!

Mvh
grstad

PS
I do like the "Efteling-park" near Delft!
Reply By: Imar Reply Date: 3/10/2007 9:30:56 AM
I understood that part, but that's about it.

Where is this code used? How do you call it? Why do you think moving the validation from the disk based method to the database method would be enough? Where do you get your file? Does the current code work? What happens when you upload an image right now? Does it work? If not, do you get an error? And so on and so on. All relevant information if you need help....

Anyway, I would take one step back if I were you. At the place where you define the value for sPath and before you call SaveToDisk, look into that string, and see if it ends with an extension you want to allow using VB Script's Mid, Left and Right functions.

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 3/10/2007 9:56:18 AM
... yes, my web app does work. The upload-code works. There are no errors. But I do not wish any clients to be able to upload any harmful files to the webserver.

The code is called by using
      <!--#include file="img_to_serv.asp"-->

Why do you think moving the validation from the disk based method to the database method would be enough?
      I thaught that there were no ".jpeg" or ".gif" in the binary-file text to be recognized!

What aboat;

<%
    Public Sub SaveToDisk(sPath)
        Dim oFS, oFile
        Dim nIndex
    
        If sPath = "" Or FileName = "" Then Exit Sub
        If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\"
    
        Set oFS = Server.CreateObject("Scripting.FileSystemObject")
        If Not oFS.FolderExists(sPath) Then Exit Sub
        
        Set oFile = oFS.CreateTextFile(sPath & FileName, True)
        
        For nIndex = 1 to LenB(FileData)
            oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
        Next

        oFile.Close
    End Sub
    
    Public Sub SaveToDatabase(ByRef oField)
             If LenB(FileData) = 0 Or Right(FileData) <> .gif Then Exit Sub         
       If IsObject(oField) Then
            oField.AppendChunk FileData
        End If
    End Sub
%>



Mvh
grstad
Reply By: Imar Reply Date: 3/10/2007 10:05:41 AM
Since I don't know your code and only see the SaveToDatabase method, I can only guess. However, this looks weird:

If LenB(FileData) = 0 Or Right(FileData) <> .gif Then Exit Sub

What is FileData? Where does it get a value? I would assume it contains the actual file bytes, not the file name, right?

Again: I would take one step back if I were you. At the place where you get the file and call SaveToDisk or SaveToDatabase, look into the uploaded filename, and see if it ends with an extension you want to allow using VB Script's Mid, Left and Right functions.

There's no point in posting the same method over and over again. You'll need to look at the code that *calls* these methods to understand what contains the file name, and what contains the actual file.
Otherwise, I can only guess; which leads to nowhere...

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 3/10/2007 10:33:07 AM
...the sPath must be the binary value and the FileName must be the file path (!) saved to the database-field. So, if the FileName is different from ".gif", then Exit Sub!

Is it not enough to ad to the script;
      
If sPath = "" Or FileName = "" Or Right("FileName",4) <> ".gif" Then Exit Sub

..?

Mvh
grstad



Class FileUploader
    Public  Files
    Private mcolFormElem

    Private Sub Class_Initialize()
        Set Files = Server.CreateObject("Scripting.Dictionary")
        Set mcolFormElem = Server.CreateObject("Scripting.Dictionary")
    End Sub
    
    Private Sub Class_Terminate()
        If IsObject(Files) Then
            Files.RemoveAll()
            Set Files = Nothing
        End If
        If IsObject(mcolFormElem) Then
            mcolFormElem.RemoveAll()
            Set mcolFormElem = Nothing
        End If
    End Sub

    Public Property Get Form(sIndex)
        Form = ""
        If mcolFormElem.Exists(LCase(sIndex)) Then Form = mcolFormElem.Item(LCase(sIndex))
    End Property

    Public Default Sub Upload()
        Dim biData, sInputName
        Dim nPosBegin, nPosEnd, nPos, vDataBounds, nDataBoundPos
        Dim nPosFile, nPosBound

        biData = Request.BinaryRead(Request.TotalBytes)
        nPosBegin = 1
        nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(13)))
        
        If (nPosEnd-nPosBegin) <= 0 Then Exit Sub
         
        vDataBounds = MidB(biData, nPosBegin, nPosEnd-nPosBegin)
        nDataBoundPos = InstrB(1, biData, vDataBounds)
        
        Do Until nDataBoundPos = InstrB(biData, vDataBounds & CByteString("--"))
            
            nPos = InstrB(nDataBoundPos, biData, CByteString("Content-Disposition"))
            nPos = InstrB(nPos, biData, CByteString("name="))
            nPosBegin = nPos + 6
            nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(34)))
            sInputName = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
            nPosFile = InstrB(nDataBoundPos, biData, CByteString("filename="))
            nPosBound = InstrB(nPosEnd, biData, vDataBounds)
            
            If nPosFile <> 0 And  nPosFile < nPosBound Then
                Dim oUploadFile, sFileName
                Set oUploadFile = New UploadedFile
                
                nPosBegin = nPosFile + 10
                nPosEnd =  InstrB(nPosBegin, biData, CByteString(Chr(34)))
                sFileName = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
                oUploadFile.FileName = Right(sFileName, Len(sFileName)-InStrRev(sFileName, "\"))

                nPos = InstrB(nPosEnd, biData, CByteString("Content-Type:"))
                nPosBegin = nPos + 14
                nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(13)))
                
                oUploadFile.ContentType = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
                
                nPosBegin = nPosEnd+4
                nPosEnd = InstrB(nPosBegin, biData, vDataBounds) - 2
                oUploadFile.FileData = MidB(biData, nPosBegin, nPosEnd-nPosBegin)
                
                If oUploadFile.FileSize > 0 Then Files.Add LCase(sInputName), oUploadFile
            Else
                nPos = InstrB(nPos, biData, CByteString(Chr(13)))
                nPosBegin = nPos + 4
                nPosEnd = InstrB(nPosBegin, biData, vDataBounds) - 2
                If Not mcolFormElem.Exists(LCase(sInputName)) Then mcolFormElem.Add LCase(sInputName), CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
            End If

            nDataBoundPos = InstrB(nDataBoundPos + LenB(vDataBounds), biData, vDataBounds)
        Loop
    End Sub

    'String to byte string conversion
    Private Function CByteString(sString)
        Dim nIndex
        For nIndex = 1 to Len(sString)
           CByteString = CByteString & ChrB(AscB(Mid(sString,nIndex,1)))
        Next
    End Function

    'Byte string to string conversion
    Private Function CWideString(bsString)
        Dim nIndex
        CWideString =""
        For nIndex = 1 to LenB(bsString)
           CWideString = CWideString & Chr(AscB(MidB(bsString,nIndex,1)))
        Next
    End Function
End Class

Class UploadedFile
    Public ContentType
    Public FileName
    Public FileData
    
    Public Property Get FileSize()
        FileSize = LenB(FileData)
    End Property

    Public Sub SaveToDisk(sPath)
        Dim oFS, oFile
        Dim nIndex
    
        If sPath = "" Or FileName = "" Then Exit Sub
        If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\"
    
        Set oFS = Server.CreateObject("Scripting.FileSystemObject")
        If Not oFS.FolderExists(sPath) Then Exit Sub
        
        Set oFile = oFS.CreateTextFile(sPath & FileName, True)
        
        For nIndex = 1 to LenB(FileData)
            oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
        Next

        oFile.Close
    End Sub
    
    Public Sub SaveToDatabase(ByRef oField)
        If LenB(FileData) = 0 Then Exit Sub
        
        If IsObject(oField) Then
            oField.AppendChunk FileData
        End If
    End Sub

End Class
Reply By: Imar Reply Date: 3/10/2007 12:35:25 PM
Sorry, I give up. I fail to see where SaveToDisk is called. I fail to see where SaveToDatabase may be called. I don't know where sPath gets a value. I don't know where you use this code and how your web forms look like,

I have no desire to play hide and seek, and I don't feel like wading through your code in the hopes I somehow magically see your point. Sorry.

Maybe someone else can give it a shot.

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.
Reply By: grstad Reply Date: 3/10/2007 3:30:10 PM
...OK, Imar. But the point is simple; how do I server-side validate the files uploaded. When I try to use the ASP-tech and beginning VB-script.

That is all!

Thank you for your time spent on the subject!

Mvh
grstad

Internet has become favorable with that tool...thank you Tim Berners-Lee!
Reply By: Imar Reply Date: 3/10/2007 4:13:47 PM
I think I have told you that many times before: right before you save the file to disk, check the name of the file you're about to save.

My example in C# shows the general idea: simply check the string before you save the file. In VB Script you can, as I said earlier, use Right:

If LCase(Right(fullFileName, 4)) = ".jpg" Then
  ' File is a jpg
Else
  ' File is NOT a jpg
End If

How and where you use this code is up to you, as I can't find head nor tail in the code and description you have posted so far.... ;-)

Imar
---------------------------------------
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Everyone is unique, except for me.
Author of ASP.NET 2.0 Instant Results and Beginning Dreamweaver MX / MX 2004
Want to be my colleague? Then check out this post.

Go to topic 57530

Return to index page 5
Return to index page 4
Return to index page 3
Return to index page 2
Return to index page 1