Interaction entre un site dans un navigateur et un programme exécuté localement

Parfois, il devient nĂ©cessaire de transfĂ©rer des donnĂ©es entre une application exĂ©cutĂ©e dans un navigateur et un programme s'exĂ©cutant sur le mĂȘme systĂšme sur lequel le navigateur s'exĂ©cute. Cela peut ĂȘtre nĂ©cessaire, par exemple, si nous devons travailler avec des Ă©quipements connectĂ©s localement. Lecteur de carte Ă  puce, clĂ© de cryptage matĂ©riel, etc.



Photo d'ici


Les premiers à penser sont trois façons de résoudre ce problÚme:


  1. Faites avec les outils du navigateur ou écrivez des plugins pour eux
  2. Organiser l'échange de données via le backend, en agissant comme intermédiaire
  3. Ajoutez un service HTTP au programme et accédez-y directement depuis le navigateur

Le troisiÚme élément semble bon, vous permet de supprimer l'autorisation dans le programme, ne nécessite aucune interface utilisateur. Essayons de l'implémenter en écrivant un programme en C # sous le .NET Framework 4. Puisque nous parlons de .NET, la solution ne sera que pour Windows (XP et plus récent). Nous allons rendre l'application web angulaire.


Pourquoi pas 1 et 2?


Le premier élément apportera certainement beaucoup de douleur, vous devrez prendre en charge les navigateurs séparément, vous pouvez faire loin de tout dans les plug-ins de navigateur. Néanmoins, théoriquement, il est possible de travailler avec des cartes à puce via des plugins. Mais vous avez besoin d'un moyen plus simple.


Le deuxiĂšme point est facile Ă  mettre en Ɠuvre, mais pour ce schĂ©ma, vous devrez faire une autorisation non seulement sur le site, mais aussi dans l'application locale. Cela signifie qu'une sorte d'interface sera nĂ©cessaire, mais lors du changement de mot de passe, une nouvelle autorisation dans le programme sera Ă©galement requise. De plus, dans les rĂ©seaux d'entreprise, il y aura des problĂšmes supplĂ©mentaires avec le rĂ©seau, ils ont souvent accĂšs Ă  Internet via des proxy avec filtrage et autorisation sĂ©vĂšres, vous devez Ă©galement crĂ©er une interface pour configurer les proxy et vous ne pouvez pas toujours vous dĂ©barrasser des paramĂštres automatiques. Il sera plus difficile pour un utilisateur loin de l'informatique de travailler avec cela; nous allons crĂ©er plus de travail de support technique. Bien sĂ»r, vous pouvez crĂ©er un package d'installation individuellement pour chaque utilisateur afin de supprimer le besoin d'autorisation principale, mais cela ne fera qu'ajouter aux problĂšmes.


Qu'est-ce que HTTPS a Ă  voir avec cela?


Lorsqu'un site s'exécute sur HTTPS, les navigateurs bloquent le téléchargement de contenu actif via HTTP. Cependant, selon la logique des choses, les navigateurs devraient considérer la demande à la machine locale via HTTP comme sûre et ne devraient pas la bloquer. Ce ne fut pas tout à fait le cas.
Le tableau présente les résultats d'une petite étude du comportement des navigateurs sur la plateforme Windows:


Firefox 65Chrome 72IE 11
http: // localhost /❌ Chargement de contenu actif mixte bloquĂ©âœ“âŒ Erreur: l'accĂšs est refusĂ© 0x8007005
http://127.0.0.1/✓✓❌ Erreur: l'accĂšs est refusĂ© 0x8007005
https: // localhost /❌ SEC_ERROR_UNknown_ISSUER✓✓

Le tableau montre le comportement des navigateurs lors d'une tentative de demande Ă  l'adresse appropriĂ©e. Les navigateurs du moteur Chromium se comportent de maniĂšre similaire Ă  Chrome et le comportement d'Edge 44 est similaire Ă  celui d'IE 11. Un certificat valide est Ă©mis pour HTTPS, signĂ© avec un certificat racine auto-signĂ©. Le comportement pour https://127.0.0.1 et https: // localhost est le mĂȘme, juste pour 127.0.0.1, vous devez Ă©galement Ă©mettre un certificat, et les certificats pour les adresses IP sont rarement trouvĂ©s, alors sautons ce point.


Tout fonctionne dans Chrome. Chrome et IE utilisent le magasin de certificats systÚme, donc HTTPS y fonctionne également. Firefox utilise son propre magasin de certificats, il ne fait donc pas confiance à notre certificat auto-signé. Firefox et IE ne font pas confiance au nom de l'hÎte local, et c'est vrai, car personne ne garantit qu'il se résout en 127.0.0.1 (bien qu'ils puissent simplement le vérifier comme Chrome le fait).


