Практика хранение Cookies в БД.

Hartwell

Client
Регистрация
25.09.2014
Сообщения
194
Благодарностей
118
Баллы
43
Куки – это небольшие строки данных, которые хранятся непосредственно в браузере. Они являются частью HTTP-протокола, определённого в спецификации RFC 6265.
Мне потребовалось сохранять значения куки контейнера в СУБД mysql, и вот к чему я пришел.
По ходу текста встречаем *, сноски смотрим ниже текста основного.

И так, в хранении кукисов непосредственно в ячейке со строковым параметров если рассматривать реляционную СУБД mysql/mariadb у нас есть две основные проблемы.
  1. Спец. символы в cookie. Все возможные звездочки палочки, слеши, кавычки.
  2. Длинна данных строки. Приближаясь к реалии, любой слепок cookies netscape или иных двух форматах могут достигать приличных размеров.*
----- Рассмотрение специфики проблем
1. По спец. символам: мы можешь обработать строку перед добавлением в БД, выполнив операции escape спец символов, но честно говоря мне даже лениво задумываться над всеми "ЕСЛИ" которые содержит данный способ, сразу отбросил его.
А если спец символы?
А если выполнится подзапрос sql который DROP'нит все бд (мы ведь понятия не имеем что лежит в куках?
А если куки выйдут за рамки размера хранения ячейки в бд? (обрежем половину кук?) и тд.) **
2. По конечно хотелось бы все это уместить в varchar, но понятное дело что это на грани фантастики. Но точно никакие blob, hex, и другие специфичные типы данных в БД. Оптимальным будет след. после varchar - text ***

---- Реализация.

Не претендую на роль лучшего решения, в нем есть свои плюсы и минусы, но решение основных проблем покрываем полностью.
Снова к вопросу номер "1". Для адекватного и безопасного хранения разумным шагом будет привести к более пригодному формату для хранения там, где у нас имеется типизация и другие относительно строгие правила к передачи обработке данных. Первым сразу же приходит на ум - Base64, даже учитывая что он не идеальный**** но в данном случае он полностью приемлем, да и не особо хочется таскать зависимости в виде сторонних библиотек.

На первый взгляд казалось бы все, base64 encode/decode - таска выполнена. Перевод в base64 существенно увеличивает длинну данных (мы же используем меньший набор символов, всего из 64). В base58 - логично оно будет еще длиннее и так далее (так как набор включает 58 символов используемых)
Собственно пруф. 40 символов в base64 будет уже 58 занимать.
C#:
// Raw string:
Set-Coookie: Key=Value; domain=.goolge;/
// Base64encode of string below
U2V0LUNvb29raWU6IEtleT1WYWx1ZTsgZG9tYWluPS5nb29sZ2U7Lw==
Вспоминаем сколько кук может прилететь, понимаем что возможны проблемы с макс. допустимым размером и тд. (Вернее просто факт что оно может быть, и я максимально ленив чтобы считать, и разбираться в действительности хватит ли макс. допустимое под куки, да еще высчитывать base64, есть куда быстрее и интересней вариант чем все это моделировать).

Собрал все факты в уме, а именно: приемлемый формат который можно без пляски сохранить должен не содержать спец символов и тд, чем меньше доступный набор символов тем длиннее его запись что затронет второй вопрос *****. Вспоминаем какие есть варианты уменьшить размер данных? - конечно же компрессия.

Конечно же нам нужна без потерь, куки нам важны и хотим целыми сохранить, и целыми их получить в итоге.

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

