We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Issue with form validation when sending manipulated POST

I'm using Phalcon 3.4 with Micro application. I've a simple form:

class LoginForm extends Base {
    public function initialize() {
        $field = new Text('email');
        $field->addValidators([
            new PresenceOf(),
            new Email([ 'allowEmpty' => TRUE ]),
            new StringLength([ 'max' => 255 ]),
        ]);
        $this->add($field);
    }
}

Form validation is executed like stated in documentation. The controller action looks like:

$form = new LoginForm();
if ($this->request->isPost()) {
    if ($form->isValid($this->request->getPost())) {
        [...]
    }
}

All is fine so far. But when I'm sending a manipulated POST request to my app intentionally, i.e.:

email[][email protected]

Then email would be parsed as array in PHP, so form validation fails and I get an error:

<b>WARNING:</b> mb_strlen() expects parameter 1 to be string, array given in [...]

This error is triggered on line $form->isValid(...) definitely. Whoa!? How can I prevent such behavior?

EDIT 2019-04-20: Added missing StringLength validator to sample code

edited Apr '19

Disable error reporting, or change the severity:

ini_set('display_errors', 0); // turns off errors altogether
error_reporting(E_ERROR); // only shows ERROR level messages


4.2k

This is definitely not a solution that I'll implement, because when rendering this field in volt, this "broken" data leads to next problem:

Value at index: 'value' type: 'array' cannot be rendered

I'm trying to fix this with a beforeValidation action in every form, which removes non-scalar values for related form fields from superglobals. But it feels dirty.



8.4k
edited Apr '19

i searched all of the source code and couldn't find anywhere mb_strlen() except in class StringLength

i would suggest creating custom validator and i just wrapped one for you but i didn't test it

<?php

use Phalcon\Validation;
use Phalcon\Validation\Validator;
use Phalcon\Validation\Message;

class ScalarValue extends Validator
{
    public function validate(Validation $validation, $field)
    {
        $value = $validation->getValue($field);

        if (!is_scalar($value)) {

            $label = $this->prepareLabel($validation, $field);

            $code = $this->prepareCode($field);

            $message = $this->prepareMessage($validation, $field, 'ScalarValue');

            $replacePairs = [':field' => $label];

            $validation->appendMessage(
                new Message(
                    strtr($message, $replacePairs),
                    $field,
                    'ScalarValue',
                    $code
                )
            );

            return false;
        }

        return true;
    }
}

then add this validator

public function initialize()
{
    $field = new Text('email');
    $field->addValidators([
        new PresenceOf(),
        new ScalarValue(['cancelOnFail' => true]),
        new Email([ 'allowEmpty' => TRUE ]),
    ]);
    $this->add($field);
}


4.2k

@talal424 Great idea, so you just have to remember to use this validator additionally if using StringLength. But on validation all validators are executed, so even, if ScalarValue fails, the following StringLength will fail again. Your approach would work only, if Phalcon validation returns on first fail.



8.4k

correct

we can use cancelOnFail option to reslove this

public function initialize()
{
    $field = new Text('email');
    $field->addValidators([
        new PresenceOf(),
        new ScalarValue(['cancelOnFail' => true]),
        new Email([ 'allowEmpty' => TRUE ]),
    ]);
    $this->add($field);
}


8.4k
Accepted
answer

btw i tested passing an array to email validator and i didn't get any warnings what php version and phalcon you are working with ?

this is the test code on phalcon v3.4.2 and php 5.6

$validation = new Phalcon\Validation;

$validation->add(
    'email',
    new Phalcon\Validation\Validator\PresenceOf
);

$validation->add(
    'email',
    new ScalarValue([
        'message' => 'message for ScalarValue',
        //'cancelOnFail' => true
    ])
);

$validation->add(
    'email',
    new Phalcon\Validation\Validator\Email([
        'message' => 'message for Email',
        'allowEmpty' => TRUE
    ])
);

$messages = $validation->validate(['email' => ['dfdf','ccc','xx']]);

if (count($messages)) {
    foreach ($messages as $message) {
        var_dump($message);
    }
}

exit();

result:

object(Phalcon\Validation\Message)[121]
    protected '_type' => string 'ScalarValue' (length=11)
    protected '_message' => string 'message for ScalarValue' (length=23)
    protected '_field' => string 'email' (length=5)
    protected '_code' => int 0

object(Phalcon\Validation\Message)[119]
    protected '_type' => string 'Email' (length=5)
    protected '_message' => string 'message for Email' (length=17)
    protected '_field' => string 'email' (length=5)
    protected '_code' => int 0


4.2k

Oh, right now I see, that I forgot to include StringLength validator in my sample code above. It has nothing to do with Email validator, the error is thrown by StringLength really. But your approach is really good. Thanks