Что вызывает эти символы?

#php #encryption #encoding

#php #шифрование #кодирование

Вопрос:

У меня есть страница PHP, которая перебирает CSV-файл и шифрует столбец «email», используя следующую функцию:

 function my_encrypt($data, $key)
{
    // Remove the base64 encoding from our key
    $encryption_key = base64_decode($key);
    // Generate an initialization vector
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    // Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
    $encrypted = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, 0, $iv);
    // The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
    return base64_encode($encrypted . '::' . $iv);
}
  

В другой части приложения я расшифровываю возвращаемое значение с помощью:

 function my_decrypt($data, $key)
{
    // Remove the base64 encoding from our key
    $encryption_key = base64_decode($key);
    // To decrypt, split the encrypted data from our IV - our unique separator used was "::"
    list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
    return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}
  

По большей части все это работает гладко, но время от времени расшифрованное значение возвращается с несколькими странными символами.

Например: rsmi3�6CTΣ%mecompany.com был возвращен вместо rsmith@somecompany.com .

Я не уверен, что это плохой ввод или вывод, но я предполагаю, что это как-то связано с загруженным файлом CSV … проблема с кодировкой? Что означают эти символы и при каких условиях они создаются?

Обновить

Вот код, который я использую для добавления зашифрованного значения в CSV:

 $file = fopen(get_stylesheet_directory() . "/emma_members.csv", "r"); //Open the old file for reading
$newFile = fopen(get_stylesheet_directory() . "/emma_members_new.csv", "w"); //Create a new file for writing

if (!$file) error_log('ERROR opening file');
if (!$newFile) error_log('ERROR creating file');

$columns = ['email', 'member_id', 'member_since', 'plaintext_preferred', 'bounce_count', 'status_name', 'last_modified_at', 'city', 'first_name', 'last_name', 'request-demo', 'job-function', 'title', 'country', 'current-ams', 'opt-in', 'address-2', 'unique-identifier', 'state', 'postal_code', 'web-address', 'address', 'phone-number', 'company', 'area-of-specialization', 'work-phone'];

while (($data = fgetcsv($file)) !== FALSE) {

    $row = array_combine($columns, $data);

    $email = "{$row['email']}";
    $uid = my_encrypt($email, ENCRYPT_KEY_1);

    $row['unique-identifier'] = $uid;
    $ret = fputcsv($newFile, array_values($row));
}
  

ОБНОВЛЕНИЕ 2

Итак, после долгого тестирования с тысячами электронных писем кажется my_encrypt , что функция возвращает некоторые неверные значения, в зависимости от ввода, конечно. Это произошло не с КАЖДЫМ адресом электронной почты, но даже 1 слишком много для моего варианта использования.

Я даже пытался избавиться от :: между данными и iv, но это тоже не сработало (хотя, возможно, я сделал это неправильно).

В любом случае, я закончил тем, что использовал следующую функцию вместо нее, и все хорошо:

 function encrypt_decrypt($action, $string) {
    $output = false;

    $encrypt_method = "AES-256-CBC";
    $secret_key = PHRASE_1;
    $secret_iv = PHRASE_2;

    // hash
    $key = hash('sha256', $secret_key);
    
    // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
    $iv = substr(hash('sha256', $secret_iv), 0, 16);

    if ( $action == 'encrypt' ) {
        $output = openssl_encrypt($string, $encrypt_method, $key, 0, $iv);
        $output = base64_encode($output);
    } else if( $action == 'decrypt' ) {
        $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv);
    }

    return $output;
}
  

Комментарии:

1. Единственная проблема с опубликованным вами кодом заключается в том, что строка :: вполне может естественным образом встречаться в зашифрованном тексте или IV, нарушая декодирование. Просто соедините их без разделителя и используйте известную длину IV, чтобы разрезать строку. Что касается того, почему ваш вывод выглядит так … возможно, проблема с кодировкой? Вам нужно будет опубликовать остальную часть вашего кода и репрезентативный пример входных данных, которые мы можем использовать для воспроизведения проблемы, хотя это может быть проблематично, учитывая, что это адрес электронной почты

2. @Sammitch — Я опубликовал немного больше кода. К сожалению, я не могу опубликовать какие-либо реальные адреса электронной почты из своего списка. Я понимаю, что вы говорите о :: — может ли это быть причиной этого?

Ответ №1:

Я протестировал ваши функции шифрования и дешифрования, и они работают должным образом, поэтому причиной такого поведения, по-видимому, является другая кодировка файла на вашем устройстве.

Особенно при чтении CSV-файла, иногда устройство (Windows) изменяет кодировку, и вы получаете некоторые любопытные символы, подобные тем, которые вы показали. Я рекомендую читать файлы с другой кодировкой как кодировку по умолчанию (ISO …).

Я настраиваю живой пример, который «доказывает» правильность простой строки en- и расшифровки: https://paiza.io/projects/e/Y-1gy9Y3b-VAlXAMG4odng

Результат прост:

 plaintext:     rsmith@somecompany.com
ciphertext:    Y0RrMWRwR1pWeGtGbFdic3dIVmFzVmp4VUFYemJGdUhzMStRSll6akIwWT06Orf twLGopVa4083RckEw44=
decryptedtext: rsmith@somecompany.com
  

Вот код:

 <?php
function my_encrypt($data, $key)
{
    // Remove the base64 encoding from our key
    $encryption_key = base64_decode($key);
    // Generate an initialization vector
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    // Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
    $encrypted = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, 0, $iv);
    // The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
    return base64_encode($encrypted . '::' . $iv);
}

function my_decrypt($data, $key)
{
    // Remove the base64 encoding from our key
    $encryption_key = base64_decode($key);
    // To decrypt, split the encrypted data from our IV - our unique separator used was "::"
    list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
    return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}

$plaintext = 'rsmith@somecompany.com';
echo 'plaintext:     ' . $plaintext . PHP_EOL;
$encryptionKey = base64_encode(32);
$ciphertext = my_encrypt($plaintext, $encryptionKey);
echo 'ciphertext:    ' . $ciphertext . PHP_EOL;
$decryptedtext = my_decrypt($ciphertext, $encryptionKey);
echo 'decryptedtext: ' . $decryptedtext . PHP_EOL;

?>
  

Комментарии:

1. Исходный CSV-файл поступает из экспорта списка рассылки, поддерживаемого сторонним поставщиком. file -I говорит мне, что это закодировано как text/plain; charset=utf-8 . Нужно ли мне преобразовать его во что-то другое?

2. «UTF-8» звучит хорошо, поэтому похоже, что инициализирующий вектор каким-то образом изменен, поэтому некоторые символы были скремблированы. Попробуйте повторно сгенерировать зашифрованную строку с правильным адресом электронной почты и сравнить зашифрованный текст со строкой в csv-файле.