В этой статье я объясню, как создать класс PHP, который будет шифровать и дешифровать любые данные с заданным паролем. Это объект программируется и использует существующие алгоритмы PHP.
Вступление
Подумайте, для чего нам может понадобиться такой класс? Мы хотим зашифровать важные данные с помощью пароля в целях безопасности. Мы также хотим, как уже упоминалось, иметь возможность расшифровывать эти данные при необходимости. Почему вы должны использовать симметричные алгоритмы? Это просто; когда вы предлагаете пароль, отправленный по электронной почте или что-то в этом роде, вам необходимо отправить пароль в виде открытого текста. Алгоритмы хеширования необратимы. После того как вы хешировали строку, вы не можете расшифровать исходный текст из хеша.
Может быть, вы уже слышали о MD5? Это больше не лучший вариант, потому что он небезопасен. В Интернете есть базы данных, о которых я не хочу упоминать, которые можно использовать для извлечения открытого текста из хеша, просто введя хеш в поле поиска. Таким образом, вы должны использовать что-то вроде SHA, которое было разработано NSA (Агентство национальной безопасности). SHA является аббревиатурой от Secure Hash Algorithm и является одним из самых безопасных алгоритмов хеширования. Есть и другие, такие как WHIRLPOOL, PANAMA и RIPEMD, но в настоящее время SHA является безопасным стандартом для хэшей и используется во многих приложениях.
Шаг 1: Подготовка
Я думаю, что важно создать интерфейс. Это потому, что мы всегда можем не задумываясь использовать методы, которые определены в интерфейсе, при создании экземпляра объекта класса, который реализует этот интерфейс.
Когда класс реализует интерфейс, он должен реализовывать методы, указанные в этом интерфейсе, иначе будет ошибка! Итак, вот пример:
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.
Хорошая ошибка, верно? Таким образом, вы можете быть уверены, что у классов действительно есть методы!
Шаг 2: Пароль для шифрования и дешифрования
Как я уже говорил, мы хотим иметь возможность использовать определенный пароль для шифрования и дешифрования. Этот пароль должен быть доступен для функций шифрования и дешифрования, поэтому мы определим переменную экземпляра, называемую ключом, которая передается конструктору. Определение $Key
необходимо только в классе rypter
:
1
|
private $Key;
|
Однако определение конструктора должно быть в интерфейсе. Следовательно, это также необходимо в классе, потому что мы должны реализовать все, что мы определили в интерфейсе. Интерфейс будет содержать:
1
|
public function __construct($Key);
|
и класс:
1
|
public function __construct($Key){ … }
|
Теперь, когда мы знаем, что получили ключ, мы можем использовать его для шифрования и дешифрования!
Шаг 3: Конструктор
В конструкторе мы должны установить ключ и выбрать алгоритм. Мы будем использовать алгоритм 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.
Шаг 4: теперь к шифрованию
Первое, что нужно проверить: есть ли данные для шифрования? Если нет, вы можете пойти дальше и сломать шифрование. Если вы хотите использовать любые другие режимы шифрования, вам нужно добавить следующий код.
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 предоставляет эти безопасные символы, поэтому мы его используем. Мы не знаем, что будет сделано с данными после шифрования.
Шаг 5: Расшифровка — это обратное шифрование
Мы снова задаем тот же первый вопрос. Есть ли данные? Если есть, вы должны 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, и вы можете шифровать и дешифровать столько раз, сколько пожелаете! Загрузите полный исходный код с хорошим примером, если вы не хотите вводить его самостоятельно. Я надеюсь, что вам понравилась эта статья.