Безопасный неразборчивый URL-компонент в Perl с использованием симметричного шифрования?
-
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 всего с другой секретной солью, чтобы обнаружить вмешательство?
Я бы не использовал это для банковских данных, но, вероятно, было бы хорошо для большинства веб-вещей ...
большой