Interaction between getPostParam and file uploads

59 views
Skip to first unread message

Leon Smith

unread,
Sep 14, 2012, 11:56:17 AM9/14/12
to snap, snap_fr...@googlegroups.com
I've had a problem where getPostParam/getParam returns Nothing for a CSRF token passed to Snap as a hidden form parameter when dealing with file uploads.   I've included a minimal-ish demonstration of the behavior.

Now, it makes sense that Snap would not find the "comment" field until after the file upload is completed,  because the browser sends the comment after the file and Snap would have to process the entire file upload before it would receive the comment field.   But the browser sends the CSRF token before the file upload,   and yet both getPostParam and getParam return Nothing,  which seems like a bug.

Best,
Leon


upload-params.tar.gz

Gregory Collins

unread,
Sep 15, 2012, 3:01:18 AM9/15/12
to snap_fr...@googlegroups.com
On Fri, Sep 14, 2012 at 5:56 PM, Leon Smith <leon.p...@gmail.com> wrote:
I've had a problem where getPostParam/getParam returns Nothing for a CSRF token passed to Snap as a hidden form parameter when dealing with file uploads.   I've included a minimal-ish demonstration of the behavior.

Now, it makes sense that Snap would not find the "comment" field until after the file upload is completed,  because the browser sends the comment after the file and Snap would have to process the entire file upload before it would receive the comment field.   But the browser sends the CSRF token before the file upload,   and yet both getPostParam and getParam return Nothing,  which seems like a bug.

Definitely not a bug here. You're misunderstanding the Snap execution model. When control is passed to the user handler, unless the request body has a type of application/x-www-form-encoded, the request body has NOT been read at all. The reason for this is that the request body can be large and Snap doesn't know what to do with it -- we're obviously not going to read it into RAM, and we're also not just going to shunt every request to disk. This has to be left up to the programmer.

So with a multipart/form-data upload, the parameter you're looking for is not in the parameters map because you haven't read it from the socket yet! It will be in the parameters map *after* the handleFileUploads call finishes. Usually we stuff the csrf token in a cookie for this reason, although you're going to have to read the whole request body anyways to maintain the HTTP protocol invariants. If you want the connection to be forcibly terminated instead, you have to call terminateConnection.

Hope this helps.

G
-- 
Gregory Collins <gr...@gregorycollins.net>

Leon Smith

unread,
Sep 17, 2012, 4:02:52 PM9/17/12
to snap_fr...@googlegroups.com
On Sat, Sep 15, 2012 at 3:01 AM, Gregory Collins <gr...@gregorycollins.net> wrote:
Definitely not a bug here. You're misunderstanding the Snap execution model. When control is passed to the user handler, unless the request body has a type of application/x-www-form-encoded, the request body has NOT been read at all. The reason for this is that the request body can be large and Snap doesn't know what to do with it -- we're obviously not going to read it into RAM, and we're also not just going to shunt every request to disk. This has to be left up to the programmer.

So with a multipart/form-data upload, the parameter you're looking for is not in the parameters map because you haven't read it from the socket yet! It will be in the parameters map *after* the handleFileUploads call finishes. Usually we stuff the csrf token in a cookie for this reason, although you're going to have to read the whole request body anyways to maintain the HTTP protocol invariants. If you want the connection to be forcibly terminated instead, you have to call terminateConnection.

You can't stuff a CSRF token into a cookie,  as cookies are submitted with any request from the browser to the site,  whether that request is legitimate or not.

For my purposes,  allowing the upload before checking the csrf token is perfectly acceptable;   I suppose this might be a CSRF DOS attack,  but I'm honestly not too worried about that.    Still,  is there a way to process only part of a multipart/form data upload?   Some way of dealing with this use case (under programmer control) seems like a good idea.

Best,
Leon

Gregory Collins

unread,
Sep 18, 2012, 5:47:07 AM9/18/12
to snap_fr...@googlegroups.com
On Mon, Sep 17, 2012 at 10:02 PM, Leon Smith <leon.p...@gmail.com> wrote:
On Sat, Sep 15, 2012 at 3:01 AM, Gregory Collins <gr...@gregorycollins.net> wrote:
Definitely not a bug here. You're misunderstanding the Snap execution model. When control is passed to the user handler, unless the request body has a type of application/x-www-form-encoded, the request body has NOT been read at all. The reason for this is that the request body can be large and Snap doesn't know what to do with it -- we're obviously not going to read it into RAM, and we're also not just going to shunt every request to disk. This has to be left up to the programmer.

So with a multipart/form-data upload, the parameter you're looking for is not in the parameters map because you haven't read it from the socket yet! It will be in the parameters map *after* the handleFileUploads call finishes. Usually we stuff the csrf token in a cookie for this reason, although you're going to have to read the whole request body anyways to maintain the HTTP protocol invariants. If you want the connection to be forcibly terminated instead, you have to call terminateConnection.

You can't stuff a CSRF token into a cookie,  as cookies are submitted with any request from the browser to the site,  whether that request is legitimate or not.

Right. Sorry. Although usually you match cookie contents against a token contained in the request.
 
For my purposes,  allowing the upload before checking the csrf token is perfectly acceptable;   I suppose this might be a CSRF DOS attack,  but I'm honestly not too worried about that.    Still,  is there a way to process only part of a multipart/form data upload?   Some way of dealing with this use case (under programmer control) seems like a good idea.

You can use "handleMultipart" rather than "handleFileUploads" but in general there's no way to safely cancel after a csrf mismatch except for pulling the eject lever and terminating the connection, which is probably not what you want. (The browser is expecting you to read the data it's still sending and respond with an appropriate HTTP response.)

Leon Smith

unread,
Sep 18, 2012, 7:50:38 AM9/18/12
to snap_fr...@googlegroups.com
On Tue, Sep 18, 2012 at 5:47 AM, Gregory Collins <gr...@gregorycollins.net> wrote:
Right. Sorry. Although usually you match cookie contents against a token contained in the request.

Yup,  I communicate the csrf token in a cookie,  and then use javascript to read the cookie and submit it as a hidden form parameter.  I do this mostly so that people can log out, log back in, and then still submit a form they had open in another tab.   But maybe there are other reasons?

So submitting a form requires two cookies,  an HTTP-only cookie that cannot be read by javascript (to validate the browser) and a cookie that *must* be read by javascript.  (to validate the particular request from the browser.)    For some reason,  I find this rather amusing.    Though I admit web development is not my specialty.


You can use "handleMultipart" rather than "handleFileUploads" but in general there's no way to safely cancel after a csrf mismatch except for pulling the eject lever and terminating the connection, which is probably not what you want. (The browser is expecting you to read the data it's still sending and respond with an appropriate HTTP response.)

I suppose I can simply ignore the file upload.    In any case, I'm not too worried at this stage.

Leon Smith

unread,
Sep 18, 2012, 7:58:17 AM9/18/12
to snap_fr...@googlegroups.com
And by ignore the file upload,  I mean check the csrf token early,  and if it doesn't match, then send the file contents to (the equivalent of) /dev/null.
Reply all
Reply to author
Forward
0 new messages