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

StackOverflow https://stackoverflow.com/questions/2900819

  •  04-10-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

Другие советы

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

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

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

Я вижу здесь три шага. Во-первых, попробуйте сжимать данные. С таким небольшим количеством данных 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);

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

Наконец, база 64 кодирует это. Я бы использовал модифицированную безопасную URL-безопасную базу 64, которое использует - и _ вместо + и / экономя от необходимости проводить адрес 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.

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

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

большой

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top