Pull to refresh

Определяем, что у пользователя заблокирована Википедия

Reading time 5 min
Views 26K

Недавно Роскомнадзор предпринял попытку блокировки доступа с территории РФ к Википедии. Попытка провалилась, и самое время перейти в контрнаступление. В статье под катом я покажу, что, если пользователь Х зашёл на сайт А, то сайт А может определить, заблокирован ли для пользователя другой сайт Б. А дальше, по намечающейся традиции, изложение пойдёт в вопросно-ответной форме.

Для чего это вообще нужно?


При обсуждении блокировки Википедии сообщество заметило два обстоятельства, которые весьма выгодны цензуре. Во-первых, Википедия использует HTTPS и, более того, HSTS. Это означает, что заблокирована энциклопедия может быть только целиком (с точностью до домена), но при этом перенаправить на пресловутую страницу с сообщением о блокировке нельзя. Вместо этого высвечивается сообщение об ошибке установления соединения. Это выглядит как внутренняя проблема Википедии и смягчает недовольство пользователя действиями Роскомнадзора; в то же время для развития правового государства необходима обратная связь. Во-вторых, даже если пользователи узнают о блокировке, многие из них просто не будут знать, что делать. Вешать перманентную плашку «Если заблокировали Википедию — качайте Tor Browser!» на неравнодушных сайтах как минимум странно; в то же время, оповещение пользователей о блокировке Википедии и способах её обхода необходимо. Решение поставленной проблемы и пытается дать настоящая статья.

Что это вообще такое?


Это — достаточно универсальный способ определить доступность практически любого (предварительно исследованного) сайта на стороне клиента, средствами Javascript.

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

Хочу демку!


Пожалуйста!
Та самая занимательная геометрия - распечатать при запрете кроссдоменных событий для изображений

Как это работает?


Создаётся невидимый блок HTML-кода, содержащий два изображения: одно контрольное — с заведомо несуществующего URL, второе сигнальное — любое изображение с проверяемого сайта. Если их размеры через некоторое время совпадают — значит, изображение с проверяемого сайта не загрузилось, то есть он либо «лежит» настолько, что не может отдавать даже статику, либо заблокирован.

А подробнее?


Изображение в HTML, вставлемое тэгом img, по умолчанию имеет размеры, определяемые самим файлом-картинкой. Изображение, которое загрузить не удалось, тоже имеет какие-то размеры — в разных браузерах разные. Вот они (ширина x высота):
  • Firefox 37.0 — 24x24
  • Chrome 44.0.2403.155 — 0x0 (до загрузки), 20x20 (при неудачной загрузке)
  • Opera 12.16 — 114x22
Получить размеры элемента на Javascript можно с помощью свойств элемента .offsetWidth и .offsetHeight

А если вдруг сигнальное изображение по размерам совпадёт с тем, что отобразит браузер при ошибке?


Это достаточно маловероятно, учитывая, что сравниваются и ширина, и высота. Можно запросить два изображения заведомо разных размеров и сравнить их полученные размеры. Если совпадут — загрузка прошла неуспешно. И всё же фавиконы в качестве единственных изображений лучше не дёргать.

А исходники?


На гитхабе под GPLv3.

И как этим всем пользоваться?


Постарался сделать код читабельным, хотя, конечно, это не освобождает меня от приведения примера.
	chasAntidot.testSiteWithImg({
		url: 'https://ru.wikipedia.org/static/images/project-logos/ruwiki.png',	//URL картинки-детектора
		ifBlocked: function(){message('заблокирована');},	//callback, если заблокировано
		ifNotBlocked: function(){message('не заблокирована');},	//callback, если не заблокировано
		time: 3500, //Время ожидания ответа в миллисекундах, по умолчанию 4000
		secondImage: 'https://ru.wikipedia.org/favicon.ico',	//URL второй картинки - необязательно
	});
Никаких дополнительных библиотек типа jQuery не требуется, всё на чистом JS.

Хорошо, я готов информировать своих пользователей о возможной блокировке Википедии! Можно мне готовый код?