Le principal problÚme: IE ne permet pas d'accéder au programme via HTTP. Alors agitez les certificats que nous ne pouvons pas éviter.


Pour travailler avec les navigateurs, vous devrez Ă©galement spĂ©cifier les en-tĂȘtes corrects Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers ( CORS ) dans le programme.


Certificat SSL


Vous pouvez crĂ©er un enregistrement DNS pour votre domaine, par exemple local.example.com, qui sera rĂ©solu en 127.0.0.1. Émettez un certificat SSL pour ce domaine, distribuez-le avec le programme. Vous devrez distribuer la clĂ© privĂ©e de ce certificat avec le programme. C'est totalement inadaptĂ©. Et le certificat du programme devra Ă©galement ĂȘtre mis Ă  jour.


IE ne fera pas confiance Ă  un certificat SSL auto-signĂ©, il doit ĂȘtre signĂ© avec un certificat racine de confiance (et il peut ĂȘtre auto-signĂ©).


Vous pouvez gĂ©nĂ©rer un certificat racine et un certificat SSL et les distribuer avec le programme, en les ajoutant au magasin de certificats local. Cela semble dangereux. Et il peut Ă©galement ĂȘtre nĂ©cessaire de rĂ©voquer ou de renouveler le certificat. Par consĂ©quent, nous gĂ©nĂ©rerons des certificats avec des clĂ©s directement sur l'ordinateur de l'utilisateur au premier dĂ©marrage du programme.


Création de certificats en C #


Pour .NET, il existe une bibliothÚque BouncyCastle qui peut faire tout ce dont nous avons besoin. Le seul problÚme est que pour ajouter un certificat au magasin, vous devrez demander une augmentation des privilÚges. Vous aurez également besoin de droits élevés pour utiliser netsh pour sécuriser le certificat sur un port spécifique du systÚme.


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

Dans l'exemple, la méthode RegisterSslOnPort de la classe SslHelper fait ce travail.


Service HTTP dans le programme C #


Pour créer un serveur HTTP (S) léger, nous utilisons la bibliothÚque Nancy . Nancy est un framework web léger pour .NET, simple et facile à utiliser. Beaucoup a été écrit sur lui, y compris sur Habré . Grùce au module Nancy.SelfHosting, nous pouvons héberger notre application sans utiliser IIS.


Par exemple, crĂ©ons un point de terminaison traitant de l'addition de deux nombres. Il est important ici de dĂ©finir les en-tĂȘtes CORS corrects, sinon le navigateur n'exĂ©cutera pas de demande Ă  notre 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() ?? ""; } } 

Ajoutez l'initialisation Nancy Ă  notre application et nous sommes prĂȘts pour la bataille.


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

Au premier démarrage, vous devez générer des certificats et les placer dans le magasin, en demandant les droits appropriés. La classe SslHelper est utilisée pour ces manipulations, dans lesquelles la seule méthode publique CheckOrCreateCertificates fait le travail. En tant que paramÚtres, des certificats SubjectName lui sont transmis. La méthode vérifie si les certificats nécessaires sont disponibles et sinon, le systÚme les crée.


Pour simuler un travail acharné et de longs retards dans l'exemple, ajoutez Thread.Sleep (1000) à nos appels d'API.


Sur cette application est prĂȘte Ă  fonctionner, allez sur le Web.


Application Web


Comme vous pouvez le voir dans le tableau de comportement du navigateur, un endpoint ne peut pas ĂȘtre supprimĂ©; au moins deux doivent ĂȘtre utilisĂ©s:



Dans une application Web, nous devons déterminer si nous sommes dans IE (ou Edge) - utilisez HTTPS, sinon - HTTP. Vous pouvez le rendre plus fiable et ne pas savoir dans quel navigateur nous nous trouvons, mais essayez simplement d'exécuter une demande à la méthode GET / Calc de notre API, si la demande est réussie, nous travaillons, sinon, nous essayons un autre protocole.


Tout cela n'est nĂ©cessaire que si l'application Web elle-mĂȘme utilise HTTPS, car lors de l'utilisation du protocole HTTP, les navigateurs n'imposent pas de restrictions sur les demandes, seuls les en-tĂȘtes CORS corrects sont nĂ©cessaires.


Dans l'application angulaire, créez un service InteractionService qui vérifiera la disponibilité du point de terminaison local d'abord via HTTP, puis par HTTPS. La vérification est effectuée par la méthode checkAvailable et le résultat de la vérification est disponible lors de l'abonnement à la variable $ disponible de type BehaviorSubject avec la valeur initiale false.

