Interacción entre un sitio en un navegador y un programa que se ejecuta localmente

A veces se hace necesario transferir datos entre una aplicación que se ejecuta en un navegador y un programa que se ejecuta en el mismo sistema en el que se ejecuta el navegador. Esto puede ser necesario, por ejemplo, si necesitamos trabajar con equipos conectados localmente. Lector de tarjetas inteligentes, clave de cifrado de hardware, etc.



Foto de aqui


Los primeros que vienen a la mente son tres formas de resolver este problema:


  1. Hazlo con las herramientas del navegador, o escribe complementos para ellas
  2. Organice el intercambio de datos a través del backend, actuando como intermediario.
  3. Agregue un servicio HTTP al programa y acceda directamente desde el navegador

El tercer elemento se ve bien, le permite eliminar la autorización en el programa, no requiere ninguna interfaz de usuario. Intentemos implementarlo escribiendo un programa en C # bajo .NET Framework 4. Como estamos hablando de .NET, la solución solo será para Windows (XP y más reciente). Haremos la aplicación web en angular.


¿Por qué no 1 y 2?


El primer elemento definitivamente traerá mucho dolor, tendrá que admitir los navegadores por separado, puede hacer mucho más que todo en los complementos del navegador. Sin embargo, teóricamente, es posible trabajar con tarjetas inteligentes a través de complementos. Pero necesitas una forma más simple.


El segundo punto es fácil de implementar, pero para este esquema tendrá que hacer una autorización no solo en el sitio, sino también en la aplicación local. Significa que se necesitará algún tipo de interfaz, pero al cambiar la contraseña, también se requerirá una nueva autorización en el programa. Además, en las redes corporativas habrá problemas adicionales con la red, a menudo tienen acceso a Internet a través de servidores proxy con filtrado y autorización severos, también tiene que hacer una interfaz para configurar servidores proxy y no siempre puede salir con la configuración automática. Será más difícil para un usuario alejado de TI trabajar con esto; crearemos más trabajo de soporte técnico. Por supuesto, puede crear un paquete de instalación individualmente para cada usuario para eliminar la necesidad de una autorización principal, pero esto solo aumentará los problemas.


¿Qué tiene que ver HTTPS con él?


Cuando un sitio se ejecuta a través de HTTPS, los navegadores bloquean la descarga de contenido activo mediante HTTP. Sin embargo, de acuerdo con la lógica de las cosas, los navegadores deben considerar que la solicitud a la máquina local a través de HTTP es segura y no deben bloquearla. Esto resultó no ser así.
La tabla muestra los resultados de un pequeño estudio sobre el comportamiento de los navegadores en la plataforma Windows:


Firefox 65Cromo 72IE 11
http: // localhost /❌ Bloqueado cargando contenido activo mixto✓❌ Error: acceso denegado 0x8007005
http://127.0.0.1/✓✓❌ Error: acceso denegado 0x8007005
https: // localhost /❌ SEC_ERROR_UNKNOWN_ISSUER✓✓

La tabla muestra el comportamiento de los navegadores cuando intenta realizar una solicitud en la dirección adecuada. Los navegadores en el motor Chromium se comportan de manera similar a Chrome, y el comportamiento de Edge 44 es similar al comportamiento de IE 11. Se emite un certificado válido para HTTPS, firmado con un certificado raíz autofirmado. El comportamiento para https://127.0.0.1 y https: // localhost es el mismo, solo para 127.0.0.1, entonces también debe emitir un certificado, y rara vez se encuentran certificados para direcciones IP, por lo que omitimos este punto.


Todo funciona en Chrome. Chrome e IE usan el almacén de certificados del sistema, por lo que HTTPS también funciona en ellos. Firefox usa su propio almacén de certificados, por lo que no confía en nuestro certificado autofirmado. Firefox e IE no confían en el nombre del host local, y eso es correcto, porque nadie garantiza que se resuelva a 127.0.0.1 (aunque podrían simplemente verificarlo como Chrome).


El principal problema: IE no permite acceder al programa a través de HTTP. Así que alboroto con los certificados que no podemos evitar.


Para trabajar con navegadores, también deberá especificar los encabezados correctos de Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers ( CORS ) en el programa.


Certificado SSL


