📜 ⬆️ ⬇️

The interaction of the site in the browser and locally running programs

Sometimes it becomes necessary to transfer data between an application running in a browser and a program running on the same system on which the browser is running. This may be necessary, for example, if we need to work with equipment connected locally. Smart card reader, hardware encryption key and so on.



Picture from here


Three ways to solve this problem come to mind first:


  1. Do it with the means of browsers, or write plugins for them.
  2. To organize data exchange through the backend, acting as an intermediary
  3. Add an HTTP service to the program and access it directly from the browser

The third item looks good, allows you to remove the authorization in the program, does not require any user interface at all. Let's try to implement it by writing a program in C # under the .NET Framework 4. Since it will be a question of .NET, the solution will be only for Windows (XP and later). We make the web application angular.


Why not 1 and 2?


The first item will definitely bring a lot of pain, you will have to support browsers separately, you can do far from everything in browser plugins. Nevertheless, theoretically, it is possible to work with smart cards through plugins. But you need a way easier.


The second item is easy to implement, but for this scheme you will have to do authorization not only on the site, but also in the local application. This means that some kind of interface is required, but the interface, if you change the password, you will need to re-authorize in the program. Plus, corporate networks will have additional problems with the network, they often have access to the Internet through proxy servers with harsh filtering and authorization, to configure a proxy, you also have to create an interface, it is not always possible to get rid of automatic detection of settings. It will be more difficult for a user far from IT to work with this, we will create more work for technical support. Of course, you can create an installation package for each user individually, to eliminate the need for initial authorization, but this will only add problems.


What does it have to do with HTTPS?


When the site is working on HTTPS, browsers block the download of active content using HTTP. However, logically, the request to the local machine via HTTP browsers should be considered secure , and should not block it. This turned out to be not quite so.
The table shows the results of a small study of browser behavior on the Windows platform:


Firefox 65Chrome 72IE 11
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

The table shows the behavior of browsers when trying to make a request to the appropriate address. Browsers on the Chromium engine behave similarly to Chrome, and the Edge 44's behavior is similar to that of IE 11. For HTTPS, a valid certificate has been issued, signed with a self-signed root certificate. The behavior for https://127.0.0.1 and https: // localhost is the same, just for 127.0.0.1 then you also need to issue a certificate, and certificates for IP addresses are rarely found, so omit this point.


Everything works in Chrome. Chrome and IE use the system certificate store, so HTTPS also works in them. Firefox uses its own certificate store, so it does not trust our self-signed certificate. Firefox and IE do not trust the localhost name, and rightly so, because no one guarantees that it will resolve to 127.0.0.1 (although they could just check it out, as Chrome does).


Main problem: IE does not allow access to the program via HTTP. So fuss with certificates, we can not avoid.


To work with browsers, you also need to specify the correct headers Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers ( CORS ) in the program.


SSL certificate


You can make a DNS record for your domain, for example local.example.com, which will be resolved to 127.0.0.1. To issue an SSL certificate for this domain, distribute it along with the program. We'll have to distribute the private key of this certificate along with the program. It is absolutely no good. And the certificate in the program also needs to be updated.


IE will not trust a self-signed SSL certificate, it must be signed with a trusted root certificate (and it can be self-signed).


You can generate a root certificate and an SSL certificate and distribute them with the program by adding them to the local certificate store. Looks unsafe. And it may also be necessary to revoke or renew a certificate. Therefore, we will generate certificates with keys directly on the user's computer when you first start the program.


Creating certificates in C #


For .NET, there is a BouncyCastle library that can do everything we need. The only problem is that in order to add a certificate to the repository you will have to request elevation of rights. Also elevated rights will be needed to secure the certificate to a specific port in the system using netsh.


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

In the example, this work is done by the RegisterSslOnPort method in the SslHelper class.


HTTP service in C # program


To create a lightweight HTTP (S) server, use the Nancy library . Nancy is a lightweight .NET web framework, simple and easy to use. Much has been written about him, including on Habré . Thanks to the Nancy.SelfHosting module, we can host our application without using IIS.


For example, create an endpoint that adds two numbers. It is important here to set the correct CORS headers, otherwise the browser will not fulfill the request to our 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() ?? ""; } } 