------ Сноски:
* - есть утверждение об максимальном размере файла с куками браузера - 4кб (до ~300 пар Ключ=Значение), но в современных версиях оно может быть расширено до 8кб (~500 Пар ключ значение).
** - Костыль java'истов, encoding cookie fix- как факт, куки требуют доп. обработки. К тому-же у нас еще и СУБД
*** - Формат TEXT: представляет текст длиной до 65 КБ в mysql.
**** - В википеди на сколько помню можно прочитать про это все. Суть в том что base64 все же содержит символы которые в некоторых случаях при работе с вебом имеет проблемные символы. (имеется ввиду общий подход независимо от ПО/решения/реализации). Был придуман формат base64 для url решающий проблему при работе с веб адресами. В последствии вышел base58 который исключает проблемные символы, почти идеальный формат не содержащий управляющие символы и тд.
***** - Для тех кто не въехал, причем же тут какие-то base58/64, наборы символов и тд. Выполняем задачку - получаем полное понимание. Задача: Взять строку "привет мир" и привести в бинарный формат (где используется вообще всего набор из 2х символов - ноль и единица), и посчитать длину данных исходных и в бинарном формате. Base64 подобен бинарному, но используется набор из 64символов, base58 - из 58, base32 - очевидно 32 и так далее. Чем меньше набор тем длиннее запись, тем больше места потребуется.


Реализация кода
Ну и сам код. Потребуется библиотека компрессии. Она есть, в поставке с фреймворком, находится в окружении System.IO.Compressiob
Находим ее в GAS (прямо в списке, dll не требуется так как они уже должны присутствовать, поэтому просто добавляем ссылку в gas выбрав из списка ее)

Класс с методами сжатия + кодирования base64 и обратная операция:
Method for compress/decompress:
using System.IO.Compression; // Добавляем системную либу компрессии

namespace Compress // Создаем новый namespace ля операции с куками
{
    /*
    var uncompressedString = "Hello World!";
    var compressedString = Compress.StringCompression.Compress(uncompressedString);

    var decompressedString = Compress.StringCompression.Decompress(compressedString);
    return decompressedString;   
    */
    public static class StringCompression
    {
        /// <summary>
        /// Compresses a string and returns a deflate compressed, Base64 encoded string.
        /// </summary>
        /// <param name="uncompressedString">String to compress</param>
        public static string Compress(string uncompressedString)
        {
            byte[] compressedBytes;
            using (var uncompressedStream = new MemoryStream(Encoding.UTF8.GetBytes(uncompressedString)))
            {
                using (var compressedStream = new MemoryStream())
                {
                    // setting the leaveOpen parameter to true to ensure that compressedStream will not be closed when compressorStream is disposed
                    // this allows compressorStream to close and flush its buffers to compressedStream and guarantees that compressedStream.ToArray() can be called afterward
                    // although MSDN documentation states that ToArray() can be called on a closed MemoryStream, I don't want to rely on that very odd behavior should it ever change
                    using (var compressorStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true))
                    { uncompressedStream.CopyTo(compressorStream); }
                    // call compressedStream.ToArray() after the enclosing DeflateStream has closed and flushed its buffer to compressedStream
                    compressedBytes = compressedStream.ToArray();
                }
            }
            return Convert.ToBase64String(compressedBytes);
        }

        /// <summary>
        /// Decompresses a deflate compressed, Base64 encoded string and returns an uncompressed string.
        /// </summary>
        /// <param name="compressedString">String to decompress.</param>
        public static string Decompress(string compressedString)
        {
            byte[] decompressedBytes;
            var compressedStream = new MemoryStream(Convert.FromBase64String(compressedString));
            using (var decompressorStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
            {
                using (var decompressedStream = new MemoryStream())
                {
                    decompressorStream.CopyTo(decompressedStream);
                    decompressedBytes = decompressedStream.ToArray();
                }
            }
            return Encoding.UTF8.GetString(decompressedBytes);
        }
    }
}
Остается лишь заюзать метод в кубике c# код
Код берет сразу из куки контейнера данные текущего инстанса, выполняет компрессию и кодирует в base64. Получаем строку которую можно сразу отправлять в бд.
Сжимаем:
var packCookie = Encoding.UTF8.GetString(project.Profile.CookieContainer.Export()); // достаем куки из куки контейнера
var pbCoookie = Compress.StringCompression.Compress(packCookie); // пакуем компрессией в pbCookie + encode Base64
string strComprCookies = pbCoookie.ToString(); // Приводим к строке из добавленного метода в OwnCode, получаем base64 строку

