Pull to refresh
133.6
Контур
Делаем сервисы для бизнеса

Кроссбраузерный запуск «злобного» кода на клиенте

Reading time 4 min
Views 14K
Пост будет интересен веб-разработчикам, заинтересованным в запуске небезопасного кода на клиенте (из браузера). Под «злобным» мы понимаем код, который мы не можем выполнить в чистом JavaScript’е (в нашем случае — подписание куска данных определенным сертификатом).

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

Вот как мы решали эту не самую тривиальную задачу.

Итак, нам необходимо запустить на машине клиента достаточно небезопасный код. Конечно, просто так никто нам это сделать не даст, но нас не покидала надежда, что с достаточным количеством вопросов «Вы уверены?» и «Вы доверяете этому сайту?» наша задумка таки пройдет.

На входе у нас была работающая ActiveX-библиотека, которая делала всё, что нам нужно, но только в IE. Нам же нужно было кросс-браузерное решение.

Сразу оговоримся о нашем понимании термина «кросс-браузерное». Согласно статистике, наши пользователи используют следующие браузеры:



IE 8+ (39%)
Mozilla Firefox 3.6+ (19%)
Opera 9+ (17%)
Chrome (14%)
IE 7 (6%)
IE 6 (4%)
Safari 5+ (1%)

Соответственно, нам требовалась корректная работа во всех этих браузерах.

Решение пришло довольно быстро. Идея заключается в следующем: пользователь на машину устанавливает небольшую программку, которая могла бы выполнять нужные нам действия. Взаимодействие же с ней можно осуществлять при помощи HTTP-запросов. Программка получилась действительно небольшой, она умела принимать входящие запросы специального вида и запускать соответствующие методы CryptoApi.

Оставалась понять, как же сделать нужный запрос на такой сервер. Сделать такой запрос с сервера не представлялось возможным — машина пользователя может не иметь внешнего ip-адреса по десятку разных причин. Значит, запрос надо выполнять из javascript-кода на адрес 127.0.0.1. С одной стороны, это существенно облегчало задачу идентификации выполняющего запрос и позволило обойтись без HTTPS, с другой — это означало, что нам придется делать кросс-доменный вызов, что само по себе сопряжено с рядом проблем. Тонкости кросс-доменных вызовов уже обсуждались на Хабре, например, в этой статье. Вооруженные таким образом богатым инструментарием, мы приступили к экспериментам.

CORS


В качестве первого подхода мы решили воспользоваться самым простым из имеющихся методов — Cross-Origin Resource Sharing. Эта технология поддерживается многими современными браузерами и проста в реализации. Но, к сожалению, она не поддерживается Оперой, а ее реализация в IE не дает возможности делать локальные вызовы с сайтов в категориях Internet и Intranet. И если с последним мы еще могли смириться (у нас в запасе был наш ActiveX-компонент), то поддержкой Оперы мы жертвовать не стали. От идеи написать расширение для Оперы, которое проксировало бы локальные вызовы с нужных адресов, нас отговорили интерфейсологи — да мы и сами не были уверены в том, что все пользователи разберутся с установкой расширения. Кроме того, его тоже пришлось бы своевременно обновлять… Пришлось искать другие решения.

Flash


Следующей нашей идеей было использовать для кросс-доменных запросов библиотеку flXHR. Но на этом пути нас снова подстерегали политики безопасности, на этот раз самого flash. Не успели мы переписать наш сервис на использование только GET-запросов (чтобы обойти запрет на POST запросы с https на http), как выяснилось, что flash-приложения, полученные из областей Internet и Intranet, помещаются в песочницу, изолированную от машины пользователя, в том числе и по http-запросам. Снова мимо.

JSONP


Тут же нас осенила новая идея. Возможность обойтись только GET-запросами означала, что мы можем, слегка подкорректировав формат запросов, воспользоваться технологией JSONP. Правим, запускаем — работает. Но это только в Хроме. Firefox — аналогично. А вот в Опере и в IE нас вновь поджидали проблемы. Выяснилось, что политики безопасности этих браузеров запрещают подключение скриптов, находящихся в более приватной зоне адресов. Агррррх!

Iframe




Следующий эксперимент — открыть iframe на 127.0.0.1 и как-то с ним общаться. И опять нас подвела безопасность Оперы. Эти свойства политик безопасности Оперы грозили поставить крест на нашей идее. Выяснилось, что при кросс-доменном обращении к машине пользователя, включая подключение скриптов, redirect и открытие в iframe, Опера подкладывает вместо ответа служебную страницу opera:crossnetworkwarning, в которой пользователь может подтвердить свое желание осуществить переход на локальную машину (естественно, при загрузке скрипта с локальной машины, пользователь этой страницы не увидит и подтвердить ничего не сможет). Единственное исключение — это когда переход в явном виде осуществлен пользователем (например, при клике на ссылку <a href="http://localhost/" target="my_frame">).

Iframe + PostMessage


Получается, что каким-никаким образом, но iframe с локальным адресом открыть можно. Но тогда можно и общаться с локальным сервером посредством postMessage! Осталось только выбрать, как открывать локальный фрейм. Возможностей, собственно, две: либо показывать пользователю ссылку, которая будет выполнять в скрытом фрейме переход на локальный адрес, либо выполнять переход самим и предоставить пользователю самостоятельно разрешить переход. Поколебавшись, мы выбрали второй путь. Преимущество его в том, что на странице opera:crossnetworkwarning (как видно на скриншоте) есть возможность запомнить разрешение для конкретного домена, в результате лишнее действие потребуется от пользователя всего один раз. Кроме того, этот способ позволяет повторять попытку обращения к сервису без участия пользователя, в том случае, когда он не установлен или не запущен, и запустить требуемый метод, как только сервис станет доступен.

Итог


postMessage не работает в IE 6-7 и в Firefox 3, но мы решили не добавлять на этот случай костылей a-la hash-polling. Для пользователей IE мы оставили ActiveX-компонент, а пользователям Firefox 3 (которых у нас считанные единицы) мы решили предложить обновиться до более поздних версий.

Ну и чуть-чуть пиара (проект хороший, поэтому надеюсь на понимание). Если ваша жена или мама — бухгалтер и входят в сотню тысяч несчастных женщин, рассчитывающих зарплату в программе-монстре, — порекомендуйте им посмотреть Эврику (так называется наш сервис). Хватит уже мучиться от всякой непотребщины.
Tags:
Hubs:
+38
Comments 50
Comments Comments 50

Articles

Information

Website
tech.kontur.ru
Registered
Founded
Employees
5,001–10,000 employees
Location
Россия