Безопасный несанкционированный URL-компонент в Perl с использованием симметричного шифрования?

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

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

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

Ответ 1

У меня есть код в приложении 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 не превышают ваш произвольный предел.

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

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

большой