Security

Guiding principles

Forms validation

Client-side validation

It is preferable to perform an initial client-side validation to avoid submitting invalid data to the server.

Example of the creation of a form in the action of a controller (this part could be located in a dedicated service for a better separation of layers):

app/controllers/UsersManagement.php
 1 public function index(){
 2     $frm=$this->jquery->semantic()->dataForm('frm-user',new User());
 3     $frm->setFields(['login','password','connection']);
 4     $frm->fieldAsInput('login',
 5         ['rules'=>'empty']
 6     );
 7     $frm->fieldAsInput('password',
 8         [
 9             'inputType'=>'password',
10             'rules'=>['empty','minLength[6]']
11         ]
12     );
13     $frm->setValidationParams(['on'=>'blur','inline'=>true]);
14     $frm->fieldAsSubmit('connection','fluid green','/submit','#response');
15     $this->jquery->renderDefaultView();
16 }

The Associated View:

app/views/UsersManagement/index.html
 {{ q['frm-user'] | raw }}
 {{ script_foot | raw }}
 <div id="response"></div>
../_images/frm-user.png

Nota

The CRUD controllers automatically integrate this client-side validation using the Validators attached to the members of the models.

#[Column(name: "password",nullable: true,dbType: "varchar(255)")]
#[Validator(type: "length",constraints: ["max"=>20,"min"=>6])]
#[Transformer(name: "password")]
private $password;

Server-side validation

It is preferable to restrict the URLs allowed to modify data.
Beforehand, by specifying the Http method in the routes, and by testing the request :

#[Post(path: "/submit")]
public function submitUser(){
   if(!URequest::isCrossSite() && URequest::isAjax()){
      $datas=URequest::getPost();//post with htmlEntities
      //Do something with $datas
   }
}

Nota

The Ubiquity-security module offers additional control to avoid cross-site requests.

After modifying an object, it is possible to check its validity, given the validators attached to the members of the associated Model:

#[Post(path: "/submit")]
public function submitUser(){
   if(!URequest::isCrossSite()){
      $datas=URequest::getPost();//post with htmlEntities
      $user=new User();
      URequest::setValuesToObject($user,$datas);

      $violations=ValidatorsManager::validate($user);
      if(\count($violations)==0){
         //do something with this valid user
      } else {
         //Display violations...
      }
   }
}

DAO operations

It is always recommended to use parameterized queries, regardless of the operations performed on the data:
  • To avoid SQL injections.

  • To allow the use of prepared queries, speeding up processing.

$googleUsers=DAO::getAll(User::class,'email like ?',false,['%@gmail.com']);
$countActiveUsers=DAO::count(User::class,'active= ?',[true]);

Nota

DAO operations that take objects as parameters use this mechanism by default.

DAO::save($user);

Passwords management

The Password Transformer allows a field to be of the password type when displayed in an automatically generated CRUD form.

#[Transformer(name: "password")]
private $password;

After submission from a form, it is possible to encrypt a password from the URequest class:

$encryptedPassword=URequest::password_hash('password');
$user->setPassword($encryptedPassword);
DAO::save($user);

The algorithm used in this case is defined by the php PASSWORD_DEFAULT.

It is also possible to check a password entered by a user in the same way, to compare it to a hash:

if(URequest::password_verify('password', $existingPasswordHash)){
   //password is ok
}

Importante

Set up Https to avoid sending passwords in clear text.

Security module/ ACL management

In addition to these few rules, you can install if necessary: