- PVSM.RU - https://www.pvsm.ru -
Мы с товарищем, сделали простенький тест [1] (github [2]) на проверку доступности data-uri в браузерах. Выглядит он следующим образом

В textarea javascript'ом вставляется navigator.userAgent. В этот момент я не знаю, что меня стукнуло в голову, но вместо DOMContentLoaded, я написал <body onload="onload()">. По-быстрому проверив корректную работу в десктопных браузерах и на нескольких мобильных устройствах, подключённых к интернету через wi-fi, мы успокоились и разошлись по домам.
Утром, в полупустом вагоне метро, я как всегда открыл браузер на своем телефоне, на котором со вчерашнего вечера была открыта тестовая страничка. Сказать, что я удивился, когда я не увидел вывода userAgent внутри textaria — ничего не сказать.
Добравшись до компьютера, решил потратить немного времени на поиск проблемы. Запустив страничку на десктопе и в эмуляторе, я ничего не заметил. Открыл на телефоне. Чудеса! Всё работает.
Увидев включённый значок wi-fi, в мою голову начали закрадываться первые подозрения о причине проблемы. Я отключил wi-fi, подключил телефон к компьютеру и начал дебаг с помощью веб-инспектора десктопного сафари.
![]()
Уоу-уоу, полегче, подумал я. Откуда всё это могло взяться в 6 строка JS'а?
А вот откуда

Отличился наш любимый оператор Beeline, с которого я ещё не до конца успел слезть. На небольшую тестовую страничку внезапно прилетело ~133КБ паразитного траффика.
Anchor.js расположен по адресу http://toolbar.beeline.ru/ets/scripts/Anchor.js, который доступен только из сотовой сети Билайна. Для всех желающих посмотреть на стиль кодирования на JS в Билайне, я подготовил ссылку — gist [3].
Что же входит в эти 133 КБ:
* 91 КБ jQuery v1.7.2;
* 42 КБ самописного кода.
Видно, что никто не заморачивался даже использованием CDN для jQuery. Ну ОК. Смотрим дальше на самописный код [4].
Первые 914 строк — один большой if:
if (document.getElementById('toolbar') == null)
{
//...много кода
}
Дальше немного хелперов с упором на мобильные устройства от Apple:
function getIOSVersionNumber(){}
function isIt(theDevice){}
function isMobile(){}
function isIPhone(){}
function isIPad(){}
function getBodyZoomLevel () {};
function isNumberPercentageBased(number) {}
function getVisualViewportInfo () {};
Испектируем код про тулбар:
(function(jQFrm){
if (window.XDomainRequest) {
// много кода
}
})(jQFrm);
Находим здесь упоминание проприетарного XDomainRequest, который существовал только в IE8 и IE9. Не понимаю, зачем этот код нужен на мобильных устройствах.
Идём дальше:
var ets_scripts = jQFrm('[name="ets-anchor"]');
jQFrm(window).resize(function()
{
resizeIframe();
});
if (ets_scripts.length == 1)
{
// много кода
}
Встречаем второй большой if и первое вмешательства в обработку событий на произвольном мобильном сайте.
По этому селектору находится этот самый Anchor.js, который вставляется в момент перехвата ответа от веб-сервера мобильному браузеру.
![]()
Продолжаем чтение по диагонали:
(function()
{
var msgHandler = function(e)
{
var ets_frame = document.getElementById('toolBarPcFrame');
if (e.origin == 'http://toolbar.beeline.ru')
{
// 500 строк кода
}
else
{
//alert(e.origin);
throw new Error('Origin domain is not allowed.');
}
}
// listen for the message event
window.addEventListener('message', msgHandler, false);
})();
Находим механизм общения со встроенным iframe через window.postMessage.
Опять хелперы и...:
function GetWidth() {}
function GetHeight(){}
function resizeIframe(){}
window.onpageshow = function(evt){};
window.onorientationchange = function(){};
window.onload = function(){
//etsAppMain functions (search application)
if (InitParamForScroll)
{
InitParamForScroll();
}
};
function isLandscape(){}
function orientation_changed(actionId){}
if (isMobile()){}
Oh, wait. Вот и причина изначальной проблемы — подмена window.onload
window.onload = function()
{
//etsAppMain functions (search application)
if (InitParamForScroll)
{
InitParamForScroll();
}
};
В качестве быстрого решения, можно использовать примерно такой gist [5].
document.addEventListener('DOMContentLoaded', function() {
var toolbar = document.getElementById('toolbar'),
iframe = toolbar && toolbar.firstElementChild;
if (iframe && /beeline/.test(iframe.getAttribute('src'))) {
toolbar.parentNode.removeChild(toolbar);
}
});
setTimeout(function() {
var script = document.querySelector('script[name=ets-anchor]');
if (script) {
script.parentNode.removeChild(script);
}
}, 0);
Может я подумаю ещё пару минут чуть позже и придумаю более элегантное решение. Смотрите в gist :)
Кроме капитанского «не использовать onload», могу дать несколько полезных советов:
* если вы разработчик, по возможности, используйте SSL на ваших сайтах, чтобы _никто_ не мог перехватить ваш траффик;
* если вы подключили на свою страницу 90КБ jQuery кода, то используйте его, а не пишите потом document.getElementById;
* если вы оператор связи, то никогда не включайте такие услуги «Мини-Кабинет» [6];


* или такие homenet.beeline.ru/index.php?showtopic=315433 [7].
P.S. Этот пост ни в коем случае не наезд на компанию Билайн. Я имел положительный опыт решения проблем с помощью их твиттера @Beeline_Rus. Надеюсь, чтот этот пост поможет хорошим людям внутри компании вырезать эту странную услугу на корню и сделать лично меня, и миллионы абонентов чуточку счастливей.

Автор: sbmaxx
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/informatsionnaya-bezopasnost/65806
Ссылки в тексте:
[1] простенький тест: http://rozhdestvenskiy.ru/experiments/datauri/
[2] github: https://github.com/sbmaxx/datauri
[3] gist: https://gist.github.com/sbmaxx/4c1f8749fce13eba6ee0
[4] самописный код: https://gist.github.com/sbmaxx/27d839e5fa5520d1e3db
[5] gist: https://gist.github.com/sbmaxx/93023231373f2e105eef
[6] «Мини-Кабинет»: http://static.beeline.ru/upload/contents/366/Tulbar-usloviy-predostavleniy.pdf
[7] homenet.beeline.ru/index.php?showtopic=315433: http://homenet.beeline.ru/index.php?showtopic=315433
[8] Источник: http://habrahabr.ru/post/230921/
Нажмите здесь для печати.