Во-первых, в любом случае саму библиотеку лучше скачать к себе на сайт. github.io не застрахован от блокировок. Во-вторых, для чистого JS возможен, например, такой вариант:
chasAntidot.createBanner(
	'Вероятно, <a href="https://ru.wikipedia.org">Википедия</a> заблокирована. <a href="http://rublacklist.net" target="_blank">Узнайте, что нужно сделать!</a>',
	{
		url: 'https://ru.wikipedia.org/static/images/project-logos/ruwiki.png',
		secondImage: 'https://ru.wikipedia.org/favicon.ico',
	}
);
Это, конечно, работает, но не очень красиво :) Патчи, добавляющие функции создания баннеров с использованием различных библиотек, приветствуются!

А если действительно Википедия упадёт?


На этот случай можно использовать схожий механизм, сравнивая доступность картинки напрямую и через какой-нибудь анонимайзер. Ресурс в таком случае можно считать заблокиованным тогда и только тогда, когда через анонимайзер картинка видна, а напрямую — нет. Правда, анонимайзер тоже может быть заблокирован, и тогда придётся проверять ещё и его доступность — например, запрашивая через него картинку с сайта самого РКН.

Где ещё есть толк от этого в народном хозяйстве?


  • Мобильная версия Википедии сможет точно вовремя предупредить пользователей о блокировке десктопной (они на разных адресах и на разных доменах), и наоборот.
  • Вебмастер, установив на страницу скрипт с аналогичным принципом действия, сможет на лету менять ссылки на конечное количество заблокированных ресурсов на ссылки через прокси. Готового решения нет, но патчи принимаются.
  • Можно точно выявлять пользователей, у которых заблокирован (неважно, госцензурой или работодателем) любимый сайт (ВКонтакте и т. д.) и предлагать им купить VPN/прокси или использовать бесплатный прокси с рекламой.
  • МожноНельзя на клиенте определить факт использования TOR/I2P, запросив таким образом картинку с домена .onion / .i2p
  • Хотите знать, активен ли у вашего пользователя IPv6? Просто загрузите картинку на какой-нибудь IPv6-адрес. Или воспользуйтесь картинками на серверах кого-нибудь крупного (типа Гугла), поддерживающего новый протокол.
  • А ещё в некоторых случаях можно определить DNS, которым пользуется клиент. Например, OpenNIC и прочие NameCoin/.bit
  • В веб-интерфейсах роутеров тоже бывают картинки! А значит, модель роутера при желании тоже можно определить, даже если веб-интерфейс извне недоступен.
  • Наконец, можно проверять принадлежность к определённой сети. Например, у некоторых провайдеров есть целые поддомены с сайтами, доступными только своим абонентам. Неужели там не найдётся ни одной картинки? Можно проверять и принадлежность к корпоративным сетям — если, конечно, знать, какие ресурсы доступны только изнутри. Может быть, вы хотите показывать что-то особенное пользователям, заходящим в сеть по вайфаю из московского метро? Из распространённой сети кафе? Тогда стоит поискать на соответствующей странице авторизации картинки, доступные только изнутри! Хотя нельзя не признать, что определение по IP иногда более оправданно.
  • Нельзя с помощью chrome extension URLs проверить наличие любого расширения, в котором есть отдельные файлы-картинки. У меня всегда получается «битая» картинка.
  • В некоторых старых браузерах (IE6?), не блокирующих доступ к локальным картинкам, вероятно, можно с довольно неплохой точностью определять версии установленных у пользователя прикладных программ (Libre Office, GIMP, MS Office, Adobe Photoshop и т. д.) — достаточно знать, какие картинки какого размера характерны для каждой из версий. Конечно, это сработает, только если программа установлена в директорию по умолчанию, но, например, такой приём позволит вывести на сайте инструкции по установке программы, отсутствующей у пользователя, но необходимой для работы с сайтом. В современных браузерах, скорее всего, не работает.
Tags:
Hubs:
+24
Comments 38
Comments Comments 38

Articles