5
votes

I'm using CakePHP's SecurityComponent. And it's very essential as it saves forms from CSRF attacks. My project has total 10-12 forms and this is my first CakePHP project. After enabling SecurityComponent I was in a bit trouble but could get rid off after some careful minutes. This is the last form of my project and seems everything is correct to me but still the form is being black holed :(. Can anybody please tell me the problem? I don't want to disable CSRF checking or SecurityComponent. Here is my view code:

<?php
echo $this->Form->create('Record');
?>
<script type="text/javascript"> var me = new MetroExam(); </script>
<div class="exam_paper">
    <div class="question_box" id="q_b">
        <div class="q_n_a_header">
            <div class="instructions">
                <b>Instructions:</b><br>
                <?=$inst['value_text']; ?>
            </div>
            <div id="timer">Please wait</div>
        </div>
        <div id="q_paper">
           <img id="q" style="display: none;" src="/oes/<?=$exam['path'].'?ts='.time(); ?>">

            <img id="loading_img" src="/oes/img/loading.gif">
        </div>
    </div>
    <div class="ans_box" id="a_b">
        <!-- information about answer paper. !important -->
        <?php
        $i = 0;

        //these fields are essential for evaluating ans paper
        echo $this->Form->hidden('submit', array('value' => 'true'));
        echo $this->Form->hidden('start_time', array('value' => ''));
        echo $this->Form->hidden('end_time', array('value' => ''));
        echo $this->Form->hidden('duration', array('value' => ''));
        echo $this->Form->hidden('valid', array('value' => ''));
        echo $this->Form->hidden('passed', array('value' => ''));

        //options for all radio
        $options     = array(
            '1' => 'A',
            '2' => 'B',
            '3' => 'C',
            '4' => 'D'
        );
        if($exam['choices'] == 5){
            $options['5'] = 'None';
        }

        $questions = (int)$exam['questions']; // 40 <= $exam['questions'] <= 100
        $i = 1;
        while($questions--){
            echo '<div class="'.(($i%2)==1?'each_answer_even':'each_answer_odd').'" id="ans-'.$i.'">';
            echo '<div class="q_number">'.($i <= 9 ? '0'.$i : $i).'</div>';
            $name       = 'ans'.str_pad($i, 3, '0', STR_PAD_LEFT);
            $attributes = array('empty' => false, 'legend' => false, 'onclick' => 'me.answer_click('.$i.')');
            echo '<div class="mcq">'.$this->Form->radio($name, $options, $attributes).'</div>';
            echo '</div>';
            $i++;
        }
        echo $this->Form->end('Submit');
        ?>
    </div>
</div>

This is basically a MCQ exam form. Where each group has 4 or 5 radio buttons and total 40 to 100 groups in a form. I'm using CakePHP 2.4. Thanks in advance.

2
are you by any chance changing the values of the hidden inputs with javascript? - Nunser
Yes I do. I'm changing 4/5 hidden fields by jQuery. But is it the problem? - A. K. M. Tariqul Islam
Probably, but let's debug. Try changing those hidden for simple text inputs and try submitting the form. If it submits, then that's your problem and I'll answer with the explanation and possible solution. If not, then we have to see what else can it be. - Nunser
Yes, I commented all those hidden fields who get values from jQuery and the form is working as it should. Please post an answer so that I can accept. And also tell me how to fill up those hidden fields because I have to do that. The page loads an image having a size of 1-2 MB and I need to get the time when it completes loading. After loading I start countdown timer. So, using javascript is important here. - A. K. M. Tariqul Islam

2 Answers

9
votes

As per the comments, the problem appears because you are changing the hidden values of the form. The way SecurityComponent works, is that it "locks" the name of the fields, so an evildoer can't add new fields or change the values once the form is sent. But it is even more strict with the hidden values, because it locks the field name and value. So by changing it with jQuery you're blackhole-ing your own form.

There's a nice little post where I learned this, take a look at it. The author there also explains two ways of bypassing this problem. One is to disable the security for hidden fields, so the hash calculated for the token doesn't include those values... which isn't really secure...
And another solution is to modify the FormHelper, and tell it to "lock" the hidden fields names but not the values. I don't remember what version of Cake the author uses for the example, but the code given there should be practicaly the same. So with that solution, you can tell the form to not be so strict with you with an option array.

Oh, and the other option given there (this is what I normally use) (I just read it now there... I thought I figure that on my own... oh well), is to just use normal input text fields for the ones you want hidden, and add a css style like display:none.

It's up to you what you think is best. I like the css option because is simpler, and really, if someone is going to mess with my css evily (with firebug or something like that), they might just as well do it with the values of hidden fields, it doesn't require any more effort. You should take all the extra steps and verifications when handling that form submission anyway. But like I said, up to you which do you think is best for your situation.

1
votes

In addition to what was already posted, here's something else what might be causing the problem: in my case, a hidden input had it's name overwritten.

$this->Form->create('ExampleModel'):
$this->Form->input('foo_bar', array(
    'type' => 'hidden',
    'name' => 'foo_bar',
));

As a result, the final $this->request->data had the corresponding key $this->request->data['foo_bar']. It was not within the $this->request->data['ExampleModel'] array, and that's what the problem was.

To fix the issue, I had to remove the name key from the template, making the input belong to the model's data, and then just change the controller to accept that value.

Hope this helps someone else.

Update: this would also work on a form not attached to any model, e.g.:

$this->Form->create(false, array(
    'url' => '/example',
)):