Безопасность

Overview

Примечание: требуется, чтобы расширение openssl PHP присутствовало в системе

Phalcon\Security - это компонент, который помогает разработчикам решать общие задачи, связанные с безопасностью, такие как хэширование паролей и защита от подделки межсайтовых запросов (CSRF).

Хэширование Паролей

Хранение паролей в открытом тексте-это плохая практика безопасности. Любой человек, имеющий доступ к базе данных, немедленно получит доступ ко всем учетным записям пользователей, что позволит ему участвовать в несанкционированных действиях. Для борьбы с этим многие приложения используют популярные односторонние методы хеширования md5 и sha1. Однако аппаратное обеспечение развивается ежедневно, и по мере того, как процессоры становятся быстрее, эти алгоритмы становятся уязвимыми для атак грубой силы. Эти атаки также известны как радужные таблицы.

Компонент безопасности использует bcrypt в качестве алгоритма хеширования. Благодаря алгоритму настройки ключа Eksblowfish мы можем сделать шифрование пароля настолько медленным, насколько захотим. Медленные алгоритмы минимизируют воздействие атак грубой силы.

Bcrypt - это адаптивная хэш-функция, основанная на криптографическом алгоритме симметричного блочного шифра Blowfish. Он также вводит фактор безопасности или работы, который определяет, насколько медленно хэш-функция будет генерировать хэш. Это фактически сводит на нет использование методов хеширования ПЛИС или графических процессоров.

Если в будущем аппаратное обеспечение станет быстрее, мы можем увеличить коэффициент работы, чтобы смягчить это. Соль генерируется с помощью псевдослучайных байтов с помощью функции PHP openssl_random_pseudo_bytes.

Этот компонент предлагает простой интерфейс для использования алгоритма:

<?php

use Phalcon\Security;

$security = new Security();

echo $security->hash('Phalcon'); 
// $2y$08$ZUFGUUk5c3VpcHFoVUFXeOYoA4NPFEP4G9gcm6rdo3jFPaNFdR2/O

Хэш, который был создан, использовал коэффициент работы по умолчанию, который установлен в 10. Использование более высокого коэффициента работы займет немного больше времени для вычисления хэша.

Теперь мы можем проверить, является ли значение, отправленное нам пользователем через пользовательский интерфейс нашего приложения, идентичным нашей хэшированной строке:

<?php

use Phalcon\Security;

$password = $_POST['password'] ?? '';

$security = new Security();
$hashed = $security->hash('Phalcon');

echo $security->checkHash($password, $hashed); // true / false

Приведенный выше пример просто показывает, как можно использовать checkHash(). В производственных приложениях нам обязательно нужно будет санировать входные данные, а также хранить хэшированный пароль в хранилище данных, таком как база данных. Используя контроллеры, приведенный выше пример можно показать следующим образом:

<?php

use MyApp\Models\Users;
use Phalcon\Http\Request;
use Phalcon\Mvc\Controller;
use Phalcon\Security;

/**
 * @property Request  $request
 * @property Security $security
 */
class SessionController extends Controller
{
    /**
     * Login
     */
    public function loginAction()
    {
        $login    = $this->request->getPost('login');
        $password = $this->request->getPost('password');

        $user = Users::findFirst(
            [
                'conditions' => 'login = :login:',
                'bind'       => [
                    'login' => $login,
                ],
            ]
        );

        if (false !== $user) {
            $check = $this
                ->security
                ->checkHash($password, $user->password);
            
            if (true === $check) {
                // OK
            }
        } else {
            $this->security->hash(rand());
        }

        // ERROR
    }

    /**
     * Register
     */
    public function registerAction()
    {
        $login    = $this->request->getPost('login', 'string');
        $password = $this->request->getPost('password', 'string');

        $user = new Users();

        $user->login    = $login;
        $user->password = $this->security->hash($password);

        $user->save();
    }

}

ПРИМЕЧАНИЕ Приведенный выше фрагмент кода является неполным и не должен использоваться как таковой для производственных приложений

registerAction() выше принимает отправленные данные из UI. Он очищает его строковым фильтром string , а затем создает новый объект модели User. Затем переданные данные присваиваются соответствующим свойствам перед сохранением. Обратите внимание, что для пароля используется метод hash() компонента Phalcon\Security, чтобы не сохранять его как обычный текст в базе данных.