project.Variables["compessedCookie"].Value = strComprCookies; // Сохраним в переменной чтобы в след. кубике распаковать для демонстрации (и не более)

return strComprCookies;
// в ретурн у нас находится готовая к INSERT строка с куки-контейнером и всеми куками этого сеанса
Распаковка выполняется одной строкой. Получаем кукисы в исходном формате из сжатой + base64 строки с нашим набором кукисов.
Распаковываем:
// Присваиваем строку запакованные кук (предполагается что их забрали из БД)
var compressedString  = project.Variables["compessedCookie"].Value;

var decompressedString = Compress.StringCompression.Decompress(compressedString); // Распаковываем + decode base64 = получаем raw string Cookie (какие были изначально)

project.SendInfoToLog("Raw Cookies: "+ decompressedString, true);

project.Variables["rawCookies"].Value = decompressedString;

return decompressedString;
Ну а теперь о плюсах и минусах.
Минусы - мы не можем взять из БД куки путем копи паст. Нам потребуется что-то что еще и распакует. Это может быть не всегда практично. Но в моей задачи не предполагалось взаимодействия юзера с этими данными вручную. Но все же сделал сразу же мини утилиту консольную которая расколдовывает сжатый набор кукисов.

Плюсы - 1) там где минус там и плюс, такой подход вполне может добавить чуточку безопасности хранения, от всякого рода школы /скрипт кидди. Естественно более менее познавшего мир и суровость cyber security дяде вызовет лишь смех, когда школа будет со слезами создавать десятый пост с вопросом "подскажите что за ШИФР" или "как РАСШИФРОВАТЬ", так и не поняв, что хеширование вообще не возможно расшифровать, а компрессия далеко от темы шифрования, собственно как и хеширования, но последнее куда ближе.:ap:
2) мы храним в удобном формате, делаем операцию безопаснее и менее геморройную исключая проблемы с длинной / спец символами.



p.s. только не говорите что я вас убеждал мол это как то безопасно) нет это не безопаснее с точки зрения формата, разве что с точки зрения оперирования с данными. Для безопасного хранения существуют практики хеширования и так далее. Это уже не входит в рамки данной задачи =)


Бонус. Утилита распаковки
Утилиту можете скомпилировать самостоятельно, исходники в cookie_decoder_src.zip, кому лень или сложно, забирайте скомпилированную версию под win x64 cookie_decoder_bin.zip (включает в и рантайм и все необходимое, поэтому такой вес)

79754

Пример проекта с подробным описанием прикреплён в CompressCookies.zip

p.s.s. на эту статью потратилось в 4 раза больше времени чем на все шаги от проблемы до решения выше *HAHA*
 

Вложения

Для запуска проектов требуется программа ZennoPoster.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...

Для того чтобы запустить шаблон, откройте программу ZennoPoster. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.

uuw

Client
Регистрация
04.06.2020
Сообщения
146
Благодарностей
54
Баллы
28
легких путей не ищем
varbinary на 64К и пофиг что там в куках и на их размер, экранировать не забыть при сохранении в БД
 
Последнее редактирование:

Hartwell

Client
Регистрация
25.09.2014
Сообщения
194
Благодарностей
118
Баллы
43
легких путей не ищем
varchar binary на 64К и пофиг что там в куках и на их размер, экранировать не забыть при сохранении в БД
Не читаем статью =) В условиях же сказал что не хочу бинарные форматы, и экранирование не панацея. Гляньте rfc, куки могут не только буковки цифорки, а any symbols из набора us какойто там..


И говорю что не претендую на лучшее решение. Однако вполне как уневерсальное. Что же если понадобится вдруг не куки а что-то размером в 60кб ?) Данное решение уместит такое в тот же text поле, а у вас еще накинутся экранирования + бинарный формат (см сноски) тоже прибавят размер данных.


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