Puede hacer un registro DNS para su dominio, por ejemplo local.example.com, que se resolverá en 127.0.0.1. Emita un certificado SSL para este dominio, distribúyalo con el programa. Deberá distribuir la clave privada de este certificado con el programa. Esto es completamente inadecuado. Y el certificado en el programa también deberá actualizarse.


IE no confiará en un certificado SSL autofirmado, debe estar firmado con un certificado raíz de confianza (y puede ser autofirmado).


Puede generar un certificado raíz y un certificado SSL y distribuirlos con el programa, agregándolos al almacén local de certificados. Se ve inseguro. Y también puede ser necesario revocar o renovar el certificado. Por lo tanto, generaremos certificados con claves directamente en la computadora del usuario al primer inicio del programa.


Crear certificados en C #


Para .NET, hay una biblioteca BouncyCastle que puede hacer todo lo que necesitamos. El único problema es que para agregar un certificado a la tienda, deberá solicitar un aumento de privilegios. También necesitará derechos elevados para usar netsh para asegurar el certificado en un puerto específico del sistema.


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

En el ejemplo, el método RegisterSslOnPort en la clase SslHelper hace este trabajo.


Servicio HTTP en el programa C #


Para crear un servidor HTTP (S) liviano, usamos la biblioteca Nancy . Nancy es un framework web ligero para .NET, simple y fácil de usar. Mucho se ha escrito sobre él, incluso sobre Habré . Gracias al módulo Nancy.SelfHosting, podemos alojar nuestra aplicación sin usar IIS.


Por ejemplo, creemos un punto final que aborde la suma de dos números. Aquí es importante establecer los encabezados CORS correctos, de lo contrario el navegador no ejecutará una solicitud a nuestra API.