loginAction() принимает опубликованные данные из пользовательского интерфейса, а затем пытается найти пользователя в базе данных на основе поля login. Если пользователь действительно существует, он будет использовать метод checkHash() компонента Phalcon\Security, чтобы определить, совпадает ли предоставленный пароль с паролем, сохраненным в базе данных.

ПРИМЕЧАНИЕ: Вам не нужно хешировать предоставленный пароль (первый параметр) при использовании checkHash() - компонент сделает это для вас.

Если пароль неверен, можно сообщить пользователю, что с учетными данными что-то не так. Всегда полезно не предоставлять конкретную информацию о пользователях пользователям, которые хотят взломать ваш сайт. Таким образом, например, приведенный выше пример может создать два сообщения:

  • Пользователь не найден в базе данных
  • Пароль неправильный

Разделение сообщений об ошибках не является хорошей идеей. Если хакер, использующий грубую силовую атаку, обнаруживает второе сообщение, он может перестать пытаться угадать логин и сконцентрироваться на пароле, тем самым увеличивая свои шансы получить доступ. Более подходящее сообщение для обоих возможных условий ошибки

Invalid Login/Password combination

Наконец, в примере видно, что, когда пользователь не найден, мы называем:

$this->security->hash(rand());

Это делается для защиты от временных атак. Независимо от того, существует ли пользователь или нет, выполнение скрипта займет примерно столько же времени, поскольку он снова вычисляет хэш, хотя мы никогда не будем использовать этот результат.

Исключения

Все исключения, создаваемые в компоненте безопасности, будут иметь тип Phalcon\Security\Exception. Это исключение можно использовать для выборочного перехвата исключений, создаваемых только из этого компонента. Исключения могут быть вызваны, если алгоритм хэширования неизвестен, если служба session не присутствует в контейнере Di и т. д.

<?php

use Phalcon\Security\Exception;
use Phalcon\Mvc\Controller;

class IndexController extends Controller
{
    public function index()
    {
        try {
            $this->security->hash('123');
        } catch (Exception $ex) {
            echo $ex->getMessage();
        }
    }
}

Защита от подделки межсайтовых запросов (CSRF)

Подделка межсайтовых запросов (CSRF) - еще одна распространенная атака на веб-сайты и приложения. Формы, предназначенные для выполнения таких задач, как регистрация пользователей или добавление комментариев, уязвимы для этой атаки.

Идея заключается в том, чтобы предотвратить отправку значений формы за пределы нашего приложения. Чтобы исправить это, мы генерируем случайное специальное слово (токен) в каждой форме, добавляем токен в сеансе и затем проверяем токен, как только форма отправляет данные обратно в наше приложение, сравнивая сохраненный токен в сеансе с тем, который был отправлен формой:

<form method='post' action='session/login'>

    <!-- Login and password inputs ... -->

    <input type='hidden' name='<?php echo $this->security->getTokenKey() ?>'
        value='<?php echo $this->security->getToken() ?>'/>

</form>

Затем в действии контроллера вы можете проверить, является ли токен CSRF действительным:

<?php

use Phalcon\Mvc\Controller;

/**
 * @property Request  $request
 * @property Security $security
 */
class SessionController extends Controller
{
    public function loginAction()
    {
        if ($this->request->isPost()) {
            if ($this->security->checkToken()) {
                // OK
            }
        }
    }
}

Примечание: важно помнить, что вам нужно будет иметь действительную службу session , зарегистрированную в вашем контейнере инъекции зависимостей. В противном случае checkToken() не будет работать.

Добавление капчи в форму также рекомендуется, чтобы полностью избежать рисков этой атаки.

Функциональность

Hash

getDefaultHash() / setDefaultHash()

Геттер и сеттер для хэша по умолчанию, который будет использоваться компонентом. По умолчанию хэш имеет значение CRYPT_DEFAULT (0). Доступные варианты таковы:

  • CRYPT_BLOWFISH_A
  • CRYPT_BLOWFISH_X
  • CRYPT_BLOWFISH_Y
  • CRYPT_MD5
  • CRYPT_SHA256
  • CRYPT_SHA512
  • CRYPT_DEFAULT

hash()

