Статьи

Создание класса Crypter с PHP

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


Подумайте, для чего нам может понадобиться такой класс? Мы хотим зашифровать важные данные с помощью пароля в целях безопасности. Мы также хотим, как уже упоминалось, иметь возможность расшифровывать эти данные при необходимости. Почему вы должны использовать симметричные алгоритмы? Это просто; когда вы предлагаете пароль, отправленный по электронной почте или что-то в этом роде, вам необходимо отправить пароль в виде открытого текста. Алгоритмы хеширования необратимы. После того как вы хешировали строку, вы не можете расшифровать исходный текст из хеша.

Может быть, вы уже слышали о MD5? Это больше не лучший вариант, потому что он небезопасен. В Интернете есть базы данных, о которых я не хочу упоминать, которые можно использовать для извлечения открытого текста из хеша, просто введя хеш в поле поиска. Таким образом, вы должны использовать что-то вроде SHA, которое было разработано NSA (Агентство национальной безопасности). SHA является аббревиатурой от Secure Hash Algorithm и является одним из самых безопасных алгоритмов хеширования. Есть и другие, такие как WHIRLPOOL, PANAMA и RIPEMD, но в настоящее время SHA является безопасным стандартом для хэшей и используется во многих приложениях.


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

Когда класс реализует интерфейс, он должен реализовывать методы, указанные в этом интерфейсе, иначе будет ошибка! Итак, вот пример:

1
2
3
4
5
6
7
8
9
interface ICrypter{
    public function Encrypt($data);
    public function Decrypt($data);
}
 
class Crypter implements ICrypter{
    public function Encrypt($data){ … }
    public function Decrypt($data){ … }
}

Как видите, интерфейс инструктирует классы, которые реализуют ICrypter иметь публичную функцию Encrypt с одним параметром $data . Открытая функция Decrypt также имеет параметр $ data . Вы можете попробовать это; если в классе отсутствует один из указанных методов интерфейса, вы получите фатальную ошибку. Вот пример:

Неустранимая ошибка: класс Crypter содержит 1 абстрактный метод и поэтому должен быть объявлен как абстрактный или реализовать оставшиеся методы (ICrypter :: Decrypt) в C: \ www \ Nettuts \ Crypter \ crypter.php в строке 32.

Хорошая ошибка, верно? Таким образом, вы можете быть уверены, что у классов действительно есть методы!


Как я уже говорил, мы хотим иметь возможность использовать определенный пароль для шифрования и дешифрования. Этот пароль должен быть доступен для функций шифрования и дешифрования, поэтому мы определим переменную экземпляра, называемую ключом, которая передается конструктору. Определение $Key необходимо только в классе rypter :

1
private $Key;

Однако определение конструктора должно быть в интерфейсе. Следовательно, это также необходимо в классе, потому что мы должны реализовать все, что мы определили в интерфейсе. Интерфейс будет содержать:

1
public function __construct($Key);

и класс:

1
public function __construct($Key){ … }

Теперь, когда мы знаем, что получили ключ, мы можем использовать его для шифрования и дешифрования!


В конструкторе мы должны установить ключ и выбрать алгоритм. Мы будем использовать алгоритм Blowfish для этого примера и использовать его в качестве стандартного значения. Я объясню немного больше о симметричных алгоритмах позже в тексте, но для простоты мы будем использовать Blowfish. Вы можете изменить это позже, если хотите. Поэтому нам нужна еще одна переменная экземпляра с именем Algo:

1
private $Algo;

и конструктор …

1
2
3
4
public function __construct($Key, $Algo = MCRYPT_BLOWFISH){
    $this->Key = substr($Key, 0, mcrypt_get_key_size($Algo, MCRYPT_MODE_ECB));
    $this->Algo = $Algo;
}

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

Теперь мы даем нашей переменной экземпляра Key правильный ключ для алгоритма и назначаем нашу переменную экземпляра Algo.

Итак, теперь у нас есть конструктор. Как я уже говорил ранее, вы можете изменить стандартное значение Algo на любой другой алгоритм, поддерживаемый MCrypt.

