Thursday, August 23, 2007

PHP large file uploads

There are several upload limits you need to worry about in PHP. They are configurable in the php.ini file. The post_max_size is devious, as if a user uploads an image larger than this size, php will transfer no POST data to your script. If you look in the php log (generally set to /var/log/messages) you will see a line like this:

POST Content-Length of 66605360 bytes exceeds the limit of 8388608 bytes in Unknown on line 0 describes how to signal an error to the user, in this case.

There is an added complication if you use cake. Normally in cake, the same controller/action is used for both rendering the form and on form submit. The action identifies whether it should render the form or act on the submit based on the form data. If the form data is available it processes it, else it will pass back the form.

Since the only way we can figure out that the upload didn't work is by checking if the POST data is empty, we can't use this design for our controller/action in cake. What we could do is use different actions for rendering the form and submitting it. In the action for submit, we can check $this->data and alert the user if that is empty.

Here are the 2 example actions (file : app/controllers/profiles_controller.php)

// This function was written as a work-around for detecting failures in large POST
// requests - if the POST data is bigger than the php ini setting 'post_max_size',
// php will not transfer any POST data to the controller action, but if we use the same
// controller action for rendering the form as well as on form submit, we can't
// differentiate between the two.
function basics() {
if (!$this->data) {
$this->Session->setFlash("Couldn't upload the image, please choose a smaller image");
else {
$this->redirect(array('controller'=>'profiles', 'action'=>'basic'), null, true);

function basic() {

// regular logic follows ...


Make sure the form app/views/profiles/basic.ctp directs the action to /profile/basics) :

create('User', array('type' => 'post', 'enctype' => 'multipart/form-data', 'url'=>'/profiles/basics')); ?>

Thursday, August 02, 2007

analyzing browser traffic

Today I played around with tcpdump/wireshark/curl to analyze/replay some HTTP traffic sent by my browser to usps - in particular I was analyzing the form data that was submitted in their 'tracking confirmation' service.

I first set tcpdump on the desktop to capture traffic going to port 80, and then hit the 'submit' button on the tracking service page on usps. After I got the data from the service, I quit from tcpdump (Ctrl+C) and then looked at the generated raw dump (file) with wireshark.

Wireshark showed me the post data used in the form, and I used curl to replay the data back to the usps web server, and it returned me the html including my delivery confirmation data.

Since the tracking codes seem rather sequential, one can imagine an attack where a script runs many requests against the usps server and obtains a mega list of information about package delivery.

Here are the steps:
1. run tcpdump on the desktop:
tcpdump -a -x -i eth0 tcp dst port 80 -s 1024 -w /home/user/tcp.dump
2. enter the tracking number and hit 'Go' button at
3. after the browser has retrieved the data, quit tcpdump (Ctrl+C) on the command line
4. analyze the raw file (/home/user/tcp.dump) using wireshark
5. look for the HTTP packets, on one you will see the post data that looks like this:

6. to replay the data, run curl with this data as post data against the host found in the HTTP header (in wireshark) as follows (don't forget to enclose the post data within quotes):

curl -d 'origTrackNum=0307+0020+0000+8347+7000&Go+to+Track+and+Confirm+Label+%2F+Receipt+Number+Page.x=19&Go+to+Track+and+Confirm+Label+%2F+Receipt+Number+Page.y=7&Go+to+Track+and+Confirm+Label+%2F+Receipt+Number+Page=Go'

7. observe the html that the server returns (which has the delivery confirmation details)