Хэшируется как строка или пароль и возвращает хэшированную строку обратно. Второй параметр является необязательным и позволяет временно установить определенный рабочий фактор или проходы, которые переопределяют стандартный.

checkHash()

Принимает строку (обычно пароль), уже хэшированную строку (хэшированный пароль) и необязательную минимальную длину пароля. Он проверяет их оба и возвращает true, если они идентичны, и false в противном случае.

isLegacyHash()

Возвращает true, если переданная хэшированная строка является допустимым хэшем bcrypt.

HMAC

computeHmac()

Генерирует хэш-значение с помощью метода HMAC. Он использует метод hash_hmac PHP внутренне, поэтому все параметры, которые он принимает, такие же, как и hash_hmac.

Случайный

getRandom()

Возвращает объект Phalcon\Security\Random , который является защищенным экземпляром генератора случайных чисел. Этот компонент подробно описан ниже.

getRandomBytes() / setRandomBytes()

Методы геттера и сеттера для указания количества байтов, генерируемых псевдо-случайным генератором openssl. По умолчанию он равен 16.

getSaltBytes()

Генерирует псевдослучайную строку, которая будет использоваться в качестве соли для паролей. Он использует значение getRandomBytes() для длины строки. Однако он может быть переопределен переданным числовым параметром.

Token

getToken()

Генерирует псевдослучайное значение токена, которое будет использоваться в качестве входного значения при проверке CSRF.

getTokenKey()

Генерирует псевдослучайный ключ токена, который будет использоваться в качестве имени ввода при проверке CSRF.

getRequestToken()

Возвращает значение токена CSRF для текущего запроса.

checkToken()

Проверьте, совпадает ли токен CSRF, отправленный в запросе, с текущим сеансом in. Первый параметр - это ключ токена, а второй-значение токена. Он также принимает третий логический параметр destroyIfValid, который, если установлено значение true, уничтожит маркер, если метод возвращает true.

getSessionToken()

Возвращает значение токена CSRF в сеансе

destroyToken()

Удаляет значение токена и ключа CSRF из сеанса

Случайный

Класс Phalcon\Security\Random позволяет очень легко генерировать множество типов случайных данных для использования в солях, новых паролях пользователей, сеансовых ключах, сложных ключах, системах шифрования и т. д. Этот класс частично заимствует библиотеку SecureRandom из Ruby.

Он поддерживает следующие безопасные генераторы случайных чисел:

  • random_bytes
  • libsodium
  • openssl, libressl
  • /dev/urandom

Чтобы использовать вышеизложенное, вам нужно будет убедиться, что генераторы доступны в вашей системе. Например, чтобы использовать openssl, ваша установка PHP должна поддерживать его.

<?php

use Phalcon\Security\Random;

$random = new Random();

echo $random->hex(10);       // a29f470508d5ccb8e289
echo $random->base62();      // z0RkwHfh8ErDM1xw
echo $random->base64(16);    // SvdhPcIHDZFad838Bb0Swg==
echo $random->base64Safe();  // PcV6jGbJ6vfVw7hfKIFDGA
echo $random->uuid();        // db082997-2572-4e2c-a046-5eefe97b1235
echo $random->number(256);   // 84
echo $random->base58();      // 4kUgL2pdQMSCQtjE

base58()

Генерирует случайную строку base58. Если параметр $len не указан, то предполагается 16 . В будущем он может стать еще больше. Результат может содержать буквенно-цифровые символы, кроме 0 (ноль), O (заглавная буква o), I (заглавная буква i) и l (строчная буква L).

Он похож на base64(), но был изменен, чтобы избежать как не буквенно-цифровых символов, так и букв, которые могут выглядеть неоднозначно при печати.

<?php

use Phalcon\Security\Random;

$random = new Random();

echo $random->base58(); // 4kUgL2pdQMSCQtjE

base62()

Генерирует случайную строкуbase62. Если параметр $len не указан, то предполагается 16. В будущем он может стать еще больше. Он похож на base58(), но был изменен, чтобы обеспечить наибольшее значение, которое можно безопасно использовать в URL-адресах без необходимости принимать во внимание дополнительные символы, потому что это [A-Za-z0-9]

<?php

use Phalcon\Security\Random;

$random = new Random();