Но по всему что вы сказали, абсолютно, все оговрено это еще в самой статье =) Можно так то вообще в тхт, можно взять nosql и ниче не паковать ни конвертить, вариантов больше сотни это и так понятно. Можно вообще на стороне бд выполнять меджик процедурками. Все это можно =) Но как нужно, все-же определяет задача)
 

one

Client
Регистрация
22.09.2015
Сообщения
6 833
Благодарностей
1 275
Баллы
113
А для чего это нужно в принципе?
 

uuw

Client
Регистрация
04.06.2020
Сообщения
146
Благодарностей
54
Баллы
28
За то время пока Вы разбирались и писали код и пост, Вы могли бы с большим эффектом прочитать документацию на MySQL.
Тогда вы не выдали бы такое

Гляньте rfc, куки могут не только буковки цифорки, а any symbols из набора us какойто там..
Форматы Binary для того и созданы в MySql что бы хранить пофигу какие символы.
Фраза
а у вас еще накинутся экранирования + бинарный формат
значит только абсолютное непонимание работы БД.
символы экранирования не хранятся в бд если что.

А что касается компрессии...
жмите хоть гзипом хоть чертом лысым, формату varbinary пофиг что хранить.

не хочу бинарные форматы
Использование бинарных форматов и есть универсальный подход для любых задач, а остальное пустая трата времени
меджик процедурками
инфоцыган не стоит цитировать, нет такого понятия в mysql
:-)
 
  • Спасибо
Реакции: one

one

Client
Регистрация
22.09.2015
Сообщения
6 833
Благодарностей
1 275
Баллы
113
За то время пока Вы разбирались и писали код и пост, Вы могли бы с большим эффектом прочитать документацию на MySQL.
Тогда вы не выдали бы такое



Форматы Binary для того и созданы в MySql что бы хранить пофигу какие символы.
Фраза

значит только абсолютное непонимание работы БД.
символы экранирования не хранятся в бд если что.

А что касается компрессии...
жмите хоть гзипом хоть чертом лысым, формату varbinary пофиг что хранить.


Использование бинарных форматов и есть универсальный подход для любых задач, а остальное пустая трата времени

инфоцыган не стоит цитировать, нет такого понятия в mysql
:-)
Я не специалист по MySQL, но сказано сильно!:ce:
 

Hartwell

Client
Регистрация
25.09.2014
Сообщения
194
Благодарностей
118
Баллы
43
За то время пока Вы разбирались и писали код и пост, Вы могли бы с большим эффектом прочитать документацию на MySQL.
Тогда вы не выдали бы такое
За 30мин + 60мин осилите доку ?) Респект если так.

Форматы Binary для того и созданы в MySql что бы хранить пофигу какие символы.

Формат бинари создан для того чтобы хранить бинарные форматы, т.е. всякого рода файлы картинки и тд. В тоже время TEXT для хранения строковых данных.

что бы хранить пофигу какие символы.
Если быть точнее 255 вариантов против 77 или около того относящихся к текстовым с полем TEXT. В примере 64 всего используется, для чего тут еще 191 ? конвертируем же base.


BLOBзначения обрабатываются как двоичные строки (байтовые строки). У них есть binary набор символов и сопоставление, а сравнение и сортировка основаны на числовых значениях байтов в значениях столбцов. TEXTзначения обрабатываются как недвоичные строки (символьные строки). У них есть набор символов, отличный от binary, и значения сортируются и сравниваются на основе сопоставления набора символов.
MySQL Connector / ODBC определяет BLOBзначения как LONGVARBINARYи TEXT значения как LONGVARCHAR.
значит только абсолютное непонимание работы БД.
символы экранирования не хранятся в бд если что.
Скорее не об-мыслил, логично что это указания для парсера и не более. Что-же этот факт определяет уровень познания БД? забавно

А что касается компрессии...
жмите хоть гзипом хоть чертом лысым, формату varbinary пофиг что хранить.
К чему это? Бинари не жмет сам по себе
LENGTH(`binary`.`binary`)
3719

LENGTH(compres.compressed)
1512

