Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDN домены (в т.ч. кириллица) #194

Closed
iachaly opened this issue Apr 17, 2017 · 54 comments
Closed

IDN домены (в т.ч. кириллица) #194

iachaly opened this issue Apr 17, 2017 · 54 comments

Comments

@iachaly
Copy link

iachaly commented Apr 17, 2017

В данной теме будет собрана информация по работе Maxsite с IDN-доменами, например на кириллице. Проблема тянется ещё с давних времён и касается скорее особенностей работы CodeIgniter с Cookie и особенностей Javascript, jQuery, Ajax. Для меня всё продолжилось с появления новых сайтов на подобных доменах, что сразу выявило несколько проблем:

  1. Ошибка Disallowed Key Characters. Для ветки 0.8х мне удалось избавиться от вылета с данным сообщением на белом экране путём исправления ошибки фильтрации в библиотеке jquery.showhide.js крайне изящным способом - подключив библиотеку перекодировки русских символов в window.location.host в так называемый punycode, скрипт же исправил вот так:
    cookieName : ( punycode.toASCII(window.location.host) + window.location.
  2. На новом сервере было решено развернуть крайнюю версию системы. Применён хак из пункта 1, но к моему удивлению, всё повторилось. В этот момент стало понятно, что система неверно отрабатывает что-то с cookie. Копать я начал сразу в сторону CodeIgniter, поскольку уже имел с ним подобные проблемы. В файле /system/core/Input.php в строке 746 было исправлено регулярное выражение следующим образом: preg_match("/^[a-z0-9:_\/-]+$|/i", $str). На самом деле я не знаю, насколько это понизило безопасность системы, однако альтернатива такому решению - исправлять все подсистемы javascript с неизвестным заранее результатом. Подчеркну однако, что проблема кроется именно в неправильной подстановке Cookie!
  3. Неправильная работа прекрасного плагина загрузки файлов к записи. Здесь я сломал себе голову, пытаясь понять, почему на русских доменах во всех браузерах, кроме Chromium не работает показ списка загруженных файлов. Надо признаться, работа в Chrome поставила меня в тупик, уже позже я прочитал, что и ошибки из предыдущих пунктов в нём не всегда проявляются - был соблазн поставить своим задачу администрировать сайты через этот браузер. Как же это выглядит в Firefox: загрузка происходит, но списка нет, отладчик показывает 404 при загрузке файла /application/maxsite/admin/plugins/admin_page/all-files-update-ajax.php через Ajax в файле /application/maxsite/admin/plugins/admin_page/all-files.php
    Важно, что при обращениях, браузер распознаёт пути в кодировке ISO-8859-1, то есть кракозябры другие. Для решения пришлось отказаться от вызова скрипта по абсолютному пути. Вообще я противник использования абсолютных путей на сайте, и это было для меня очевидным решением в случае неправильного кодирования как раз имени хоста. В указанном файле 3 вызова скрипта, и убрал константу getinfo('ajax') вот так: '/ajax/' . base64_encode('admin/plugins/admin_page/all-files-update-ajax.php'); Данное решение не будет работать, если система находится не в корне домена, а в подпапке, но для меня это было неважно. Проблема возникает, как мне кажется, из-за того, что при различном кодировании кириллицы браузер может признавать разные варианты как различные домены - вот почему предложенное решение помогло.
  4. Данный список в дальнейшем может быть продолжен и систематизирован...

P.S. Не ругайте, если что-то неверно оформил, только начал изучать GitHub. Кроме того, прошу прощения за всё-же очень костыльные решения данных проблем. Надеюсь более толковые коллеги предложат более правильные решения. Прикладываю файлы, на которые ссылаюсь в архиве: chaly-maxsitepatch.zip

UPD0. Ранее выкладывал решение и не добавил его в шапку. Регулярное выражение в следующем виде: preg_match("/^[a-z0-9а-я:_\/-]+$/iu", $str).

По регулярке второй день тестируем. Пока нет уверенности, что она работает для всех случаев, в частности неизвестно, отрабатывается ли корректно буква ё и буквы, допустимые для белорусского, украинского, и иных языков.

UPD1. Уважаемый HaipaiUser предложил изменить регулярное выражение следующим образом: preg_match("/^[a-z0-9%:_\/-]+$/i", $str). Результат тестирования:

И вот это %D0%9B%D0%B0%D0%BA%D0%B8 потом пишется в куки. Тут не спасет ни punycode, ни добавления в регулярку символов а-я. Самый простой выход - это добавить в регулярку символ %.

Мнение iachaly: кука с процентами - плохое решение, поскольку в три раза увеличивает её длину. Можно вылететь за пределы стандарта и получить ошибку.

Данный хак добавлен в систему: ac5cb78

@maxsite
Copy link
Owner

maxsite commented Apr 19, 2017

Тут вот в чём дело. Кириллицы вообще не должно быть. Домен — это ascii-символы и если сервер отдаёт именно кириллицу, то это ошибка. Поэтому вначале нужно копать в сторону phpinfo() и что отдает $_SERVER.

@iachaly
Copy link
Author

iachaly commented Apr 19, 2017

Сам недоумеваю. Могу скинуть ссылку на полный phpinfo, либо даже предоставить доступ к виртуалке - если нужно, но только в личку. Вообще я бы не стал грешить на сервер. Apache и на фронте nginx. Картинки прилагаю.
2017-04-19_082531
2017-04-19_082602

@maxsite
Copy link
Owner

maxsite commented Apr 19, 2017

Да, домен вроде бы нормально формируется.

Тогда есть два раздельный направления. Я подскажу, а вы уже проверите.

Ошибка Disallowed Key Characters, как вы и написали в /system/core/Input.php. Нужно в функции _clean_input_keys как-то залогировать вывод входящей $str и при каких вариантах возникает ошибка (срабатывает if). Так можно будет понять куда двигаться.

Второй момент — установка куки через js, как вы написали в jquery.showhide.js (и возможно в других вариантах). Я правда, не нашел в документации, что имя куки должно быть только английским. Это получается строчка window.location.host + window.location.pathname нужно через консоль посмотреть что там получается. Если так какие-то запрещенные символы, то нужно попробовать их определить. Дальше обычные замены.

@iachaly
Copy link
Author

iachaly commented Apr 20, 2017

Да, с этим как раз всё хорошо. Уже проверял, делал. Всё очень интересно, там долгое время светится admin-menu, admin-files и так далее. Но после нескольких переходов, не более двух дюжин - вываливается поддомен_домен_рус-admin-files1.
JS выявил навскидку по недостаточности фильтрации, стандарт не смотрел: реализации стандартов в браузерах таковы, что только практика является критерием... Там правда появляются русские символы - когда не появляются, проблем нет. Решение, подчеркну наиболее верное - использовать корректную замену для IDN из официальной стандартной библиотеки. Отдельно хочу обратить внимание на пути к библиотекам файрбага.
2017-04-20_142549

@maxsite
Copy link
Owner

maxsite commented Apr 20, 2017

Вы имеете ввиду в Input.php? Думаю, что вначале нужно с ней разобраться.

@maxsite
Copy link
Owner

maxsite commented Apr 20, 2017

В openserver не пробовали на русском домене проверить работу?

@iachaly
Copy link
Author

iachaly commented Apr 20, 2017

Проверил, как и просили, переменную $str в файле /system/core/Input.php. Это та функция (_clean_input_keys), которую просили посмотреть и которую надо исправлять для корректной работы (в ней же в строке 746 и было исправлено регулярное выражение). Всё очень интересно, там (в переменной $str) долгое время светится admin-menu, admin-files и так далее. Но после нескольких переходов, не более двух дюжин - вываливается поддомен_домен_рус-admin-files1. Замечу только, что я проходил авторизацию, может быть без неё, ошибки и не будет... Надо смотреть.
Openserver как и прочий подобный софт вещи проблемные, пользую виртуальные машины. В данном случае это вообще стандартное развёртывание ubuntu 16.04, apache 2.4, php 5/7, mysql 5 из коробки. Но и на хостинге (best-hoster.ru) наблюдаются аналогичные ошибки.

@maxsite
Copy link
Owner

maxsite commented Apr 21, 2017

Тут непонятно как тестировать и искать ошибки.

Если куку кто-то ставит, то нужно определять кто именно. От этого и плясать. Теоретически проверку в /system/core/Input.php можно убрать или как-то упростить, но насколько я понял, это не решает проблемы.

@iachaly
Copy link
Author

iachaly commented Apr 24, 2017

На самом деле проблему решает применение моих патчей из первого сообщения. Для старых версий ветки 0.8х следует исправить недостаточную фильтрацию window.location.host в jquery.showhide.js как указано в первом пункте. Для новых версий проблема решается исправлением регулярки в /system/core/Input.php и сказано об этом во втором пункте . Для корректной работы загрузки файлов к записям следует внести исправления из третьего пункта.
Чисто формально - мой патч исправляет указанные баги - но требует внесения изменений в файлы систем Codeigniter и Maxsite. Предлагаю включить в новые версии.

@maxsite
Copy link
Owner

maxsite commented Apr 24, 2017

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

@iachaly
Copy link
Author

iachaly commented Apr 24, 2017

Нормальные домены - это те, которые стандартизированы ICANN, в них входит и кириллица и иероглифы и диакритические костыли европейских языков. Предлагаю всё же использовать термин IDN и всё же использовать стандартное преобразование punycode, где это возможно. То есть: пункты 1 и 3 не влияют на безопасность точно. По факту этому багу много лет, странно, что не вылез раньше.
По регулярке второй день тестируем preg_match("/^[a-z0-9а-я:_\/-]+$/iu", $str). Пока нет уверенности, что она работает для всех случаев, в частности неизвестно, отрабатывается ли корректно буква ё и буквы, допустимые для белорусского, украинского, и иных языков. Универсальным бы стало решение с использованием как раз punycode... Но пока может быть запилить в качестве временной меры данное решение? Оно не понизит безопасность системы.

@maxsite
Copy link
Owner

maxsite commented Apr 25, 2017

Нормальные домены, это те, которые не требуют никакого преобразования для корректной работы. Всё остальное — это «хак».

Далее. У вас ситуация обратная. Обычно проблема в том, что адрес идет ASCII, а нужно получить кириллицей. Здесь наоборот. Если говорить о PHP, то для этого есть специальные функции idn_to_ascii и idn_to_utf8. Но ваш сервер отдаёт уже нормальное имя, которое не требует преобразования. Поэтому проблема вот в этом (цитата):

Всё очень интересно, там долгое время светится admin-menu, admin-files и так далее. Но после нескольких переходов, не более двух дюжин - вываливается поддомен_домен_рус-admin-files1.

Если это js-код, то проблема с взаимодействием с браузером (именно он отдает window.location.host). Вы можете объяснить почему так происходит?

Что касается относительных путей, то это даже не буду обсудать. Только абсолютный путь.

Всё это можно решить только после тестирования. Ваши решения «в лоб», не факт, что будут корректно работать у других. Может так сервер настроен, может браузер и т.д. Это слишком серьёзные изменения, чтобы их внедрять без тестирования и вообще, нормальной реализации.

@iachaly
Copy link
Author

iachaly commented Apr 25, 2017

Нормальные домены, это те, которые не требуют никакого преобразования для корректной работы. Всё остальное — это «хак».

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

  1. Актуальный список корневых доменов (https://www.iana.org/domains/root/db)
  2. Кодовые таблицы символов для них допустимые (https://www.iana.org/domains/idn-tables)

Но ваш сервер отдаёт уже нормальное имя, которое не требует преобразования.

Далёк от мысли, что стандартная установка из коробки может давать такие ошибки, у нас много серверов построенных так, а проблемы только с Codeigniter и Maxsite. Скорее здесь проблема на стыке браузер-javascript. Потому как реализация Chromium таких ошибок не даёт. Вполне возможно, Codeigniter в используемой версии вышел так давно, что IDN тогда был только в проекте...

Если это js-код, то проблема с взаимодействием с браузером (именно он отдает window.location.host). Вы можете объяснить почему так происходит?

Конечно нет! Не я писал этот код и не могу знать, почему и где допущена ошибка фильтрации. Путь для исправления я описал выше (используем библиотеку punycode), но видимо по невнимательности или глупости не могу найти где это исправить. Подозреваю, что если найти и исправить этот баг - не придётся вносить изменения в допустимые символы /system/core/Input.php

Всё это можно решить только после тестирования. Ваши решения «в лоб», не факт, что будут корректно работать у других. Может так сервер настроен, может браузер и т.д. Это слишком серьёзные изменения, чтобы их внедрять без тестирования и вообще, нормальной реализации.

На мой взгляд, статус ошибки обнаруженной мной ошибки - критический. Это как-никак недостаточная фильтрация и крах системы для пользователей всех браузеров кроме Chromium.

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

@maxsite
Copy link
Owner

maxsite commented Apr 25, 2017

По доменам же, вы мягко говоря неправы и я вынужден переадресовать к документации

Боюсь, что вы не до конца понимаете как это работает. Все домены должны быть в ASCII-формате. Для того, что бы обеспечить поддержку non-ASCII, используется «хак», а именно префикс xn--, при наличие которого и происходит Punycode-де/кодирование. Юникод преобразуется в нормальный ASCII-формат.

Поскольку кука — это часть заголовка http-запроса, то проблема на уровне взаимодействия с браузером. На уровне сервера проблем с кодированием нет. По phpinfo(), видно, что _SERVER формирует домен как положено в ASCII-формате.

В _clean_input_keys() совершенно верная проверка, разрешающие только ASCII-формат. Когда там появляется юникод, то это уже ошибка.

То что вы сделали — это конктерно ваш частный случай. В одном случае вы отключили проверку, в другом, убрали путь. Если бы браузер отправлял данные в ASCII-формате, то таких проблем не будет. И, строго говоря, так оно и должно быть, хотя бы из принципа обратной совместимости.

Если в одном браузере всё работет, а в другом нет, то вывод напрашивается сам собой где нужно искать проблему. Лично я не пользуюсь кирилическими доменами, поэтому у меня нет возможности даже воспроизвести данную проблему. Придумайте способ поднять кириллический домен на OpenServer (https://ospanel.io/docs/ — у меня почему-то не получается), тогда мы сможем приступить к конкретному разговору.

@HaipaiUser
Copy link

HaipaiUser commented Jun 6, 2017

Я тоже сталкивался с ошибкой Disallowed Key Characters из-за jquery.cookie.js.
В общем вот:
https://www.w3schools.com/tags/ref_urlencode.asp
https://en.wikipedia.org/wiki/Percent-encoding
Ошибку в данном случае вызывает то, что регулярка в CodeIgniter - preg_match("/^[a-z0-9:_/-]+$|/i", $str) не пропускает символ %.
Вот как выглядит к примеру слово Лаки - %D0%9B%D0%B0%D0%BA%D0%B8
И вот это %D0%9B%D0%B0%D0%BA%D0%B8 потом пишется в куки.
Тут не спасет ни punycode, ни добавления в регулярку символов а-я.
Самый простой выход - это добавить в регулярку символ %.
Либо не использовать encodeURI и encodeURIComponent.
Ну или делать проверку на % и если есть, то делать decode, а затем накатить punycode и только потом это пихать в куки.

@maxsite
Copy link
Owner

maxsite commented Jun 6, 2017

Вы можете исправить регулярку и проверить её работу?

@HaipaiUser
Copy link

HaipaiUser commented Jun 6, 2017

дак проверил уже давно
Если не изменяет память:
preg_match("/^[a-z0-9%:_\/-]+$/i", $str)
P.S. Сейчас снова перепроверил на 0.96, все нормально работает
P.P.S Это одна и таже проблемма:
#148

@HaipaiUser
Copy link

HaipaiUser commented Jun 6, 2017

В общем в 0.96 в файле system/core/Input.php строку 746:
if ( ! preg_match("/^[a-z0-9:_\/-]+$/i", $str))
заменить на:
if ( ! preg_match("/^[a-z0-9%:_\/-]+$/i", $str))

и проблем с encodeURI и encodeURIComponent не будет.

@HaipaiUser
Copy link

Поправил предыдущие два сообщения, там съедались символы в регулярке.
Обрамил регулярки в тэг code, теперь показывается нормально.

@iachaly
Copy link
Author

iachaly commented Jun 6, 2017

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

@HaipaiUser
Copy link

HaipaiUser commented Jun 6, 2017

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

@maxsite
Copy link
Owner

maxsite commented Jun 6, 2017

Да, мне тоже кажется, что с символом % более элегантное решение. Кука может именоваться с этого символа?

@HaipaiUser
Copy link

HaipaiUser commented Jun 6, 2017

Вот к примеру кука выведенная по print_r:
test1_ru-2-%D0%9B%D0%B0%D0%BA%D0%B8 Array ( [ci_session] => +xDZKZj1/msfsf2G [test1_ru-2-%D0%9B%D0%B0%D0%BA%D0%B8] => {"0":0,"1":0} [_ga] => GA1.2.1802358565.1495731778 [_gid] => GA1.2.735865149.1495731778 [_gat] => 1 )

В данном случае в начале идет английский символ, т.к. домен используется английский и поэтому сработал только encodeURIComponent.
Но если будет русский домен, то в начале там будет %, т.к сработает и encodeURI и encodeURIComponent.

@iachaly
Copy link
Author

iachaly commented Jun 6, 2017

Во-первых ваше решение это абсолютно такой же костыль - внесение изменений в систему CI. Но без него никак, тут я согласен. Можно конечно переопределить метод, но это повлечёт корректировки по всему Maxsite.
Во-вторых, на одном из серваков это решение у меня не взлетело (хотя возможно я делал что-то не так). А моё как раз легко. Объясните, почему именно моё решение - костыль? Кроме того, для старых систем вмешательство в ядро и не нужно, всё выше описано как и почему.
В-третьих, есть на самом деле половинчатая мера, о которой я писал чуть ниже, читайте внимательнее. Использую: это модификация регулярки включением символов русского языка (это возможно, поскольку utf). Это неуниверсальное решение, но поскольку эксплуатанты Maxsite используют латиницу и кириллицу, возможно подойдёт лучше. Естественно надо решить вопрос диалектов, а именно включить в регулярку специфические для смежных алфавитов буквы.
HaipaiUser, если вы специалист по регуляркам, было бы неплохо, если перепишете её в корректном виде. Дело ведь ещё в том, что ни русские символы, ни проценты не должны попадать в эту функцию, об этом говорил Максим и совершенно справедливо!

@iachaly
Copy link
Author

iachaly commented Jun 6, 2017

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

@HaipaiUser
Copy link

HaipaiUser commented Jun 6, 2017

iachaly исправление регулярки это не костыль, а исправление ошибки.
Русских символов быть не должно - это правда, а знак процента есть в стандарте.
https://en.wikipedia.org/wiki/Percent-encoding

И если не хотите поправить регулярку, как я уже сказал не используйте никогда encodeURI и encodeURIComponent и тогда тоже ничего править не придется.

@iachaly
Copy link
Author

iachaly commented Jun 6, 2017

Кто же спорит, только оно не должно туда попадать в принципе (как и любые другие символы кроме латиницы). Если по хорошему, туда должен быть встроен конвертер пуникода, аналогично моему решению с библиотекой javascript.
Подчеркну, ваше решение тоже костыль. В общем, я добавляю в шапку, страждущие разберутся.

@HaipaiUser
Copy link

HaipaiUser commented Jun 6, 2017

iachaly вот как раз наоборот, знак процента туда должен попадать, т.к. он есть в стандарте и его используют стандартные функции encodeURI и encodeURIComponent.
https://en.wikipedia.org/wiki/Percent-encoding

Смотрите ASCII таблицу:
https://ru.wikipedia.org/wiki/ASCII
а конкретно символ 0x25 - %

И как я сказал, в данном случае регулярка не правильная.
А раз она не правильная, то это не костыль, а исправление ошибки.

@maxsite
Copy link
Owner

maxsite commented Jun 6, 2017

Не спорьте. :-) Вот есть документация: https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Set-Cookie

A cookie-name can be any US-ASCII characters except control characters (CTLs), spaces, or tabs. It also must not contain a separator character like the following: ( ) < > @ , ; : \ " / [ ] ? = { }.

Символа процента нет, значит ошибка именно в CodeIgniter, который слишком жестко фильтрует данные.

@maxsite
Copy link
Owner

maxsite commented Jun 6, 2017

Да, так и сделаю.

@iachaly
Copy link
Author

iachaly commented Nov 16, 2017

Подтверждаю наличие ошибки Disallowed Key Characters в новой версии 98. Патч за моим авторством всё также успешно исправляет ошибку, как и решение с процентом от HaipaiUser.
Свежий сервер на Ubuntu 16.04, mysqlnd 5.0.12, PHP: 7.0.18

@iachaly iachaly changed the title IDN домены (в т.ч. кириллица) IDN домены (в т.ч. кириллица) обновление Nov 16, 2017
@iachaly iachaly changed the title IDN домены (в т.ч. кириллица) обновление IDN домены (в т.ч. кириллица) Nov 16, 2017
@maxsite maxsite reopened this Nov 17, 2017
@maxsite
Copy link
Owner

maxsite commented Nov 17, 2017

На каких адресах появляется ошибка?

@iachaly
Copy link
Author

iachaly commented Nov 21, 2017

При входе в админку, через несколько кликов, отследить конкретную страницу не получается. Но есть ощущение, что переполнение происходит раньше, на страницах сайта. На самом деле, надо просто привести файлы CI и Maxsite к соответствию стандарту, правда, я уже устал отлавливать то место, где система разваливается, дальше показанного на скриншотах продвинуться на могу (я всё же не тестировщик). Патченные файлы приложены ещё в мае, собрал и для 98 версии, чем они не устраивают?

@maxsite
Copy link
Owner

maxsite commented Nov 21, 2017

Ваш патч не годится по той простой причине, что он исправляет следствие, а не причину. Проблема в фильтрации CodeIgniter, который считает, что существуют недпустимые символы, и их нужно жестко «рубануть».

Сложность в том, что нет информации по тому какие имена кук вообще допустимы. Либо это самодурство CodeIgniter, либо они на чем-то основывались.

Я могу предложить вариант, вообще убрать (закоментировать) строчки:

		if ( ! preg_match("/^[a-z0-9%:_\/-]+$/i", $str))
		{
			exit('Disallowed Key Characters.');
		}

в файле Input.php. Но насколько это повлияет на безопасность совершенно непонятно. С моей точки зрения этот код написан в доюникодовскую эпоху, а значит нужно прописать регулярку так, чтобы она поддерживала все символы юникода.

@iachaly
Copy link
Author

iachaly commented Nov 21, 2017

Уважаемый HaipaiUser находил в стандарте процент, его добавили с появлением IDN-доменов. Комментировать эту строку уж точно не следует на мой взгляд, это полностью лишит систему фильтрации. Поддерживать все символы юникода в общем и не надо. ICANN героически решила проблему, которую создали, привязав по глупости или злому умыслу DNS и интернет к латинице: у них есть документ, перечисляющий допустимые символы, плюс соответствие их в punycode.

Также продублирую из шапки темы: можно записать регулярное выражение в следующем виде: preg_match("/^[a-z0-9а-я:_\/-]+$/iu", $str). У этого достаточно параноидального решения (использую у себя) есть нюансы: не все русские буквы идут подряд. А ещё в украинском, белорусском, казахском и так далее есть специальные буквы, а ещё есть другие языки IDN. Можно дописать все символы в регулярку, всё отработает как надо... Но видимо поэтому в стандарт и внесли символ процента.

Другое дело, что это не исправляет ситуацию с быстрой загрузкой (закинул этот баг в профильную тему, там разбираться не так просто, как здесь).

З.Ы.: не ответил по существу - причина на мой скромный взгляд в общем в существовании доменов IDN и ошибках в проектировании сети интернет, привязка к латинице. Поэтому и появляются символы в куках, которых якобы не должно там быть. Punycode это ведь в общем грязный хак, который в угоду барышам решили внедрить повсеместно. CI2 появился до повсеместного его распространения. А прикладываемые мной файлы - единственный способ это исправить. Они и есть та точка отказа, остальное находится вне CI и Maxsite.

@maxsite
Copy link
Owner

maxsite commented Nov 21, 2017

Понятно, что всё не так просто. :) Кука — это http-заголовок и почти все браузеры (кроме старых сафари вроде бы) отправляют данные в юникоде. То есть использование юникода и прочих спецсимволов вообще не проблема и именно поэтому кука может именоваться как угодно. Ни на одном этапе проблемы не возникает.

То есть её фильтрует CodeIgniter, и вообще непонятно зачем это делается. Если это неверное имя, то кричать должен либо сервер, либо браузер. Все данные, которые идут от пользователя (post или куки) всё равно ещё раз фильтруются при использовании. Поэтому я думаю, то отключение вряд ли на что-то повлияет, хотя 100% гарантии дать не могу. В новых версиях CodeIgniter такй проверки нет. Только прогоняется на xss-фильтрацию (полноценно еще не изучал).

@maxsite
Copy link
Owner

maxsite commented Nov 21, 2017

Еще момент, не сообразил сразу написать. В jquery.showhide.js можно попробовать кодировать window.location.host с помощью encodeURIComponent:

cookieName : ( encodeURIComponent(window.location.host) + window.location.pathname ).replace( /\//g, '-' ),

Тогда дополнительные библиотеки в виде Punycode не нужны.

@iachaly
Copy link
Author

iachaly commented Nov 21, 2017

Да, на старых версиях этого было достаточно и я даже выкладывал исправленный файл, где не тупо резалось, а кодировалось в пуникод. Раньше эта библиотека почему-то вызывала падение некоторых javascript. Если нужно - могу поискать. Дело в том, что в новых версиях этого хватать перестало, и меня ввело в ступор такое поведение сервера...

По поводу фильтрации на уровне сервера - на мой взгляд нужно вообще фильтровать всё, что приходит от недоверенного источника. С другой стороны кука всё равно прогоняется через фильтры и кодировщики, и нелатинские символы превращаются в коды наподобие такого: %2A%3F%1C где каждая тройка - нужный нам код. Видимо процент в стандарте по кукам появился вот ради этого.

Но кстати, один из маркеров того, что здесь всё нечисто - отваливающиеся скрипты, использующие куки, например загрузка аяксом (в котором портится путь к аяксу в переменной maxsite). Что ещё более удивительно - данная проблема есть в firefox, но отсутствует в chromium. Ещё весной об этом писал)

@maxsite
Copy link
Owner

maxsite commented Nov 21, 2017

Аякс — это отдельная песня. Там нужно разбираться и там довольно сложная завязка на php-функции преобразования адреса. А вот что касается jquery.showhide.js, то вроде бы encodeURIComponent будет достаточно, чтобы не вылезти за границы допустимых символов.

@iachaly
Copy link
Author

iachaly commented Nov 21, 2017

Поправка, для jquery.showhide.js в первом архиве с патчем лежит и библиотека punycode и патченный файл. Лучше всё же его, правильнее адрес всё же кодировать, а не просто обрезать. По аяксу ошибка в кодировании переменной ajax. Если по хорошему - надо её на уровне системы прогонять через преобразователь в punycode.

@maxsite
Copy link
Owner

maxsite commented Nov 21, 2017

C аяксом немного проще. Для js адрес нужно отдавать в юникоде, то есть без idn-преобразования. У нас же он отдается как $MSO->config['site_url'], то есть от $_SERVER, ге это преобразование уже выполнено. Таким образом проблема здесь только в том, чтобы выполнить обратное преобразование в юникод.

В php для этого есть функция idn_to_utf8(), но для её использования нужно подключать библиотеку php_intl, что не на каждом хостинге сделано. Кроме того, на тестовом домене (в Open Server) у меня преобразование происходит с ошибкой. Кастомная функция с php.net тоже работает неверно. То есть нужно придумывать как выполнить это преобразование.

@maxsite
Copy link
Owner

maxsite commented Nov 21, 2017

На вскидку приходит простое решение. Для таких случаев можно предусмотреть в MaxSite CMS в конфигурационном файле явное указание адреса сайта. Для аякса, где только и требуется данная настройка, если адрес задан, то он и будет использоваться. Иначе как сейчас — автоматом. Это не идеальное решение, но хотя бы рабочее.

@maxsite
Copy link
Owner

maxsite commented Nov 21, 2017

В общем вот предварительное решение. В конец файла common.php нужно добавить функцию



/**
* Decode IDN Punycode to UTF-8 domain name
*
* @param string $value Punycode
* @return string Domain name in UTF-8 charset
*
* @author Igor V Belousov <igor@belousovv.ru>
* @copyright 2013, 2015 Igor V Belousov
* @license http://opensource.org/licenses/LGPL-2.1 LGPL v2.1
* @link http://belousovv.ru/myscript/phpIDN
*/
function mso_DecodePunycodeIDN( $value )
{
	/* search subdomains */

	$sub_domain = explode( '.', $value );

	if ( count( $sub_domain ) > 1 ) {
	  $sub_result = '';
	  
	  foreach ( $sub_domain as $sub_value ) 
	  {
		$sub_result .= '.' . mso_DecodePunycodeIDN( $sub_value );
	  }
	  
	  return substr( $sub_result, 1 );
	}

	/* search prefix */
	if ( substr( $value, 0, 4 ) != 'xn--' )
	{
		return $value;
	}
	else
	{
		$bad_input = $value;
		$value = substr( $value, 4 );
	}

	$n      = 0x80;
	$i      = 0;
	$bias   = 72;
	$output = array();

	/* search delimeter */
	$d = strrpos( $value, '-' );

	if ( $d > 0 ) {
	  for ( $j = 0; $j < $d; ++$j) {
		$c = $value[ $j ];
		$output[] = $c;
		if ( $c > 0x7F )
		  {
			return $bad_input;
		  }
	  }
	  ++$d;
	} 
	else 
	{
	  $d = 0;
	}

	while ($d < strlen( $value ) )
	{
		$old_i = $i;
		$w = 1;

		for ($k = 36;; $k += 36)
		{
			if ( $d == strlen( $value ) ) return $bad_input;

			$c = $value[ $d++ ];
			$c = ord( $c );

			$digit = ( $c - 48 < 10 ) ? $c - 22 :
			  (
				( $c - 65 < 26 ) ? $c - 65 :
				  (
					( $c - 97 < 26 ) ? $c - 97 : 36
				  )
			  );
			if ( $digit > ( 0x10FFFF - $i ) / $w ) return $bad_input;
			
			$i += $digit * $w;

			if ( $k <= $bias )
			{
				$t = 1;
			}
			elseif ( $k >= $bias + 26 )
			{
				$t = 26;
			}
			else
			{
				$t = $k - $bias;
			}
			  
			if ( $digit < $t ) break;

			$w *= 36 - $t;
		}

		$delta = $i - $old_i;

		/* http://tools.ietf.org/html/rfc3492#section-6.1 */
		$delta = ( $old_i == 0 ) ? $delta/700 : $delta>>1;
		$count_output_plus_one = count( $output ) + 1;
		$delta += intval( $delta / $count_output_plus_one );

		$k2 = 0;
		while ( $delta > 455 )
		{
			$delta /= 35;
			$k2 += 36;
		}
		
		$bias = intval( $k2 + 36  * $delta / ( $delta + 38 ) );
		
		/* end section-6.1 */
		if ( $i / $count_output_plus_one > 0x10FFFF - $n ) return $bad_input;
		
		$n += intval( $i / $count_output_plus_one );
		$i %= $count_output_plus_one;
		array_splice( $output, $i, 0, html_entity_decode( '&#' . $n . ';', ENT_NOQUOTES, 'UTF-8' ));
		++$i;
	  }

	return implode( '', $output );
}

В верху в функци getinfo() исправить часть с ajax чтобы получилось так:

		case 'ajax' :
				// проверка IDN-домена для аякса
				if (stripos($MSO->config['site_url'], 'xn--') !== false )
				{
					$host = parse_url($MSO->config['site_url'] );
					$out = '//' . mso_DecodePunycodeIDN($host['host']) . '/ajax/';
				}
				else
				{
					$out = '//' . str_replace(array('http://', 'https://'), '' , $MSO->config['site_url']) . 'ajax/';
				}
				
				break;

После этого аякс должен работать.

@iachaly
Copy link
Author

iachaly commented Dec 4, 2017

Сейчас тестирую, вроде всё идёт нормально. Предложенное решение выше работает. Даже заработал аякс в админке. Пару дней надо посмотреть как оно под нагрузкой поработает, какие отзывы будут. А вообще мы с коллегами пришли к неутешительному выводу: везде, где явно или неявно появляются IDN нужно приводить к нужному виду аналогичными функциями, иначе обречены затыкать дыры в порядке баг-репортов пользователей. Сейчас этих места два: клиентский JS и серверный PHP. Нужно делать это централизованно, но вот вопрос, где?

@maxsite
Copy link
Owner

maxsite commented Dec 5, 2017

Ну насчет дыр это вы погорячились. :-) Обычные технчиеские проблемы, которые просто сложно выявить и оттестировать. Поскольку сейчас уже разобрались, то ясно, что домен для JS должен отдаваться в юникоде через encodeURIComponent. На уровне PHP адрес определяется в IDN-формате, что не соответствует юникоду. Поэтому для передачи адреса из PHP в JS мы и используем кодирующую функцию. И здесь только одна точка — это аякс-адрес.

@iachaly
Copy link
Author

iachaly commented Dec 9, 2017

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

Добавлю: с этим изменением за последние дни не наблюдается проблем в работе сайтов на ИДНках, жизнеспособно и стабильно. Нагрузку тоже не заметил.

@maxsite
Copy link
Owner

maxsite commented Dec 10, 2017

Ок. Тогда немного доработаю и внесу изменения в систему.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants