Base64 неправильно кодирует/декодирует

У меня есть веб-сайт Pastebin , это минималистичная версия оригинального Pastebin, но у меня возникли проблемы с вставкой кода. Мой сервер его не примет.

Я попробовал закодировать его в base64, и это сработало, и сохранил данные как base64, и когда я приступил к декодированию с помощью инструмента, он декодировался идеально, но когда я просматриваю его по сгенерированной ссылке на вставку, он отображается как �������Y� ��T��T��ԑTUQT, и я не могу разберись. У меня есть в основном рабочая версия, которая прекрасно кодируется и декодируется, но она не просто отображает код, а отображает его с HTML-кодом. Поэтому, если бы в моем коде был <h1>Test</h1>, он отображал бы его как заголовок, а не как простой код. Я попытался найти это снова, но в моих редакциях оно потерялось. Ниже у меня есть текущий (не работающий) код с ошибками кодирования/декодирования/отображения base64, я не могу понять, какие именно.

JS

    window.onload = function() {
        document.querySelector('#pasteForm').addEventListener('submit', function(e) {
            e.preventDefault();
            var textArea = document.querySelector('#textToPaste');
            var formData = new FormData(e.target);

            var text = formData.get('text').replace(/\n/g, '<br>').replace(/^ /gm, '&nbsp;').replace(/^\t/gm, '&emsp;');
            var encodedText = btoa(text); // Encode the text in base64
            formData.set('text', '<pre>' + encodedText + '</pre>'); // Use the encoded text

            var xhr = new XMLHttpRequest();
            xhr.open('POST', 'create.php', true);

            xhr.onload = function() {
                if (xhr.status === 200) {
                    var password = formData.get('password');
                    if (password) {
                        document.querySelector('#linkBox').value = xhr.responseURL + '?password=' + encodeURIComponent(password);
                    } else {
                        document.querySelector('#linkBox').value = xhr.responseURL;
                    }
                    document.querySelector('#overlay').style.display = 'block';
                    document.querySelector('#popup').style.display = 'block';
                } else {
                    console.error('An error occurred');
                }
            };

            xhr.send(formData);
        });

        document.querySelector('#closeButton').addEventListener('click', function() {
            document.querySelector('#overlay').style.display = 'none';
            document.querySelector('#popup').style.display = 'none';
        });

        document.querySelector('#copyButton').addEventListener('click', function() {
            var linkBox = document.querySelector('#linkBox');
            linkBox.select();
            document.execCommand('copy');

            var copyButtonIcon = document.querySelector('#copyButton img');
            var originalIconSrc = copyButtonIcon.src;
            copyButtonIcon.src = 'https://cdn.cirrus.center/main/icons/check.png';

            setTimeout(function() {
                copyButtonIcon.src = originalIconSrc;
            }, 2000);
        });
    };

PHP

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!file_exists('paste')) {
        mkdir('paste');

        $redirectIndex = '<?php' . PHP_EOL;
        $redirectIndex .= 'header("Location: ..");' . PHP_EOL;
        $redirectIndex .= 'exit;' . PHP_EOL;
        file_put_contents('paste/index.php', $redirectIndex);
    }

    $dir = uniqid();
    mkdir("paste/$dir");

    $decodedText = '';
    if (isset($_POST['text']) && $_POST['text'] !== '') {
        $decodedText = base64_decode($_POST['text']); // Decode the base64 text
        $decodedText = mb_convert_encoding($decodedText, 'UTF-8', 'auto'); // Convert the encoding to UTF-8
    }

    $index = '<?php' . PHP_EOL;

    if (isset($_POST['password']) && $_POST['password'] !== '') {
        $index .= 'if (!isset($_GET["password"]) || $_GET["password"] !== "' . $_POST['password'] . '") {' . PHP_EOL;
        $index .= '    echo "<h1>Incorrect password</h1>";' . PHP_EOL;
        $index .= '    exit;' . PHP_EOL;
        $index .= '}' . PHP_EOL;
    }

    $index .= 'echo <<<EOT' . PHP_EOL; // Use the decoded text
    $index .= $decodedText . PHP_EOL;
    $index .= 'EOT;' . PHP_EOL;

    if (isset($_POST['burn'])) {
        $index .= 'file_put_contents(__FILE__, "<h1>This paste has been deleted</h1>");' . PHP_EOL;
        $index .= 'exit;' . PHP_EOL;
    }

    $index .= '?>' . PHP_EOL;

    $index .= '<!DOCTYPE html>' . PHP_EOL;
    $index .= '<html>' . PHP_EOL;
    $index .= '<head>' . PHP_EOL;
    $index .= '    <title>' . ($_POST['title'] ?? 'Pastebin') . '</title>' . PHP_EOL;
    $index .= '</head>' . PHP_EOL;
    $index .= '<body>' . PHP_EOL;
    $index .= '' . PHP_EOL;
    $index .= '</body>' . PHP_EOL;
    $index .= '</html>';

    file_put_contents("paste/$dir/index.php", $index);

    header("Location: /paste/$dir");
    echo "/paste/$dir";
}

Мне неясна причина использования кодировки base64, но, возможно, я что-то упускаю. Я не вижу никаких преимуществ в этом. Кроме того, это не имеет отношения к делу, но я редко вижу хорошее использование mb_convert_encoding, и я не думаю, что это тоже так.

Chris Haas 10.05.2024 18:31

Люди оставляли отзывы о том, что при вставке кода они получают ошибку 418 (странно, потому что 418 был добавлен как первоапрельская шутка, поэтому я предполагаю, что он пытается помешать людям выполнить вредоносный код на сервере), но сохраняет его как base64 устранит эту ошибку. Единственная проблема, с которой я столкнулся, это правильное отображение.

Caelen Cater 10.05.2024 19:13

Хорошо, это имеет смысл, спасибо за разъяснения. Я пытаюсь следовать вашей логике, но все равно немного запутался. В JS вы считаете ошибкой все, что не имеет статуса 200, но в PHP вы вызываете header() с директивой Location, которая по умолчанию имеет значение 302. Кроме того, когда вы вручную проверяете сгенерированный PHP, выглядит ли он правильно? И я игнорирую визуализированный вывод и сосредотачиваюсь только на серверной части.

Chris Haas 10.05.2024 20:03

Кроме того, вы разрешаете публичную отправку текста, который интерпретируется как чистый PHP, и его очень легко использовать. Пример смотрите в файле, который начинается с 663e. Задумывались ли вы о последствиях этого для безопасности? Нужно ли его выполнять? Можно ли его просто записать в файловую систему?

Chris Haas 10.05.2024 20:23

Что касается вашего первого ответа, я получил только 200 и 418. Думаю, тогда да, пока что все, кроме 200, является ошибкой, но опять же, оно ограничено 418. Да, в целом PHP выглядит правильно. Он работает для всего, кроме кода. Что касается вашего второго ответа, то на самом деле это была одна из главных вещей, о которых я думал при его создании.

Caelen Cater 10.05.2024 20:32

Вы видели мой образец и что я сделал? Кроме того, бороться с этим в JS, вероятно, не очень хорошая идея, потому что злые люди просто будут обходить JS и POST напрямую.

Chris Haas 10.05.2024 20:35

Я не видел вашего примера, но код не является исполняемым.

Caelen Cater 10.05.2024 20:39

Это хорошо, значит, приведенный выше пример кода необходимо обновить? Потому что раньше мне точно приходилось phpinfo() бегать. Кроме того, не забудьте сохранить заголовок, потому что я могу заставить PHP выполняться там.

Chris Haas 10.05.2024 21:13

И... пока я здесь, также храните свой пароль, который также разрешает использование PHP. Мне только что phpinfo() снова пришлось работать. Linux baroness-blood 5.4.32-grsec-grsec-plus+ #1 SMP Sat Apr 18 03:13:45 UTC 2020 x86_64

Chris Haas 10.05.2024 21:14

Строки JavaScript хранятся в кодировке UTF-16. Чтобы получить UTF-8, вам придется преобразовать строку самостоятельно, например. как в ecmanaut.blogspot.com/2006/07/… . Попробуйте var encodedText = btoa( unescape( encodeURIComponent( text)));

JosefZ 10.05.2024 21:42

Отметил благодарности. Буду реализовывать это для моего следующего коммита.

Caelen Cater 10.05.2024 23:10

@CaelenCater, просто предупреждение: использование htmlspecialchars, как это сейчас делает ваш код, все еще недостаточно. Все три месторождения по-прежнему пригодны для эксплуатации. На самом деле я бы рекомендовал записать необработанный текст в специальный файл и вызвать file_get_contents с помощью echo. В качестве пароля используйте OpenSSL или Sodium для шифрования полезных данных, не сохраняйте пароль. Если расшифровка не удалась, то показать «неверный пароль».

Chris Haas 17.05.2024 15:54

Я рассмотрю этот вопрос подробнее, но некоторые вещи могут оказаться невозможными на моем общем сервере. Спасибо за все ваши отзывы!

Caelen Cater 21.05.2024 01:09
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
13
116
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот что я вижу, попробуйте и дайте мне знать. Я могу пересмотреть этот ответ по мере необходимости.

В javascript вы, кажется, передаете поле формы следующим образом:

formData.set('text', '<pre>' + encodedText + '</pre>'); // Use the encoded text

Затем он отправляет форму на серверный PHP-код, и поле формы «текст» декодируется следующим образом:

$decodedText = base64_decode($_POST['text']); // Decode the base64 text
$decodedText = mb_convert_encoding($decodedText, 'UTF-8', 'auto'); // Convert the encoding to UTF-8

Итак, несколько потенциальных проблем:

1-Я думаю, вам нужно удалить теги «pre» при кодировании, поэтому:

formData.set('text', encodedText); // Use the encoded text

Вы можете поместить теги pre в PHP, чтобы текст отображался правильно:

$index .= '<pre>' . $decodedText . '</pre>' . PHP_EOL;

2-На стороне JavaScript вы используете btoa, но на стороне PHP вы используете base64_decode. Эти две команды, возможно, могут быть несовместимы, поэтому вы можете использовать инструмент, чтобы убедиться, что PHP base64_decode правильно декодирует btoa javascript. Вы упоминаете использование инструмента для проверки, но могут быть случаи, когда он иногда декодируется правильно, а иногда нет из-за тонких проблем несовместимости, поэтому полезно иметь это в виду.

3-Не уверен, что есть необходимость использовать mb_convert_encoding. Попробуйте удалить его.

Дайте мне знать, что работает для вас.

*** Обновлять: Смотрите комментарии. Теги Pre в javascript вызывали искажение текста, их просто нужно было удалить. После этого сервер генерировал ошибку 418, поскольку помечал строку PHP, выполняющую декодирование, обнаруживая уязвимость безопасности. Они обращаются к своему провайдеру серверов, чтобы решить эту проблему.

Я думаю, что его нужно сохранить в конечном файле как base64, потому что в противном случае я получаю ошибку 418 «Я чайник». Спасибо, что нашли время помочь мне с этим!

Caelen Cater 10.05.2024 19:40

Однако проблема здесь в том, что base64 должна быть идеальной кодировкой для использования в сообщениях форм и даже в параметрах запроса URL, поэтому не уверен, почему сервер генерирует такую ​​странную ошибку. Я бы поискал другую причину ошибки 418. Если вы просто удалите эти теги и больше ничего не сделаете, возникнет ли ошибка? Проблема, которую я вижу при размещении предварительных тегов в поле, заключается в том, что я думаю, что он попытается декодировать текст тега в базе 64 вместе с закодированным текстом, и поэтому он всегда будет декодировать неправильно.

MarkCaldwell 10.05.2024 19:43

Я не знаю точно, в чем ошибка. Насколько я могу судить, когда я делаю вставку без какой-либо дополнительной кодировки, она просто возвращает ошибку 418, независимо от того, какой это был код. Когда я начал вставлять его, завернутый в теги pre и закодированный в Base 64, это позволяло мне вставлять большинство типов кода, и тогда единственной проблемой было его правильное отображение, чтобы он мог декодироваться и отображаться при загрузке. С учетом предложенных вами изменений он вернет ошибку 418, когда я вставлю код (даже в base64) и сохраню его как обычный текст. Полный код находится на github, если вы хотите немного поэкспериментировать.

Caelen Cater 10.05.2024 19:52

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

MarkCaldwell 10.05.2024 19:55

Мне удалось воспроизвести вашу проблему с искаженным текстом. Это действительно теги Pre. Избавьтесь от них из javascript, и на другом конце он должен правильно декодироваться. Как уже упоминалось, вы можете поместить теги Pre на конец PHP, чтобы текст HTML, представленный в поле, отображался на выходе как необработанные теги HTML. В моих тестах base64_decode и btoa не выявили проблем, так что, вероятно, это не проблема. Кажется, mb_convert_encoding не нужен и не повредит, когда я пробовал с ним и без него. Итак, попробуйте внести изменения в тег Pre, как я предлагаю здесь, и пойдем дальше.

MarkCaldwell 10.05.2024 20:41

Это не сильно изменилось, я все еще получаю ошибку 418 при вставке кода.

Caelen Cater 10.05.2024 20:53

Да, но теперь мы можем сосредоточиться на 418. Искаженный текст вызван тегами Pre, так что теперь давайте поработаем над этим 418. Можете ли вы точно увидеть, в какой момент происходит 418?

MarkCaldwell 10.05.2024 20:55

Одна вещь, которую вы можете сделать, — это временно изменить свои данные, чтобы просто использовать оператор предупреждения, чтобы выгружать результат обратно на экран, когда вы нажимаете кнопку «Отправить». Я сделал это, как показано ниже. Если у вас это заработает, просто начните медленно переделывать другой код, и вы увидите, где именно генерируется ошибка.

MarkCaldwell 10.05.2024 21:20

Вставьте это после вызова xhr.open, чтобы просто отображать результаты, и измените php, чтобы просто распечатать данные. Это поможет лучше понять, что вызывает ошибку. xhr.onreadystatechange = () => { if (xhr.readyState === 4) { alert(xhr.response); } };

MarkCaldwell 10.05.2024 21:23

Это не дало мне никакой ценной информации, но я нашел это в своем журнале ошибок: [Пт, 10 мая 12:55:34.551050 2024] [:error] [клиент 185.246.209.56:49102] [клиент 185.246.209.56] ModSecurity : Доступ запрещен с кодом 418 (этап 2). Соответствующая фраза «base64_decode» в ARGS:text. [файл "/etc/modsecurity/mod_sec3_CRS/99_dreamhost_rules.conf"] [строка "126"] [id "1990070"] [msg "Общие известные аргументы для оболочки бэкдора, присутствующие в ARGS:text"] [имя хоста "paste-bin. us"] [uri "/create.php"], реферер: Paste-bin.us

Caelen Cater 10.05.2024 22:06

Ах-ха! Итак, ваш провайдер сервера сканирует ваш код и, похоже, помечает эту строку: $decodedText = base64_decode($_POST['text']); Попробуйте извлечь ссылку _POST из этой строки и использовать вместо нее переменную и передать ее, т. е. $formVar = $_POST['text'] и $decodedText = base64_decode($formVar); Возможно, вам придется обратиться к администратору вашего сервера, чтобы решить эту проблему.

MarkCaldwell 10.05.2024 22:31

Вот ссылка, по которой, похоже, другие сталкиваются с такой же проблемой: securitycheck.protegetuordenador.com/forum/9-newbies-area/…

MarkCaldwell 10.05.2024 22:31

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

MarkCaldwell 10.05.2024 22:32

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