А то что ее можно сжать и потом положить в бинари - спасибо кэп, но зачем? Нагрузить лишними операциями, если у нас строковой base64

Использование бинарных форматов и есть универсальный подход для любых задач, а остальное пустая трата времени
Не спорю, однако, с TEXT мы не привязаны к формату, если захотим перенести из mysql куда-то где используются только строковые. В бинари если только hex но опять же разницу выше приводил и она в 2 раза, это еще без учета перевода данных в hex, хотя конечно же в данном случае он на вовсе и не нужен.

И того резюмируем:
Вы топите за бинари, он лишь отличается от text тем что хранится в бинарном формате, мы можем туда класть че угодно - ок, но копипастом мы не заберем из таблички к примеру, да и вообще любое перемещение потребует лишних операций типо перевода в исходный формат либо hex если присутствуют not printble символы.
С TEXT у нас все в строках, не нужно плясок. (вы наверно у себя текстовые файлы сохраняете в .jpg или .bin, еще и приводите в бинарный формат? или все же если текст - то храните как текст а не картинки и тд)

Компрессию прокоментировали непонятно к чему, делаю вывод что бинари не решит проблему длинных данных без дополнительной компрессии, верно? Все вопросы с спец символами решает код. Приведите хоть один разумный аргумент зачем мне хранить строку base64 в binary нежели я это сделаю в TEXT. Аргументируйте?

p.s.s ну и пользуясь случаем, хочу спросить у гуру субд. куки - это юзерский инпут верно? допустим у нас реализация хранения кук в веб проекте, любой юзер может подсунуть любые значения в куки. В моем варианте нам плевать на все, что там будет, так как в бд попадает base64. А что-же в случае если бы мы сохраняли в бинарном, безопасно ли принимать без пред-обработки эти данные и сохранять в бинарном формате? Имею ввиду sql inj, и прочее. Честно я без понятия, поведайте поведение обработки юзерских инпутов в случае если просто сохранить бинарем?
 
Последнее редактирование:

uuw

Client
Регистрация
04.06.2020
Сообщения
146
Благодарностей
54
Баллы
28
В бинари если только hex но опять же разницу выше приводил и она в 2 раза
Жуткий секрет открою
Если взять строку
любую
с любыми символами
и тупо положить обычным инсертом в varbinary - она туда спокойно ляжет, и она же оттуда будет получена селектом. Каким концом здесь HEX?

наворачивать base64 на данные который нужно положить в БД?
Зачем?
Просто положить не судьба?
base64 для других целей разработан, в первую очередь для передачи данных протоколами поддерживающими только текстовые данные типа SMTP.

куки - это юзерский инпут верно?
Нет
Вы бы сначала почитали что такое куки, это уже за гранью
 

Hartwell

Client
Регистрация
25.09.2014
Сообщения
194
Благодарностей
118
Баллы
43
Каким концом здесь HEX?
эмм... вы вообще читаете что вам отвечают?
[
QUOTE="Hartwell, post: 628264, member: 8099"]
хотя конечно же в данном случае он на вовсе и не нужен.
[/QUOTE]


наворачивать base64 на данные который нужно положить в БД?
Зачем?
Просто положить не судьба?
Не судьба, и об этом много сказано в первом посте.


base64 для других целей разработан, в первую очередь для передачи данных протоколами поддерживающими только текстовые данные типа SMTP.
Да ну... https://ru.wikipedia.org/wiki/Base64#Применение_в_веб-приложениях


Нет
Вы бы сначала почитали что такое куки, это уже за гранью
Покажите пруф данного предположения?


И я так и не увидел аргумента...
Приведите хоть один разумный аргумент зачем мне хранить строку base64 в binary нежели я это сделаю в TEXT.
 

Ndorennu

Новичок
Регистрация
01.08.2021
Сообщения
8
Благодарностей
0
Баллы
1
Аргументов в этом случае мало.
 

Кто просматривает тему: (Всего: 2, Пользователи: 0, Гости: 2)