Nous ajoutons le travail d'ajout de nombres au composant AppComponent. Lorsque vous cliquez sur le bouton «Calculer», l'application Web fait une demande à GET / Calc / Add? Num1 = {num1} & num2 = {num2}. La réponse ou l'erreur s'affiche dans le champ Résultat.


Lors du dĂ©bogage, mĂȘme via HTTPS, vous ne remarquerez peut-ĂȘtre pas de problĂšmes, car le domaine des demandes sera le mĂȘme - localhost. Par consĂ©quent, vous devez tester l'application avec un nom de domaine diffĂ©rent.
Pour simplifier au maximum le travail de déploiement d'une application web, nous utilisons le service https://stackblitz.com , c'est un IDE web pour angulaire et pas seulement avec un avant-goût de VSCode. L'application terminée est disponible sur le lien .


Et vous pouvez creuser le code ici .


L'application ne fonctionnera pas de maniÚre interactive sur stackblitz, vous devez l'ouvrir dans un onglet privé séparé ou dans un autre navigateur à https://angular-pfwfrm.stackblitz.io .


Comment essayer?


L'application Web est facilement lancée à l'aide de stackblitz, simplement en cliquant sur le lien https://angular-pfwfrm.stackblitz.io .


Vous pouvez exécuter l'application Web localement.


Pour cela, vous avez besoin

Pour ce faire, vous devez cloner le référentiel:


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

dans le dossier AngularWebApp, vous devez exécuter les commandes:


 npm install ng serve --ssl true 

L'application Web sera disponible sur https: // localhost: 4200 /


L'application locale peut ĂȘtre compilĂ©e Ă  partir de l'exemple (ouvrez CsClientApp.sln Ă  partir du dossier CsClientApp) Ă  l'aide de Visual Studio et l'exĂ©cuter, ou utiliser le script du programme LINQPad .


Si vous ĂȘtes un dĂ©veloppeur .NET et n'utilisez pas LINQPad , assurez-vous de lire Ă  ce sujet, une chose indispensable dans le dĂ©veloppement. Pour exĂ©cuter l'exemple, vous devez ouvrir le script dans LINQPad'e (la premiĂšre fois que vous devez exĂ©cuter LINQPad avec des droits d'administrateur pour que les certificats soient installĂ©s) et installer les packages de nouget BouncyCastle, Nancy, Nancy.Hosting.Self, puis exĂ©cuter le script. AprĂšs cela, vous pouvez cliquer sur le bouton "Calculer" dans l'application Web et obtenir le rĂ©sultat de l'opĂ©ration.


La sécurité


Il est important de former correctement les en-tĂȘtes CORS dans une application rĂ©elle afin que les mĂ©chants d'autres sites ne puissent pas accĂ©der Ă  notre programme. Si le mĂ©chant a la possibilitĂ© de travailler avec les privilĂšges de l'utilisateur sur son ordinateur et de contourner la vĂ©rification CORS, alors il pourra faire tout ce que notre programme peut faire.


Dans tous les cas, le programme devrait fonctionner avec des droits minimaux, et s'il fait quelque chose de sensible avec les documents, vous devez lui ajouter des demandes de confirmation des opérations.


Conclusion


La tĂąche apparemment simple s'est avĂ©rĂ©e assez volumineuse, et nĂ©cessitant mĂȘme des bĂ©quilles supplĂ©mentaires pour travailler avec des certificats.


Cette approche a bien fonctionné dans une application réelle. Bien sûr, pour utiliser le code de l'exemple, vous devez ajouter une gestion normale des erreurs.


Il est pratique de demander une élévation de privilÚges lors de l'installation du programme, en utilisant InnoSetup, il est facile de le faire et de passer l'attribut nécessaire lors de la premiÚre exécution du programme. De plus, avant l'installation, il est pratique de vérifier la présence de .NET 4 et de l'installer s'il n'est pas installé.


Personne chez Virustotal ne réagit à ce programme, mais j'aimerais bien! Mais si vous assemblez le package d'installation dans InnoSetup, quelques antivirus de troisiÚme ordre commencent à fonctionner. Cela permet de se débarrasser de la signature du programme d'installation avec le certificat de signature de code.


La mise à jour automatique du programme ici est en coulisses, mais elle ne sera certainement pas superflue dans une application réelle. Squirrel est bien adapté pour gérer les mises à jour automatiques. Il est également pratique d'utiliser l'écureuil pour supprimer nos certificats du systÚme lorsque vous supprimez le programme.


Exemple de code publié sur GitHub .


Merci de votre attention!

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


All Articles