Nancymodule
 public class CalcNancyModule : NancyModule { public CalcNancyModule() { // ,      After.AddItemToEndOfPipeline((ctx) => ctx.Response .WithHeader("Access-Control-Allow-Origin", GetOrigin(ctx)) .WithHeader("Access-Control-Allow-Methods", "POST,GET") .WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type")); Get["/Calc"] = _ => { //      .NET  }; Get["/Calc/Add"] = _ => { //       (num1 + num2) }; } private string GetOrigin(NancyContext ctx) { // Origin,     //    //      - // https://app.example.com return ctx.Request?.Headers["Origin"]?.FirstOrDefault() ?? ""; } } 

Agregue la inicialización de Nancy a nuestra aplicación, y estamos listos para la batalla.


Inicialización de Nancy
 var hostConfigs = new HostConfiguration(); hostConfigs.UrlReservations.CreateAutomatically = true; hostConfigs.RewriteLocalhost = false; var uris = new Uri[] { new Uri($"http://localhost:{HTTPPORT}"), new Uri($"http://127.0.0.1:{HTTPPORT}"), new Uri($"https://localhost:{HTTPSPORT}") }; using (var host = new NancyHost(hostConfigs, uris)) { host.Start(); } 

Al principio, debe generar certificados y colocarlos en la tienda, solicitando los derechos correspondientes. La clase SslHelper se usa para estas manipulaciones, en las que el único método público CheckOrCreateCertificates hace el trabajo. Como parámetros, se le pasan los certificados de SubjectName. El método verifica si los certificados necesarios están disponibles y el sistema, si no, los crea.


Para simular trabajo duro y largas demoras en el ejemplo, agregue Thread.Sleep (1000) a nuestras llamadas a la API.


En esta aplicación está lista para ejecutarse, vaya a la web.


Aplicación web


Como puede ver en la tabla de comportamiento del navegador, no se puede prescindir de un punto final; se deben usar al menos dos:



En una aplicación web, necesitamos determinar si estamos en IE (o Edge) - use HTTPS, si no - HTTP. Puede hacerlo más confiable y no descubrir en qué navegador estamos, pero solo intente ejecutar una solicitud al método GET / Calc de nuestra API, si la solicitud es exitosa, trabajamos, si no, intentamos con otro protocolo.


Todo esto es necesario solo si la aplicación web en sí usa HTTPS, porque cuando se usa el protocolo HTTP, los navegadores no imponen restricciones a las solicitudes, solo se necesitan los encabezados CORS correctos.


En la aplicación angular, cree un servicio InteractionService que verificará la disponibilidad del punto final local primero a través de HTTP, luego por HTTPS. La verificación se realiza mediante el método checkAvailability, y el resultado de la verificación está disponible al suscribirse a la variable $ disponible de tipo BehaviorSubject con el valor inicial falso.

Agregamos el trabajo de agregar números al componente AppComponent. Cuando hace clic en el botón "Calcular", la aplicación web solicita GET / Calc / Add? Num1 = {num1} & num2 = {num2}. La respuesta o error se muestra en el campo Resultado.


Al depurar, incluso a través de HTTPS, es posible que no note problemas, ya que el dominio para las solicitudes será el mismo: localhost. Por lo tanto, debe probar la aplicación con un nombre de dominio diferente.
Para simplificar el trabajo de implementación de una aplicación web tanto como sea posible, utilizamos el servicio https://stackblitz.com , este es un IDE web para angular y no solo con un poco de VSCode. La aplicación final está disponible en el enlace .


Y puedes cavar el código aquí .


La aplicación no funcionará interactivamente en stackblitz, debe abrirla en una pestaña privada separada o en otro navegador en https://angular-pfwfrm.stackblitz.io .


¿Cómo intentarlo?


La aplicación web se inicia convenientemente utilizando stackblitz, simplemente haciendo clic en el enlace https://angular-pfwfrm.stackblitz.io .


Puede ejecutar la aplicación web localmente.


Para esto necesitas

Para hacer esto, debe clonar el repositorio:


 git clone https://github.com/jdtcn/InteractionExample.git cd InteractionExample 

en la carpeta AngularWebApp necesita ejecutar los comandos:


 npm install ng serve --ssl true 

La aplicación web estará disponible en https: // localhost: 4200 /


La aplicación local se puede compilar a partir del ejemplo (abrir CsClientApp.sln desde la carpeta CsClientApp) usando Visual Studio y ejecutar, o usar el script para el programa LINQPad .


Si usted es un desarrollador de .NET y no usa LINQPad , asegúrese de leerlo, algo indispensable en el desarrollo. Para ejecutar el ejemplo, debe abrir el script en LINQPad'e (la primera vez que necesita ejecutar LINQPad con derechos de administrador para que se instalen los certificados) e instalar los paquetes de nouget BouncyCastle, Nancy, Nancy.Hosting.Self, luego ejecute el script. Después de eso, puede hacer clic en el botón "Calcular" en la aplicación web y obtener el resultado de la operación.


Seguridad


Es importante formar correctamente los encabezados CORS en una aplicación real para que los villanos de otros sitios no puedan acceder a nuestro programa. Si el villano tiene la oportunidad de trabajar con los privilegios del usuario en su computadora y omitir el cheque CORS, podrá hacer todo lo que nuestro programa puede hacer.


En cualquier caso, el programa debería funcionar con derechos mínimos, y si hace algo sensible con los documentos, debe agregarle solicitudes de confirmación de operaciones.


Conclusión


La tarea aparentemente simple resultó ser bastante voluminosa e incluso requirió muletas adicionales para trabajar con certificados.


Este enfoque funcionó bien en una aplicación real. Por supuesto, para usar el código del ejemplo, debe agregar el manejo normal de errores.


Es conveniente solicitar la elevación de privilegios al instalar el programa, usando InnoSetup es fácil hacer esto y pasar el atributo necesario en la primera ejecución del programa. Además, antes de instalar, es conveniente verificar la presencia de .NET 4 e instalarlo si no está instalado.


Nadie en Virustotal reacciona a este programa, ¡pero me gustaría! Pero si ensambla el paquete de instalación en InnoSetup, un par de antivirus de tercer nivel comenzarán a funcionar en él. Esto ayuda a deshacerse de la firma del instalador con el certificado de firma de código.


La actualización automática del programa aquí está detrás de escena, pero ciertamente no será superflua en una aplicación real. Squirrel es muy adecuado para gestionar actualizaciones automáticas. También es conveniente utilizar la ardilla para eliminar nuestros certificados del sistema cuando elimina el programa.


Código de muestra publicado en GitHub .


Gracias por su atencion!

Source: https://habr.com/ru/post/438166/


All Articles