Add Nancy initialization to our application, and we are ready for battle.


Initialization 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(); } 

When you first start, you need to generate certificates and place them in the store, requesting the appropriate rights. For these manipulations, the SslHelper class is used , in which the only public method CheckOrCreateCertificates does this work. SubjectName certificates are passed as parameters. The method checks whether the necessary certificates are available and the system, if not - creates them.


To simulate hard work and long delays in the example, add Thread.Sleep (1000) to our API calls.


On this application is ready to launch, move on to the web.


Web application


As it is clear from the table of browser behavior, it’s impossible to manage with any one endpoint, you’ll have to use at least two:



In the web application, we need to determine if we are in IE (or Edge) - use HTTPS, if not - HTTP. You can make it more reliable and not find out which browser we are in, but just try to execute the request to the GET / Calc method of our API, if the request is successful, we work, if not, try another protocol.


All this is needed only if the web application itself uses HTTPS, because when using the HTTP protocol, browsers do not impose restrictions on requests, only the correct CORS headers are needed.


In the angular - application, we will create the InteractionService service, which will perform the availability check of the local endpoint, first via HTTP, then via HTTPS. The check is performed by the checkAvailability method, and the result of the check is available when subscribing to the available $ variable of BehaviorSubject type with an initial value of false.

Work on the addition of numbers put in the component AppComponent. When you click the “Calculate” button, the web application makes a request to GET / Calc / Add? Num1 = {num1} & num2 = {num2}. The answer or error is displayed in the Result field.


When debugging, even over HTTPS, you may not notice any problems, since the domain for the requests will be the same - localhost. Therefore, you need to test the application with a different domain name.
In order to maximally simplify the work on the deployment of a web application, we will use the service https://stackblitz.com , this is a web IDE for angular and not only, with a taste of VSCode. The finished application is available here .


And you can pick the code here .


In online mode on stackblitz, the application will not work, you need to open it in a separate private tab, or in another browser at https://angular-pfwfrm.stackblitz.io .


How to try?


A web application is conveniently launched using stackblitz, simply by following the link https://angular-pfwfrm.stackblitz.io .


You can run the web application locally.


For this you need

for this you need to clone the repository:


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

in the AngularWebApp folder, you need to run the following commands:


 npm install ng serve --ssl true 

The web application will be available at https: // localhost: 4200 /


You can either compile the local application from the example (open CsClientApp.sln from the CsClientApp folder) using Visual Studio and run it, or use the script for the LINQPad program.


If you are a .NET developer and do not use LINQPad , be sure to read about it, an indispensable thing in development. To run the example, you need to open a script in LINQPad (the first time you need to run LINQPad with administrator rights to install certificates), and install the nuget-packages BouncyCastle, Nancy, Nancy.Hosting.Self, then run the script. After that, you can click the “Calculate” button in the web application, and get the result of the operation.


Security


It is important to correctly form the CORS headers in a real application so that the villains from other sites cannot contact our program. If the villain has the opportunity to work with the privileges of the user on his computer and bypass the CORS check, then he can do everything that our program can do.


In any case, the program should work with minimal rights, and if it does something sensitive with documents, it is necessary to add requests for confirmation of operations to it.


Conclusion


The seemingly uncomplicated task actually turned out to be quite voluminous, and, moreover, requiring additional crutches for working with certificates.


This approach showed itself well in a real application. Of course, to use the code from the example, you need to add normal error handling.


Privilege escalation is convenient to request when installing a program, using InnoSetup is easy to do, and passing the desired attribute when you first start the program. Also, before installing it is convenient to check the availability of .NET 4, and install it if it is not installed.


Nobody on Virustotal reacts to this program, but we would like to! But if you assemble the installation package in InnoSetup, a pair of third-party antiviruses starts to work on it. It helps to get rid of this signing an installer with the help of code signing certificate.


The automatic update of the program is left behind the scenes, but it definitely will not be superfluous in a real application. Squirrel is well suited for managing automatic updates. Also, using squirrel, it is convenient to remove our certificates from the system when you uninstall the program.


The sample code is laid out on GitHub .


Thanks for attention!



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