libonion

Jul. 14th, 2020 05:53 pm
eddy_em: (Default)
[personal profile] eddy_em
В поисках наиболее быстрого решения (ну лень мне пилить свою веб-библиотечку, должно же быть что-то готовое!) нашел две вполне приличных библиотеки: libhttpd и libonion. Во второй понравилось меньшее количество кода и большее количество примеров (хотя, как оказалось, все равно их слишком мало).
Второй день мучаю эту библиотеку.
Документации ужасно мало (html, генерированный doxygen'ом, да несколько примеров), поэтому иной раз приходится для того, чтобы одну строчку кода написать, час-два ковыряться в исходниках библиотеки, чтобы понять, как это сделать...

Итак, для начала я решил попробовать методику сессионной авторизации, которая как бы реализована в libonion. Увидел, что есть поддержка сессий в sqlite и решил воспользоваться этим:
onion_set_session_backend(o,  onion_sessions_sqlite3_new("sqlite.database"));

Выдрал из примеров oterm сессионную авторизацию. Заработало. Однако, сразу смутило, что кука выдается на одну сессию браузера! Ну это же вообще жесть... И как делать сессию длиной в пару лет, чтобы пользователю не нужно было вводить логин/пароль после каждого перезапуска браузера?
Дальше мне в сессионную БД захотелось добавить IP и User-Agent подключенного пользователя. Полчаса ковыряния в исходниках показали, что геттеров для них нет. Но в структуре onion_request есть поля, которые позволят это сделать.
Пробуем с IP:
onion_connection_status oterm_nopam(onion_handler *next, onion_request *req, onion_response *res){
    char host[INET_ADDRSTRLEN];
    struct sockaddr_in* pV4Addr = (struct sockaddr_in*)&req->connection.cli_addr;
    struct in_addr ipAddr = pV4Addr->sin_addr;
    inet_ntop(AF_INET, &ipAddr, host, INET_ADDRSTRLEN);
    onion_dict *session = onion_request_get_session_dict(req);
    const char *username = onion_request_get_session(req, "username");
    if(!username){
   // здесь у нас должна происходить процедура аутентификации, пока забиваем на нее и берем из env
        username = getenv("USER");
        onion_dict_lock_write(session);
        onion_dict_add(session, "username", username, 0);
        onion_dict_add(session, "IP", host, 0);
        //onion_sessions_save(req->connection.listen_point->server->sessions, req->session_id, req->session);
        onion_dict_unlock(session);
    }
    printf("USER IP=%s\n", onion_dict_get(session, "IP"));
...
}

Закомментированной onion_sessions_save вначале не было. Она появилась, когда оказалось, что IP таким образом не сохраняется: в первый раз onion_dict_get показывает мне нормальный адрес, а вот во второй и последующие — мусор! В БД этот же мусор и записан. ОК, добавляем onion_sessions_save и … фигвам! Опять не сохраняет, собака!
Лезу в код. Ковыряюсь час! И не нахожу, как такое может быть: каждый onion_dict_add вызывает аллокатор и копирует данные! Т.е. там неоткуда взяться мусору!! Правда, все подробно изучать было лень. Возможно, где-то на стадии сохранения БД получается этот бред.
Ладно, заменяю char host на const char host, и… Все работает нормально! Вот как так?

Попытка добавить User-Agent была еще интересней: глядя на примеры, где так ловко временные данные добавлялись в "словарь", попытался сделать
const char *UA = onion_request_get_header(req, "User-Agent");
…
onion_dict_add(session, "UA", UA, 0);

Получаю то же самое, что и с IP! А вот если выделяю статическую строку и snprintf'ом в нее пишу полученное, то все ОК.

Кто-то где-то сильно накосячил.

Ладно, сидел утром на работе и, как уже выше писал, обнаружил, что сессионные куки… — сессионные! Да еще и хитрожопые: с флагом, запрещающим их чтение жабоскриптом!
Ну и как, скажите на милость, я в веб-страничке узнаю, что у меня пользователь уже прошел аутентификацию? Отправлять для этого POST-запрос на сервер?
Ладно, буду отправлять POST-запрос для определения сессионного ключа (хотя по-человечески это можно было бы сделать, просто считав данные из куки). Но после того, как пользователь перезапустит браузер, все сессионные куки канут в Лету!

В общем, в libnonion "из коробки" совершенно непригодные методы аутентификации и авторизации. Придется придумывать, как выйти из положения: либо править код самой библиотеки (что чревато: придется делать патчи на каждое обновление), либо на основе их API сделать свои, которые будут сохранять вменяемые сессионные куки (со сроком хотя бы год), которые можно будет читать из жабоскрипта (чтобы сразу же можно было проверить, все ли ОК, и если сессионная кука есть, то открывать вебсокет; если же нет — сначала делать POST-запрос с именем/паролем и получать куку).
В принципе, все равно придется работать с sqlite напрямую: чтобы достать из БД имя пользователя, уровень доступа и SHA512 от пароля, возможно, придется аутентификацию полностью брать на себя — вместе с сессионными ключами.

Второй вариант — попытаться то же самое реализовать на libhttp: вдруг в ней нет этих багов. Поддержка вебсокетов там есть (правда, тоже нет секурных, поэтому имя и пароль тоже придется передавать POST-запросом по https!).


А хочется простейшего: принимать своим демоном клиентские запросы напрямую, без apache или nginx (т.к. большинство наших железок так и работают: веб-сервер ставится на управляющий компьютер тупо ради передачи пользователю двух-трех веб-страничек и пары жабоскриптов с одной-двумя CSSинами). Иметь возможность безопасно передать с клиента на сервер пару логин-пароль, получить сессионную куку и хранить ее хотя бы год — чтобы после перезапуска браузера не нужно было опять аутентификацию проходить. Ну и после того, как подлинность клиента подтверждена, открываем вебсокет и работаем через него.

April 2025

S M T W T F S
  1 23 45
67 89101112
13141516171819
20212223242526
27282930   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated May. 22nd, 2025 06:54 am
Powered by Dreamwidth Studios