L'histoire d'une petite étude de code héritée

C'est bien quand il y a quelqu'un de plus expérimenté dans l'équipe qui montrera quoi et comment faire, quel râteau et quel angle ils attendent, et où télécharger les meilleurs dessins de vélos pour 2007 sur DVD. Cette histoire raconte comment le souhait a été exprimé comme valide, ce qui en est résulté et comment la crise a été surmontée.

Cela s'est produit à une époque où, ayant, me semblait-il, une expérience médiocre en développement, je cherchais un endroit où évoluer (ou muter) d'un non-junior à un junior même confiant. Dans des voies mystérieuses du Seigneur, un tel endroit a été trouvé, un projet a été attaché à l'endroit, et le programmeur de la «vieille école», qui a écrit plus que des filles au cours de sa carrière dans les systèmes. “Génial! Le projet, et donc il y a de l'argent pour la DP, le mentor est attaché, nous vivons! » J'ai pensé, mais ensuite, comme dans la description d'une horreur typique, les héros dans l'obscurité sombre ont fait face à une horreur terrible ...

Tout d'abord:

1. La taille compte


Nous avons commencé le développement sur un moteur php propriétaire, utilisé pour stocker des données (ici, vous pourriez penser MySQL \ PostgreSQL \ SQLite \ MongoDB \ Quelque chose d'autre, mais nécessairement avec le suffixe DB sinon) les gars ne comprennent pas, mais ils n'ont pas deviné) api-gateway.

«Haha, en utilisant php, y attachez-vous une autre passerelle api et y stockez-vous les données? N'est-il pas plus facile de travailler avec l'API directement à partir du code js? Ou utiliser DBMS + PHP? " - Demandez au lecteur chevronné. Et il aura raison. Mais à ce moment-là, moi qui n'avais pas encore vu l'espèce, je ne le pensais pas, eh bien, qui sait, les gars sympas le font probablement, et les programmeurs de la «vieille école» le savent mieux.

Comme je l'ai expliqué plus en détail:

  1. Passerelle = sécurité, personne n'entrera et sortira comme ça
  2. Passerelle = stockage de données sécurisé, vous ne pouvez pas y accéder, + sauvegardes
  3. Passerelle = vitesse, fonctionne rapidement et sans échecs, a fait ses preuves
  4. Le point de vue faisant autorité des programmeurs «à l'ancienne» est que votre php est plein de trous, toute application web est piratée par défaut, donc il n'y a rien pour stocker des données à côté d'elle

Une caractéristique de la passerelle api était que les données json étaient transmises dans une requête get. Oui, oui, ces très jons objets json, ont été soumis à l'encodage url, et mis dans la chaîne de requête. Et tout irait bien, quand soudain un jour ... la durée de la requête get cessait d'être suffisante. Bêtement, le json encodé en url, kanalya ne cadrait pas là-dedans! Le programmeur de la «vieille école», se grattant la tête, a demandé:
«Qu'allons-nous faire? Notre json a grandi, mais nous n'avons pas remarqué ... "
"Eh bien, euh, peut-être que nous les posterons dans le post?" J'ai suggéré, donc cela semble être plus correct.
"Ok, passe pour poster."
C'était au premier rang.

2. Gestion du temps et des sauvegardes


Pour visser de nouvelles fonctionnalités dans le projet, il a fallu implémenter
demandes CRUD correspondantes sur la passerelle, ce qui est exactement ce que notre camarade de la «vieille école» a réellement fait. Le problème était qu'il faisait cela une fois tous les 3 jours, donnant "Terminé, vérifiez." Des vérifications ont parfois montré que tout ne fonctionnait pas, par exemple, obtenir la liste est ok, ajouter un nouvel élément n'est pas ok. Il a fallu un certain temps pour corriger et affiner, après quoi il était possible de libérer la fonctionnalité en accès de masse. La proposition d’engager vous-même la mise en œuvre des requêtes sur la passerelle, car elle est au moins plus rapide, a été rejetée, car "c’est difficile là-bas, vous ne comprendrez pas". Le résultat de cette approche a été la clôture du travail «sur moi-même». Si, par exemple, il était nécessaire de corriger quelque chose en masse dans la base de données, alors, en choisissant entre l'attente de 3 jours et la mise en œuvre des corrections moi-même via les requêtes - j'ai choisi la 2ème option. Les clients n'aimaient pas particulièrement attendre, la nouvelle introduction a volé de manière stable. L'une de ces introductions, à savoir l'apposition en masse d'un signe aux utilisateurs d'un certain signe, m'a été confiée à mettre en œuvre, il y avait une heure pour tout sur tout, les autorités attendaient un beau rapport. Ici nous attend au numéro deux.