echo $random->base62(); // z0RkwHfh8ErDM1xw

base64()

Генерирует случайную строку base64. Если параметр $len не указан, то предполагается 16. В будущем он может стать еще больше. Длина результирующей строки обычно больше $len. Формула размера такова:

4 * ($len / 3) округляется до кратного 4.

<?php

use Phalcon\Security\Random;

$random = new Random();

echo $random->base64(12); // 3rcq39QzGK9fUqh8

base64Safe()

Генерирует URL-адрес безопасной случайной строки base64. Если параметр $len не указан, то предполагается 16. В будущем он может стать еще больше. Длина результирующей строки обычно больше $len.

По умолчанию заполнение не генерируется, поскольку = может использоваться в качестве разделителя URL-адресов. Результат может содержать A-Z, a-z, 0-9, - и _. = также используется, если $padding имеет значение true. См. RFC 3548 для определения URL-safe base64.

<?php

use Phalcon\Security\Random;

$random = new Random();

echo $random->base64Safe(); // GD8JojhzSTrqX7Q8J6uug

bytes()

Генерирует случайную двоичную строку и принимает в качестве входных данных целое число, представляющее возвращаемую длину в байтах. Если $len не указан, то предполагается 16. В будущем он может стать еще больше. Результат может содержать любой байт: x00 - xFF.

<?php

use Phalcon\Security\Random;

$random = new Random();

$bytes = $random->bytes();
var_dump(bin2hex($bytes));
// Возможный выход: string(32) "00f6c04b144b41fad6a59111c126e1ee"

hex()

Генерирует случайную шестнадцатеричную строку. Если $len не указан, то предполагается 16. В будущем он может стать еще больше. Длина результирующей строки обычно больше $len.

<?php

use Phalcon\Security\Random;

$random = new Random();

echo $random->hex(10); // a29f470508d5ccb8e289

uuid()

Генерирует случайный UUID v4 (универсальный уникальный идентификатор). UUID версии 4 является чисто случайным (за исключением версии). Он не содержит значимой информации, такой как MAC-адрес, время и т. д. Смотрите RFC 4122 для получения подробной информации о UUID.

Этот алгоритм устанавливает номер версии (4 бита), а также два зарезервированных бита. Все остальные биты (остальные 122 бита) задаются с использованием случайного или псевдослучайного источника данных. UUID версии 4 имеют вид xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx , где x-любая шестнадцатеричная цифра, а y -одна из 8, 9, A, или B (например., f47ac10b-58cc-4372-a567-0e02b2c3d479). *

<?php

use Phalcon\Security\Random;

$random = new Random();

echo $random->uuid(); // 1378c906-64bb-4f81-a8d6-4ae1bfcdec22

Инъекция Зависимостей

Если вы используете контейнер Phalcon\Di\FactoryDefault , то Phalcon\Security уже зарегистрирован для вас. Однако вы можете переопределить регистрацию по умолчанию, чтобы установить свой собственный  workFactor(). В качестве альтернативы, если вы не используете Phalcon\Di\FactoryDefault, а вместо этого используете Phalcon\Di, регистрация будет такой же. Сделав это, вы сможете получить доступ к своему объекту конфигурации из контроллеров, моделей, представлений и любого компонента, реализующего Injectable.

Пример регистрации сервиса а также доступа к нему приведен ниже:

<?php

use Phalcon\Di\FactoryDefault;
use Phalcon\Security;

// Create a container
$container = new FactoryDefault();

$container->set(
    'security',
    function () {
        $security = new Security();

        $security->setWorkFactor(12);

        return $security;
    },
    true
);

В приведенном выше примере setWorkFactor() устанавливает коэффициент хэширования паролей равным 12 раундам.

Этот компонент теперь доступен в ваших контроллерах с помощью ключа безопасности security

<?php

use Phalcon\Mvc\Controller;
use Phalcon\Security;

/**
 * @property Security $security
 */
class MyController extends Controller
{
    private function getHash(string $password): string
    {
        return $this->security->hash($password);
    }
}

Также в ваших представлениях (синтаксис Volt)

{{ security.getToken() }}

 

Внешние ресурсы

  • Vökuró, iэто пример приложения, которое использует компонент безопасности для предотвращения CSRF и хэширования паролей, Github