Безопасный компонент URL без возможности изменения в Perl, использующий симметричное шифрование? - PullRequest
10 голосов
/ 25 мая 2010

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

$ client имеет двоичные данные размером от 50 до 500 байтов, которые должны быть вставлены в середину URL-адреса и отправлены туда и обратно в браузер клиента. Так как это часть URL, мы столкнулись с «теоретическим» пределом в 1K для GET URL. Кроме того, $ client не хочет, чтобы его клиент расшифровал данные или подделал их без обнаружения. $ client также предпочел бы не хранить ничего на стороне сервера, поэтому это должно быть полностью автономно. Должен быть Perl-код и быстро, как в кодировании, так и в декодировании.

Я думаю, что последний шаг может быть base64. Но какие шаги для шифрования и хеширования имеют смысл?

Ответы [ 4 ]

5 голосов
/ 25 мая 2010

У меня есть код в приложении Cat, который использует Crypt::Util для кодирования / декодирования адреса электронной почты пользователя для ссылки для проверки электронной почты.

Я настроил Crypt::Util модель, используя Catalyst::Model::Adaptor с секретным ключом. Тогда в моем контроллере у меня есть следующая логика на стороне отправки:

my $cu = $c->model('CryptUtil');
my $token = $cu->encode_string_uri_base64( $cu->encode_string( $user->email ) );
my $url = $c->uri_for( $self->action_for('verify'), $token );

Я отправляю эту ссылку на $user->email и при нажатии на нее я использую следующее.

my $cu = $c->model('CryptUtil');
if ( my $id = $cu->decode_string( $cu->decode_string_uri_base64($token) ) ) {
    # handle valid link
} else { 
    # invalid link
}

Это в основном то, что edanite только что предложил в другом ответе. Вам просто нужно убедиться, что все данные, которые вы используете для формирования токена, не будут превышать ваш конечный предел $url.

4 голосов
/ 25 мая 2010

Создайте секретный ключ и сохраните его на сервере. Если существует несколько серверов и запросы не гарантированно возвращаются на один и тот же сервер; вам нужно будет использовать один и тот же ключ на каждом сервере. Этот ключ следует периодически поворачивать.

Если вы шифруете данные в режиме CBC (Cipher Block Chaining) (см. Модуль Crypt :: CBC), накладные расходы на шифрование составляют максимум два блока (один для IV и один для заполнения). 128-битные (то есть 16-байтовые) блоки являются общими, но не универсальными. Я рекомендую использовать AES (он же Rijndael) в качестве блочного шифра.

Вам необходимо аутентифицировать данные, чтобы убедиться, что они не были изменены. В зависимости от безопасности приложения, может быть достаточно простого хэширования сообщения и включения хэша в открытый текст, который вы шифруете. Это зависит от того, что злоумышленники не могут изменить хеш для соответствия сообщению, не зная симметричного ключа шифрования. Если вы используете 128-битные ключи для шифра, используйте 256-битный хеш, такой как SHA-256 (вы можете использовать модуль Digest для этого). Вы также можете включить в данные некоторые другие вещи, такие как отметку времени, чтобы предотвратить повторение запроса несколько раз.

3 голосов
/ 25 мая 2010

Я вижу три шага здесь. Сначала попробуйте сжать данные. С таким небольшим количеством данных bzip2 может сэкономить вам 5-20%. Я бы добавил охранника, чтобы убедиться, что он не увеличит объем данных. Этот шаг может не стоить.

use Compress::Bzip2 qw(:utilities);
$data = memBzip $data;

Вы также можете попробовать уменьшить длину любых ключей и значений в данных вручную. Например, first_name может быть уменьшено до fname.

Во-вторых, зашифруйте его. Выберите свой любимый шифр и используйте Crypt :: CBC. Здесь я использую Rijndael, потому что это достаточно хорошо для АНБ. Вы захотите провести сравнительный анализ, чтобы найти наилучший баланс между производительностью и безопасностью.

use Crypt::CBC;
my $key = "SUPER SEKRET";
my $cipher = Crypt::CBC->new($key, 'Rijndael');
my $encrypted_data = $cipher->encrypt($data);

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

Наконец, Base 64 кодирует его. Я бы использовал модифицированную безопасную для URL базу 64, которая использует - и _ вместо + и /, избавляя вас от необходимости тратить место на URL, кодирующем эти символы в строке базы 64. MIME :: Base64 :: URLSafe охватывает это.

use MIME::Base64::URLSafe;
my $safe_data = urlsafe_b64encode($encrypted_data);

Затем прикрепите его к URL, как хотите. Повторите процесс считывания в обратном порядке.

Вы должны быть в безопасности по размеру. Шифрование увеличит размер данных, но, вероятно, менее чем на 25%. Base 64 увеличит размер данных на треть (кодировка 2 ^ 6 вместо 2 ^ 8). Это должно обеспечить комфортное кодирование 500 байтов внутри 1K.

0 голосов
/ 25 мая 2010

Насколько безопасным это должно быть? Не могли бы вы просто зашифровать данные длинной случайной строкой, а затем добавить MD5-хэш всего лота с другой секретной солью для обнаружения вмешательства?

Я бы не использовал это для банковских данных, но, вероятно, было бы хорошо для большинства веб-вещей ...

большой

...