Le fait est que le format des données json transmises dans les requêtes n'impliquait que quelques champs obligatoires, tous les autres étaient arbitraires, une structure claire et définitive n'existait pas. Par exemple, pour ajouter un utilisateur, j'ai passé un json de la forme:

POST /api/users { "email":"ivanov@mail.ru", "password":"myEmailIsVeryBig", "name_last":"", "name_first":"", "name_middle":"", "birth":"01.01.1961", //     ,    -    "living_at":"., .3 .4 .24", "phone_num":"+70000000000" } 

La partie facultative qui a été transmise dans les demandes d'ajout / mise à jour a été enregistrée et donnée dans son intégralité (je vais expliquer comment cela a été mis en œuvre ci-dessous). L'essentiel est que le temps ne s'arrête pas, il faudrait résoudre le problème - mettre à jour les utilisateurs, déposer leurs étiquettes. Mais ne conduisez pas la structure entière à chaque fois? Doit vérifier! Je l'ai testé sur moi-même - je n'ai transmis qu'un seul champ dans la demande de mise à jour, vérifié, le champ est apparu, le reste des données est en place. Le point est petit - boucle et met à jour le reste.

Le script souffla doucement, recevant et transmettant des données, et tout semblait aller bien ... quand soudainement - un appel. "Nous ne voyons pas le nom des utilisateurs dans le système!" - rapport de cette extrémité du fil. «Allez! Et bien, ça a marché! » - Un frisson désagréable a traversé mon dos. Une enquête plus approfondie a montré qu'en effet, le nom "" était indiqué dans le nom, bien que toutes les autres données soient en place. Que faire dans une telle situation? Déployez la sauvegarde!

"Camarade" old-school "programmeur, wi hev er problèmes hir! Besoin de sauvegarde! Quand le dernier rapport pertinent est-il fait? " - je demande.

"Euh ... je vais voir maintenant ... Non, il n'y a pas de bakapa. "

La situation a été sauvée par le fait que quelques heures plus tôt, j'ai finalisé et testé le module avec des rapports, j'avais une boîte csv avec toutes les données nécessaires, la commande a été restaurée dans une heure.

Le manque de documentation intelligible, une description des algorithmes de fonctionnement, des contrôles de validité des entrées, et surtout - des sauvegardes de bases de données - au numéro deux.

Depuis lors, les sauvegardes ont commencé à être supprimées chaque jour.

3. Frappe profonde


Fragile, mais le travail bougeait, les problèmes étaient résolus, certains plus rapides, d'autres plus lents, quand tout d'un coup ... les clients ont réalisé que le système n'était pas compris par les serveurs de quelqu'un d'autre, et pour une telle attitude envers la PD et l'organisation des activités ZI dans ISPD ils ne caresseront pas la tête. Il est nécessaire de vous transférer le serveur.

Pourquoi le système n'a-t-il pas été transféré à l'origine? La direction avait une passion: la centralisation. La direction rêvait d'un système qui ferait tout! Avez-vous besoin d'attacher un enfant à l'école? Vous entrez dans le système, dans un bureau spécial, vous soumettez une demande. Vous devez, par exemple, commander une pizza - vous entrez dans le système, dans un autre bureau spécial, faites une demande de pizza. Peut-être que vous vouliez communiquer avec de belles dames / messieurs? À votre service, un troisième cabinet spécial - vous y soumettez également une candidature, et ainsi de suite à l'infini.

Avantages - un identifiant et un mot de passe pour tout, les données sont stockées en toute sécurité sur la passerelle. Il y a même des sauvegardes. Et, attention, personne ne nous prendra ce système! Et même si cela vous enlève - quelle est la prochaine étape? Ils ne comprennent tout de même pas le système de protection contre les programmeurs «old-school» - tout y est compliqué.

VDS avec le système a été déchargé, attribué aux clients, ils l'ont déployé, tout le monde danse et chante, la beauté!

Et puis une vague de curiosité et de suspicion me couvrit.

Si notre application Web est pleine de trous, alors où sont les données? Êtes-vous vraiment resté sur d'autres serveurs? Et s'ils décident de fermer le système de l'extérieur, alors tout s'effondrera?

Une simple vérification a montré que les données, ainsi que les processeurs de passerelle eux-mêmes, se trouvaient sur le même serveur. Et non, ils n'y ont pas été transférés à cause du transfert du serveur, ils étaient toujours là.
Maintenant, j'avais à ma disposition le développement très secret de la «vieille école», que je me suis mis à rechercher. Bien sûr, la rétro-ingénierie cool dans le style des articles du magazine Hacker, avec ollydbg, décalages et autres choses sympas n'a pas fonctionné, donc je partage ce que j'ai.
Le développement lui-même a été implémenté en python, il n'y avait que des fichiers .pyc qui pouvaient facilement être décompilés en code lisible. Franchement, il a fallu beaucoup de temps, jusqu'à 25 minutes, pour comprendre comment cela fonctionne.

Ainsi, le système complexe développé par le programmeur «old school», que peu de gens comprennent, consiste en:

  1. Le script traité par Apache, qui a effectivement reçu la demande. Qu'est-ce que ce script a fait? Ouvert une connexion à un port local spécifique et passé une demande avec toutes ses données. C’est tout. Les intérêts vont plus loin.
  2. La partie serveur qui a traité les demandes du script. La logique de ses actions était assez intéressante. Premièrement, il n'y a eu aucune manipulation de données dans le code et aucune requête dans la base de données; à la place, les fonctions de base de données dans PL \ SQL ont été appelées. Toute logique, vérification, etc., tout était prévu dans la fonction de base de données. 50% du script était un dictionnaire contenant le nom de la requête, la fonction qui lui était associée et les noms des paramètres de la fonction qui devaient correspondre aux données passées dans la ligne get-request. Si nécessaire, les données JSON ont été transmises en tant que paramètre distinct. Une caractéristique de l'organisation de la partie serveur était la connexion de sauvegarde lors de l'authentification des utilisateurs. Si le nom d'utilisateur et le mot de passe ont été trouvés dans la base de données, l'ID de session a été généré et l'instance de connexion ouverte a été repliée dans le dictionnaire (et a été supprimée après un délai de 10 minutes afin qu'il ne soit pas tué - il existait une méthode spéciale pour prolonger la durée de vie de la session), la clé était l'ID de session, qui se trouve directement dans la base de données non stocké. Comment l'ID de session est-il exactement associé aux données utilisateur? Après tout, existe-t-il une demande de données dans laquelle l'ID utilisateur n'est pas transmis? Cela fonctionne, ce qui signifie que quelque chose ne va pas ici.

Un développement très difficile a été donné à la conscience avec difficulté et n'était pas pressé de révéler les secrets perdus depuis longtemps des maîtres du passé.

Incroyable (Aller à> Définition, merci PhpStorm pour la compréhension de PL \ SQL), incompréhensible pour l'esprit du profane souffrant de la véritable connaissance de la civilisation perdue des programmeurs de la vieille école. En général, une fois connecté, une table temporaire a été générée dans la fonction de vérification des données d'authentification, dans laquelle l'ID utilisateur a été stocké.

Ce n'était que le début, une liste indicative de vulnérabilités graves a été trouvée:

  • DDoS utilisant l'authentification de masse (les connexions étaient réservées et, par conséquent, reposaient sur la limite de connexion au SGBD, ce qui, compte tenu de la possibilité d'allonger la durée de vie de la session, permettait de remplir complètement la mémoire de connexions, et le travail de nouveaux utilisateurs dans le système serait impossible);
  • manque de protection contre la force brute (le nombre de tentatives de connexion infructueuses n'est pas détecté, n'est pas stocké, n'est pas vérifié;
  • manque de contrôle sur les actions avec les entités (par exemple, la liste des documents demandés par l'utilisateur a été émise en tenant compte de l'organisation à laquelle l'utilisateur est attaché, et si vous connaissez l'ID du document, vous pouvez exécuter avec succès la demande de mise à jour / suppression du document, et la liste des utilisateurs est bonne même sans les mots de passe, qui, soit dit en passant, étaient stockés dans la base de données en clair, sans hachage, pouvaient être reçus par n'importe qui).

Et un autre problème grave n'est pas un système de stockage de données formalisé. Comme promis plus tôt, je parle de stocker "tous les champs" de JSON. Non, ils n'étaient pas stockés sous forme de ligne dans le tableau. Ils ont été divisés en paires clé-valeur et stockés dans une table distincte. Par exemple, pour les utilisateurs, il y avait 2 tables - les utilisateurs et les données_utilisateurs (clé de chaîne, valeur de chaîne) - où les données étaient réellement stockées. Le résultat de cette approche a été une augmentation du temps avec des échantillons complexes de la base de données.

En fait, cela suffisait pour prendre et mettre en œuvre la décision de transférer le système vers une nouvelle API, compréhensible, documentée et prise en charge.

Moral


Ce système est peut-être un «héritage», et le programmeur «à l'ancienne» qui l'a créé est le gardien de l'héritage.

Néanmoins, les conclusions sont les suivantes:

  1. Si on vous dit "là c'est difficile, vous ne comprendrez pas" - cela signifie qu'il y a un atas complet
  2. S'ils sont écrasés par l'autorité, alors quelque chose est impur
  3. Faites confiance, mais vérifiez - la sécurité n'est pas un état, la sécurité est un processus, d'ailleurs continu, il est donc préférable de s'assurer que les qualités déclarées sont vraies que de découvrir plus tard que tous les utilisateurs deviennent soudainement «Ivanov Ivanov Ivanitch», mais il n'y a pas de bacaps.

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


All Articles