views:

715

answers:

3

Do web browsers send the file size in the http header when uploading a file to the server. And if that the case, then, is it possible to refuse the file just by reading the header and not wait for the whole upload process to finish?

+1  A: 
  1. I'm not sure, but you should not really trust anything sent in the header, as it could be faked by the user.

  2. It depends on how the server works. For example in PHP your script will not run until the file upload is complete, so this wouldn't be possible.

Tom Haigh
+5  A: 

http://www.faqs.org/rfcs/rfc1867.html

HTTP clients are encouraged to supply content-length for overall file input so that a busy server could detect if the proposed file data is too large to be processed reasonably

But the content-length is not required, so you cannot rely on it. Also, an attacker can forge a wrong content-length.

To read the file content is the only reliable way. Having said that, if the content-lenght is present and is too big, to close the connection would be a reasonable thing to do.

Also, the content is sent as multipart, so most of the modern frameworks decode it first. That means you won't get the file byte stream until the framework is done, which could mean "until the whole file is uploaded".

Vladimir Dyuzhev
+2  A: 

EDIT : before going too far, you may want to check this other answer relying on apache configuration : http://stackoverflow.com/questions/307679/using-jquery-restricting-file-size-before-uploading#307861 . the description below is only useful if you really need even more custom feedback.

Yes, you can get some information upfront, before allowing the upload of the whole file.

Here's an example of header coming from a form with the enctype="multipart/form-data" attribute :

POST / HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.7,fr-be;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: multipart/form-data; boundary=---------------------------886261531333586100294758961
Content-Length: 135361

-----------------------------886261531333586100294758961
Content-Disposition: form-data; name=""; filename="IMG_1132.jpg"
Content-Type: image/jpeg

(data starts here and ends with -----------------------------886261531333586100294758961 )

You have the Content-Length in the header, and additionally there is the Content-Type in the header of the file part ( each file has its own header, which is the purpose of multipart encoding ). Beware that it's the browser responsibility to set a relevant Content-Type by guessing the file type ; you can't guarantee it, but it should be fairly reliable for early rejection ( yet you'd better check the whole file when it's entirely available ).

Now, there is a gotcha. I used to filter image files like that, not on the size, but on the content-type ; but as you want to stop the request as soon as possible, the same problem arises : the browser only gets your response once the whole request is sent, including form content and thus uploaded files.

If you don't want the provided content and stop the upload, you have no choice but to brutally close the socket. The user will only see a confusing "connection reset by peer" message. And that sucks, but it's by design.

So you only want to use this method in cases of background asynchronous checks ( using a timer that checks the file field ). So I had that hack :

  • I use jquery to tell me if the file field has changed
  • When a new file is chosen, disable all other file fields on the same form to get only that one.
  • Send the file asynchronously ( jQuery can do it for you, it uses a hidden frame )
  • Server-side, check the header ( content-length, content-type, ... ), cut the connection as soon as you got what you need.
  • Set a session variable telling if that file was OK or not.
  • Client-side, as the file is uploaded to a frame you don't even get any kind of feedback if the connection is closed. Your only alternative is a timer.
  • Client-side, a timer polls the server to get a status for the uploaded file. Server side, you have that session variable set, send it back to the brower.
  • The client has the status code ; render it to your form : error message, green checkmark/red X, whatever. Reset the file field or disable the form, you decide. Don't forget to re-enable other file fields.

Quite messy, eh ? If any of you has a better alternative, I'm all ears.

vincent