» » Взаимодействие сайта в браузере и локально запущенной программы

 

Взаимодействие сайта в браузере и локально запущенной программы

Автор: admin от 30-01-2019, 23:00, посмотрело: 40

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



Взаимодействие сайта в браузере и локально запущенной программы

Картинка отсюда



Первыми приходят в голову три способа решить эту задачу:




  • Обойтись средствами браузеров, или написать плагины к ним

  • Организовать обмен данными через backend, выступающий в роли посредника

  • Добавить в программу HTTP-сервис, и обращаться к ней напрямую из браузера



  • Третий пункт выглядит хорошо, позволяет убрать авторизацию в программе, не требует вообще никакого пользовательского интерфейса. Попробуем его реализовать, написав программу на C# под .NET Framework 4. Так как речь пойдет о .NET, решение будет только для Windows (XP и новее). Веб-приложение сделаем на angular.

    http://localhost/
    ? Blocked loading mixed active content
    ?
    ? Error: Access is denied 0x8007005


    http://127.0.0.1/
    ?
    ?
    ? Error: Access is denied 0x8007005


    https://localhost/
    ? SEC_ERROR_UNKNOWN_ISSUER
    ?
    ?




    В таблице приведено поведение браузеров при попытке сделать запрос по соответствующему адресу. Браузеры на движке Chromium ведут себя аналогично Chrome, а поведение Edge 44 аналогично поведению IE 11. Для HTTPS выпущен валидный сертификат, подписанный самоподписанным корневым сертификатом. Поведение для https://127.0.0.1 и https://localhost одинаковое, просто для 127.0.0.1 тогда нужно тоже выпускать сертификат, а сертификаты для IP адресов редко встречаются, так что опустим этот момент.



    В Chrome всё работает. Chrome и IE используют системное хранилище сертификатов, поэтому в них работает и HTTPS. Firefox использует собственное хранилище сертификатов, поэтому не доверяет нашему самоподписанному сертификату. Firefox и IE не доверяют имени localhost, и это правильно, ведь никто не гарантирует, что оно разрешится в 127.0.0.1 (хотя они могли просто это проверить, как делает Chrome).



    Главная проблема: IE не даёт обратиться к программе по HTTP. Значит возни с сертификатами нам не избежать.



    Для работы с браузерами также потребуется указывать в программе правильные заголовки Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers (CORS).



    SSL сертификат



    Можно сделать DNS-запись для своего домена, например local.example.com, которая будет разрешаться в 127.0.0.1. Выпустить для этого домена SSL сертификат, распространять его вместе с программой. Придётся распространять закрытый ключ этого сертификата вместе с программой. Это совершенно не годится. А сертификат в программе еще и нужно будет обновлять.



    IE не будет доверять самоподписанному SSL сертификату, его надо подписать доверенным корневым сертификатом (а он может быть и самоподписанный).



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



    Создание сертификатов в C#



    Для .NET есть библиотека BouncyCastle, умеющая всё что нам нужно. Единственная проблема – для добавления сертификата в хранилище придётся запросить повышение прав. Также повышенные права понадобятся, чтобы с помощью netsh закрепить сертификат за определённым портом в системе.



    netsh http add sslcert ipport=0.0.0.0:{PORT} certhash={certThumbprint}


    В примере эту работу делает метод RegisterSslOnPort в классе SslHelper.



    HTTP-сервис в программе на C#



    Для создания легковесного HTTP(S)-сервера воспользуемся библиотекой Nancy. Nancy – это легкий веб-фреймворк для .NET, простой и удобный в работе. Про него много написано, в том числе и на Хабре. Благодаря модулю Nancy.SelfHosting мы можем хостить наше приложение без использования IIS.



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





    Добавим инициализацию Nancy в наше приложение, и мы готовы к бою.





    При первом запуске нужно сгенерировать сертификаты и поместить их в хранилище, запросив при этом соответствующие права. Для этих манипуляций служит класс SslHelper, в котором единственный публичный метод CheckOrCreateCertificates делает эту работу. В качестве параметров ему передаются SubjectName сертификатов. Метод проверяет нет ли нужных сертификатов и системе, если нет — создаёт их.



    Для симуляции тяжелой работы и долгих задержек в примере добавим Thread.Sleep(1000) в вызовы нашего API.



    На этом приложение готово к запуску, перейдём к вебу.



    Веб-приложение



    Как понятно из таблицы поведения браузеров, каким-то одним эндпоинтом обойтись не получится, придётся использовать как минимум два:



    В веб-приложении нам нужно определить, если мы в IE (или Edge) – использовать HTTPS, если нет – HTTP. Можно сделать надёжнее и не выяснять в каком мы браузере, а просто попробовать выполнить запрос к методу GET /Calc нашего API, если запрос успешен – работаем, если нет – пробуем другой протокол.



    Всё это нужно только если веб-приложение само использует HTTPS, потому что при использовании протокола HTTP, браузеры не накладывают ограничений на запросы, нужны только правильные заголовки CORS.



    В angular – приложении создадим сервис InteractionService, который будет выполнять проверку доступности локального эндпоинта сначала по HTTP, потом по HTTPS. Проверку выполняет метод checkAvailability, а результат проверки доступен при подписке на переменную available$ типа BehaviorSubject с начальным значением false.

    Работу по сложению чисел поместим в компонент AppComponent. При нажатии кнопки “Calculate”, веб-приложение делает запрос к GET /Calc/Add?num1={num1}&num2={num2}. Ответ или ошибка отображается в поле Result.



    При отладке, даже по HTTPS, можно не заметить проблем, так как домен для запросов будет один и тот же – localhost. Поэтому тестировать приложение нужно с другим доменным именем.

    Чтобы максимально упростить работу по развертыванию веб-приложения воспользуемся сервисом https://stackblitz.com, это веб-IDE для angular и не только, со вкусом VSCode. Готовое приложение доступно по ссылке.



    А поковырять код можно здесь.



    В интерактивном режиме на stackblitz приложение не будет работать, нужно открыть его в отдельной приватной вкладке, или в другом браузере по адресу https://angular-pfwfrm.stackblitz.io.



    Как попробовать?



    Веб-приложение удобно запустить с помощью stackblitz, просто перейдя по ссылке https://angular-pfwfrm.stackblitz.io.



    Можно запустить веб-приложение локально.





    Локальное приложение можно либо скомпилировать из примера (открыть CsClientApp.sln из папки CsClientApp) с помощью Visual Studio и запустить, либо использовать скрипт для программы LINQPad.



    Если вы .NET-разработчик, и не пользуетесь LINQPad, обязательно почитайте про него, незаменимая вещь в разработке. Чтобы запустить пример, нужно открыть в LINQPad’e скрипт (первый раз нужно запустить LINQPad с правами администратора, чтобы установились сертификаты), и установить nuget-пакеты BouncyCastle, Nancy, Nancy.Hosting.Self, потом запустить скрипт. После этого можно нажать кнопку “Calculate” в веб-приложении, и получить результат выполнения операции.



    Безопасность



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



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



    Вывод



    Несложная на вид задача на деле оказалась довольно объемной, да еще и требующей дополнительных костылей для работы с сертификатами.



    Этот подход хорошо себя показал в реальном приложении. Конечно, чтобы использовать код из примера, нужно добавить нормальную обработку ошибок.



    Повышение привилегий удобно запрашивать при установке программы, с помощью InnoSetup это легко сделать, и передать при первом запуске программе нужный атрибут. Также перед установкой удобно проверять наличие .NET 4, и устанавливать его, если не установлен.



    Никто на Virustotal на эту программу не реагирует, а хотелось бы! Зато если собрать установочный пакет в InnoSetup, пара третьесортных антивирусов начинает на него срабатывать. Помогает от этого избавиться подписывание установщика с помощью code signing certificate.



    Автоматическое обновление программы здесь оставлено за кадром, но оно точно не будет лишним в реальном приложении. Для управления автоматическим обновлением хорошо подходит Squirrel. Еще с помощью squirrel удобно удалить наши сертификаты из системы при удалении программы.



    Код примера выложен на GitHub.



    Спасибо за внимание!



    Источник: Хабр / Интересные публикации

    Категория: Программирование

    Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
    Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
    Введите два слова, показанных на изображении: *