Список поддерживаемых алгоритмов от php.net:

  • MCRYPT_3DES
  • MCRYPT_ARCFOUR_IV (только libmcrypt> 2.4.x)
  • MCRYPT_ARCFOUR (только libmcrypt> 2.4.x)
  • MCRYPT_BLOWFISH
  • MCRYPT_CAST_128
  • MCRYPT_CAST_256
  • MCRYPT_CRYPT
  • MCRYPT_DES
  • MCRYPT_DES_COMPAT (только libmcrypt 2.2.x)
  • MCRYPT_ENIGMA (только libmcrypt> 2.4.x, псевдоним для MCRYPT_CRYPT)
  • MCRYPT_GOST
  • MCRYPT_IDEA (не бесплатно)
  • MCRYPT_LOKI97 (только libmcrypt> 2.4.x)
  • MCRYPT_MARS (только libmcrypt> 2.4.x, не бесплатно)
  • MCRYPT_PANAMA (только libmcrypt> 2.4.x)
  • MCRYPT_RIJNDAEL_128 (только libmcrypt> 2.4.x)
  • MCRYPT_RIJNDAEL_192 (только libmcrypt> 2.4.x)
  • MCRYPT_RIJNDAEL_256 (только libmcrypt> 2.4.x)
  • MCRYPT_RC2
  • MCRYPT_RC4 (только libmcrypt 2.2.x)
  • MCRYPT_RC6 (только libmcrypt> 2.4.x)
  • MCRYPT_RC6_128 (только libmcrypt 2.2.x)
  • MCRYPT_RC6_192 (только libmcrypt 2.2.x)
  • MCRYPT_RC6_256 (только libmcrypt 2.2.x)
  • MCRYPT_SAFER64
  • MCRYPT_SAFER128
  • MCRYPT_SAFERPLUS (только libmcrypt> 2.4.x)
  • MCRYPT_SERPENT (только libmcrypt> 2.4.x)
  • MCRYPT_SERPENT_128 (только libmcrypt 2.2.x)
  • MCRYPT_SERPENT_192 (только libmcrypt 2.2.x)
  • MCRYPT_SERPENT_256 (только libmcrypt 2.2.x)
  • MCRYPT_SKIPJACK (только libmcrypt> 2.4.x)
  • MCRYPT_TEAN (только libmcrypt 2.2.x)
  • MCRYPT_THREEWAY
  • MCRYPT_TRIPLEDES (только libmcrypt> 2.4.x)
  • MCRYPT_TWOFISH (для более старых версий mcrypt 2.x или mcrypt> 2.4.x)
  • MCRYPT_TWOFISH128 (TWOFISHxxx доступны в более новых версиях 2.x, но не в версиях 2.4.x)
  • MCRYPT_TWOFISH192
  • MCRYPT_TWOFISH256
  • MCRYPT_WAKE (только libmcrypt> 2.4.x)
  • MCRYPT_XTEA (только libmcrypt> 2.4.x)

Так что же нам следует использовать, когда мы хотим использовать класс Crypter в наших продуктах? На данный момент AES является стандартом симметричных алгоритмов. Он используется во многих приложениях, но где AES? AES был первоначально опубликован как Rijndael, который включен в список. Это действительно быстрый, но безопасный алгоритм и даже быстрый с 256-битным размером ключа. Мой совет — использовать MCRYPT_RIJNDAEL_256 для ваших приложений. В качестве примера, AES используется в WPA2, который является стандартом безопасности для WLAN.


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

1
2
$iv_size = mcrypt_get_iv_size($this->Algo, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

Этот $iv используется, например, в CBC, CFB, OFB и в некоторых алгоритмах в режиме шифрования STREAM. Если параметр не передан в этих режимах, $iv будет установлен в '\0' . Следующим шагом является шифрование данных с помощью простой функции mcrypt_encrypt. Здесь нам нужен наш алгоритм, ключ, данные и режим шифрования. $ iv не является обязательным.

1
$crypt = mcrypt_encrypt($this->Algo, $this->Key, $data, MCRYPT_MODE_ECB, $iv);

Наконец, закодируйте зашифрованные данные с помощью base64_encode и обрежьте их, прежде чем вернуть.

1
return trim(base64_encode($crypt));

Мы должны кодировать base64 зашифрованные данные, чтобы получить URL-Safe-данные. Это необходимо, потому что, если вы хотите использовать зашифрованные данные, например, в URL, у вас будут проблемы с ‘&’, поскольку это зарезервированный символ, указанный в RFC. Таким образом, вам нужно что-то вроде буквенно-цифровых символов — другими словами, символ, который является безопасным. Кодирование base64 предоставляет эти безопасные символы, поэтому мы его используем. Мы не знаем, что будет сделано с данными после шифрования.


Мы снова задаем тот же первый вопрос. Есть ли данные? Если есть, вы должны base64_decode данных, как мы ранее закодировали их с base64_encode.

1
$crypt = base64_decode($data);

Тогда необязательная часть с $ iv.

1
2
$iv_size = mcrypt_get_iv_size($this->Algo, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

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

1
$decrypt = mcrypt_decrypt($this->Algo, $this->Key, $crypt, MCRYPT_MODE_ECB, $iv);

Наконец верните урезанные и расшифрованные данные.

1
return trim($decrypt);

Определите глобальный Crypter. В этом примере мы будем использовать RIJNDAEL_256 (AES) с паролем «Любой пароль». После создания экземпляра вы вызываете свои функции или методы для проверки. Здесь мы вызываем функцию foo и метод foo1 .

1
2
3
4
5
6
$crypter = new Crypter(«Any password», MCRYPT_RIJNDAEL_256);
 
foo();
 
$foo = new Foo();
$foo->foo1();

Вы можете получить свой криптер из переменной Superglobal, которая называется $GLOBALS . Это ассоциативный массив, поэтому вы можете вызывать все свои глобальные переменные по имени, с которым вы их определили. Вы можете получить $crypter который определен вне блока foo или foo1, с помощью $GLOBALS["crypter"]

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
function foo(){
    …
    $encrypted = $GLOBALS[«crypter»]->Encrypt($data);
    $decrypted = $GLOBALS[«crypter»]->Decrypt($encrypted);
    …
}
 
class Foo{
    public function foo1(){
        …
        $encrypted = $GLOBALS[«crypter»]->Encrypt($data);
        $decrypted = $GLOBALS[«crypter»]->Decrypt($encrypted);
        …
    }
}

Теперь у вас есть полный класс Crypter, и вы можете шифровать и дешифровать столько раз, сколько пожелаете! Загрузите полный исходный код с хорошим примером, если вы не хотите вводить его самостоятельно. Я надеюсь, что вам понравилась эта статья.