Filtration, validation, escape and password of PHP best practices

Posted by werkkrew on Tue, 11 Jun 2019 21:58:17 +0200

Filtration, validation and escape

1) Don't trust any data from data sources that are not directly controlled by you. Including but not limited to:

  • $_GET

  • $_POST

  • $_REQUEST

  • $_COOKIE

  • $argv

  • php://stdin

  • php://input

  • file_get_contents()

  • Remote database

  • Remote API

  • Data from the client

2) Solution: filter input. To delete unsafe characters, data must be filtered before it reaches the storage layer of the application. Data to be filtered includes more than HTML, SQL queries and user information.

  • HTML: Use the htmlentities() function to filter HTML into corresponding entities. This function escapes the HTML character of the formulated character to render safely in the storage layer. The correct way to use it is to filter the input using htmlentities ($input, ENT_QUOTES,'UTF-8'). Or use HTML Purifier. The disadvantage is slowness.

  • SQL queries: Sometimes you have to build SQL queries based on data. At this time, we need to use PDO preprocessing statements to filter external data.

  • User information: User information is filtered using filter_var() and filter_input().

3. Validation data: filter_var() can also be used to validate that the value to be validated is returned successfully and false is returned failed. But this function cannot validate all data, so some validation functional components can be used. For example, aura/filter or symfony/validator
4) escape output: You can still use the htmlentities function, and some template engines have their own escape functions.

Password

1) Never know the user's password.
2) Never restrict the user's password, but only the minimum length.
3. Never send a user's password by e-mail. You can send a link to modify your password with a token validation for the user himself.
4. Calculate the hash value of the user's password using bcrypt. Encryption and hashing are not the same thing. Encryption is a two-way algorithm. Encrypted data can be decrypted. But hashing is a single algorithm. The data after hashing can not be restored. The data after hashing is always the same. Use the database to store the values after passing the bcrypt hash password.
5. Use password hash API to simplify the operation of calculating password hash and verifying password. The following general operations for registered users

POST /register.php HTTP/1.1
Content-Length:  43
Content-type: application/x-www-form-urlencoded

email=xiao@hello.world&password=nihao

Here is the PHP file that accepts the request

<?php
try {
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    if (!$email) {
        throw new Exception('Invalid email');
    }
    $password = filter_iput(INPUT_POST, 'password');
    if (!$password || mb_strlen($password) < 8) {
        throw new Exception('Password must contain 8+ characters');
    }
    //Create hash values for passwords
    $passwordHash = password_hash(
        $password,
        PASSWORD_DEFAULT,
        ['cost' => 12]
     );

    if ($passwordHash === false) {
        throw new Exception('Password hash failed');
    }

    //Create a user account, here is the fictitious code
    $user = new User();
    $user->email = $email;
    $user->password_hash = $passwordHash;
    $user->save();
    header('HTTP/1.1 302 Redirect');
    header('Location: /login.php');
} catch (Exception $e) {
    header('HTTP1.1 400 Bad Request');
    echo $e->getMessage();
}

6. Modify the third value of password_hash() according to the specific computing power of the machine. It usually takes 0.1s-0.5s to calculate the hash value.
7. The hash value of the password is stored in a database column of varchar(255) type.
8. General flow of logged-in users

POST /login.php HTTP1.1
Content-length: 43
Content-Type: application/x-www-form-urlencoded

email=xiao@hello.wordl&pasword=nihao
session_start();
try {
    $email = filter_input(INPUT_POST, 'email');
    $password = filter_iinput(INPUT_POST, 'password');

    $user = User::findByEmail($email);

    if (password_verify($password, $user->password_hash) === false) {
        throw new Exception(''Invalid password);
    }

    //If necessary, recalculate the hash value of the password
    $currentHasAlgorithm = PASSWORD_DEFAULT;
    $currentHashOptions = array('cost' => 15);
    $passwordNeedsRehash = password_needs_rehash(
        $user->password_hash,
        $currentHasAlgorithm,
        $currentHasOptions
    );
    if ($passwordNeedsRehash === true) {
        $user->password_hash = password_hash(
            $password,
            $currentHasAlgorithm,
            $currentHasOptions
        );

        $user->save();
    }

    $_SESSION['user_logged_in'] = 'yes';
    $_SESSION['user_email'] = $email;

    header('HTTP/1.1 302 Redirect');
    header('Location: /user-profile.php');
} catch (Exception) {
    header('HTTP/1.1 401 Unauthorized');
    echo $e->getMessage();
}

9). The password hash API before PHP 5.5.0 is not available. It is recommended to use the ircmaxell/password-compat component.

Thematic series

PHP Thematic Series Directory Address: https://github.com/xx19941215/webBlog
The PHP project series is expected to write about 20 articles. It mainly summarizes the basic knowledge that we often neglect in our daily PHP development and some practical suggestions about specification, deployment and Optimization in modern PHP development. At the same time, it also has an in-depth study on the characteristics of Javascript language.

Topics: PHP Database SQL PDO