Es ist gut, wenn jemand mit mehr Erfahrung im Team zeigt, was und wie zu tun ist, auf welchen Rechen und auf welchen Winkel sie warten und wo die besten Zeichnungen von Fahrrädern für 2007 auf DVD heruntergeladen werden können. In dieser Geschichte geht es darum, wie der Wunsch als gültig ausgesprochen wurde, was das Ergebnis war und wie die Krise überwunden wurde.
Dies geschah zu einer Zeit, als ich nach mittelmäßiger Erfahrung in der Entwicklung nach einem Ort suchte, an dem man sich von einem Nicht-Junior zu einem selbstbewussten Junior entwickeln (oder mutieren) kann. Auf mysteriöse Weise des Herrn wurde ein solcher Ort gefunden, ein Projekt an den Ort angehängt und der Programmierer der "alten Schule", der über seine Karriere in Systemen mehr als nur Mädchen schrieb. „Großartig! Das Projekt, und deshalb gibt es Geld für das RFP, der Mentor ist angeschlossen, wir leben! “ Ich dachte, aber dann, wie in der Beschreibung eines typischen Horrors, sahen sich die Helden in dunkler Dunkelheit einem schrecklichen Horror ausgesetzt ...
Das Wichtigste zuerst:
1. Größe ist wichtig
Wir haben mit der Entwicklung einer einst proprietären PHP-Engine begonnen, die zum Speichern von Daten verwendet wird (hier könnte man meinen, MySQL \ PostgreSQL \ SQLite \ MongoDB \ Etwas anderes, aber notwendigerweise mit dem Suffix DB-sonst) Jungs - nicht verstehen, aber sie haben nicht geraten) api-Gateway.
„Haha, schließen Sie mit PHP ein anderes API-Gateway an und speichern Sie Daten darauf? Ist es nicht einfacher, mit API direkt aus js-code heraus zu arbeiten? Oder DBMS + PHP verwenden? " - Fragen Sie den erfahrenen Leser. Und er wird recht haben. Aber zu dieser Zeit dachte ich, der die Spezies noch nicht gesehen hatte, nicht, na ja, wer weiß, coole Leute machen das wahrscheinlich, und die Programmierer der „alten Schule“ wissen es besser.
Wie mir weiter erklärt wurde:
- Gateway = Sicherheit, niemand wird einfach so ein- und aussteigen
- Gateway = sicherer Datenspeicher, Sie können einfach nicht darauf zugreifen, + Backups
- Gateway = Geschwindigkeit, arbeitet schnell und fehlerfrei, erprobt
- Die maßgebliche Sichtweise von "Old-School" -Programmierern ist, dass Ihr PHP voller Lücken ist, jede Webanwendung standardmäßig gehackt wird, so dass nichts zum Speichern von Daten daneben vorhanden ist
Ein charakteristisches Merkmal des API-Gateways war, dass JSON-Daten in einer Get-Anfrage übertragen wurden. Ja, ja, diese sehr schönen JSON-Objekte wurden einer URL-Codierung unterzogen und in die Abfragezeichenfolge eingefügt. Und alles wäre in Ordnung, wenn eines Tages plötzlich ... die Länge der Get-Anfrage nicht mehr ausreicht. Dummerweise passte der URL-codierte JSON Kanalya nicht hinein! Der Programmierer der „alten Schule“, der sich am Kopf kratzte, fragte:
„Was machen wir jetzt? Unser json ist gewachsen, aber wir haben es nicht bemerkt ... "
"Nun, vielleicht posten wir sie dann in der Post?" Ich schlug vor, also scheint es korrekter zu sein.
"Ok, pass zum Posten."
Es war atas Nummer eins.
2. Zeit- und Sicherungsverwaltung
Um neue Funktionen in das Projekt einzubinden, musste es implementiert werden
entsprechende CRUD-Anfragen auf dem Gateway, genau das hat unser Kamerad der „alten Schule“ tatsächlich getan. Das Problem war, dass er dies einmal alle 3 Tage tat und „Fertig, check“ ausgab. Überprüfungen haben manchmal gezeigt, dass nicht alles funktioniert hat. Zum Beispiel ist es in Ordnung, die Liste abzurufen, und das Hinzufügen eines neuen Elements ist nicht in Ordnung. Das Reparieren und Verfeinern dauerte einige Zeit. Danach war es möglich, die Funktionalität im Massenzugriff freizugeben. Der Vorschlag, die Implementierung von Abfragen auf dem Gateway selbst durchzuführen, weil es zumindest schneller ist, wurde abgelehnt, weil "es dort schwierig ist, Sie werden es nicht herausfinden". Das Ergebnis dieses Ansatzes war der Abschluss der Arbeit „an mir selbst“. Wenn es zum Beispiel notwendig war, etwas in der Datenbank massenhaft zu reparieren, entschied ich mich für die zweite Option, indem ich zwischen 3-tägigem Warten und der Durchführung von Korrekturen selbst durch Abfragen wählte. Die Kunden warteten nicht besonders gern, die neue Einführung flog stabil. Eine solche Einführung, nämlich das massenhafte Anbringen eines Zeichens an den Benutzern eines Zeichens, wurde mir anvertraut, um es umzusetzen. Es gab eine Stunde für alles über alles, die Behörden warteten auf einen schönen Bericht. Hier erwartet uns atas Nummer zwei.
Tatsache ist, dass das Format der in Anfragen übertragenen JSON-Daten nur wenige erforderliche Felder implizierte, alle anderen waren willkürlich, eine klare und endgültige Struktur existierte nicht. Um beispielsweise einen Benutzer hinzuzufügen, habe ich einen JSON des Formulars übergeben:
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" }
Der optionale Teil, der in den Add / Update-Anforderungen übertragen wurde, wurde gespeichert und vollständig angegeben (ich werde weiter unten erläutern, wie dies implementiert wurde). Das Fazit ist, die Zeit steht nicht still, es wäre notwendig, das Problem zu lösen - um Benutzer zu aktualisieren, ihre Etiketten aufzuschreiben. Aber nicht jedes Mal die ganze Struktur fahren? Muss überprüfen! Ich habe es selbst getestet - ich habe nur ein Feld in der Aktualisierungsanforderung übertragen, überprüft, das Feld wurde angezeigt, der Rest der Daten ist vorhanden. Der Punkt ist klein - Schleife und aktualisieren Sie den Rest.
Das Skript paffte leise, empfing und sendete Daten, und alles schien gut zu laufen ... als plötzlich - ein Anruf. "Wir sehen den Namen der Benutzer im System nicht!" - Von diesem Ende des Kabels berichten. „Komm schon! Nun, es hat geklappt! “ - Eine unangenehme Kälte lief mir über den Rücken. Weitere Untersuchungen ergaben, dass der Name tatsächlich im Namen angegeben war, obwohl alle anderen Daten vorhanden waren. Was tun in einer solchen Situation? Backup bereitstellen!
"Genosse" Old-School "Programmierer, mit Problemen hir! Benötigen Sie ein Backup! Wann ist die letzte relevante fertig? “ - Ich frage.
"Ähh ... ich werde jetzt sehen ... Nein, es gibt kein Bakapa. "
Die Situation wurde durch die Tatsache gerettet, dass ich ein paar Stunden zuvor das Modul mit Berichten fertiggestellt und getestet hatte, eine CSV-Box mit allen erforderlichen Daten hatte und die Bestellung innerhalb einer weiteren Stunde wiederhergestellt wurde.
Mangel an verständlicher Dokumentation, Beschreibungen von Arbeitsalgorithmen, Überprüfung der Eingabevalidierung und vor allem - Datenbanksicherungen - als Nummer zwei.
Seitdem wurden täglich Backups entfernt.
3. Tief auffällig
Wackelig, aber die Arbeit bewegte sich, Probleme wurden gelöst, einige schneller, andere langsamer, als plötzlich ... Kunden erkannten, dass das System von den Servern anderer nicht verstanden wurde, und für eine solche Haltung gegenüber PD und der Organisation von ZI-Aktivitäten in ISPD Sie werden den Kopf nicht streicheln. Es ist notwendig, den Server auf sich selbst zu übertragen.
Warum wurde das System ursprünglich nicht übertragen? Die Führung hatte eine Leidenschaft - Zentralisierung. Das Management träumte von einem System, das alles kann! Müssen Sie ein Kind an die Schule binden? Sie gehen in das System, in einem speziellen Büro, dort reichen Sie einen Antrag ein. Sie müssen zum Beispiel Pizza bestellen - Sie gehen in das System, in ein anderes spezielles Büro, beantragen Pizza. Vielleicht wollten Sie mit schönen Damen / Herren kommunizieren? Zu Ihren Diensten steht ein dritter Spezialschrank - Sie reichen dort auch einen Antrag ein und so weiter bis ins Unendliche.
Vorteile - ein Login und ein Passwort für alles, Daten werden sicher und sicher auf dem Gateway gespeichert. Es gibt sogar Backups. Und wohlgemerkt, niemand wird uns dieses System nehmen! Und selbst wenn es wegnimmt - wie geht es weiter? Trotzdem werden sie das Schutzsystem gegen "Old-School" -Programmierer nicht verstehen - dort ist alles kompliziert.
VDS mit dem System wurde entladen, Kunden zugeschrieben, sie stellten es bereit, jeder tanzt und singt, Schönheit!
Und dann bedeckte mich eine Welle der Neugier und des Misstrauens.
Wenn unsere Webanwendung voller Lücken ist, wo sind dann die Daten? Bist du wirklich auf anderen Servern geblieben? Und wenn sie beschließen, das System von außen zu schließen, wird alles zusammenbrechen?
Eine einfache Überprüfung ergab, dass sich die Daten sowie die Gateway-Prozessoren selbst auf demselben Server befanden. Und nein, sie wurden wegen der Übertragung des Servers nicht dorthin übertragen, sie waren immer da.
Jetzt verfügte ich über die sehr geheime „Old-School“ -Entwicklung, die ich erforschte. Natürlich hat cooles Reverse Engineering im Stil der Artikel des Hacker-Magazins mit ollydbg, Offsets und anderen coolen Dingen nicht funktioniert, also teile ich, was ich habe.
Die Entwicklung selbst wurde in Python implementiert, es gab nur .pyc-Dateien, die leicht wieder in lesbaren Code dekompiliert werden konnten. Ehrlich gesagt hat es viel Zeit gekostet, bis zu 25 Minuten, um herauszufinden, wie es funktioniert.
Das komplexe System, das vom Programmierer der „alten Schule“ entwickelt wurde und das nur wenige verstehen können, besteht aus:
- Das vom Apache verarbeitete Skript, das die Anfrage tatsächlich erhalten hat. Was hat dieses Skript gemacht? Es wurde eine Verbindung zu einem bestimmten lokalen Host-Port hergestellt und dort eine Anforderung mit allen Daten übergeben. Das ist alles. Die Interessen gehen weiter.
- Der Serverteil, der Anforderungen aus dem Skript verarbeitet hat. Die Logik seiner Handlungen war sehr interessant. Erstens gab es keine Datenmanipulation im Code und keine Abfragen in der Datenbank, stattdessen wurden die Datenbankfunktionen in PL \ SQL aufgerufen. Alle Logik, Prüfungen usw. wurde in der Datenbankfunktion festgelegt. 50% des Skripts waren ein Wörterbuch, das den Namen der Anforderung, die damit verbundene Funktion und die Namen der Parameter der Funktion enthielt, die den in der Zeile get-request übergebenen Daten entsprechen sollten. JSON-Daten wurden bei Bedarf als separater Parameter übergeben. Ein Merkmal der Organisation des Serverteils war die Sicherungsverbindung während der Benutzerauthentifizierung. Wenn das Login und das Passwort in der Datenbank gefunden wurden, die Sitzungs-ID generiert und die offene Verbindungsinstanz in das Wörterbuch gefaltet wurde (und nach einer Zeitüberschreitung von 10 Minuten beendet wurde, damit sie nicht beendet wurde - es gab eine spezielle Methode, um die Lebensdauer der Sitzung zu verlängern), war der Schlüssel die Sitzungs-ID, die sich direkt in der Datenbank befindet nicht gespeichert. Wie genau ist die Sitzungs-ID mit Benutzerdaten verknüpft? Gibt es schließlich eine Datenanforderung, bei der die Benutzer-ID nicht übertragen wird? Es funktioniert, was bedeutet, dass hier etwas nicht stimmt.
Das Bewusstsein wurde nur sehr schwer entwickelt und hatte es nicht eilig, die längst verlorenen Geheimnisse der Meister der Vergangenheit zu enthüllen.
Unglaublich (Gehe zu> Definition, danke PhpStorm für das Verständnis von PL \ SQL), unverständlich für den Laien, der unter wahrem Wissen über die verlorene Zivilisation der Programmierer der alten Schule leidet. Im Allgemeinen wurde beim Verbinden eine temporäre Tabelle in der Authentifizierungsdatenüberprüfungsfunktion generiert, in der die Benutzer-ID gespeichert wurde.
Dies war nur der Anfang, eine indikative Liste schwerwiegender Sicherheitslücken:
- DDoS unter Verwendung der Massenauthentifizierung (Verbindungen wurden reserviert und beruhten daher auf dem DBMS-Verbindungslimit, das es angesichts der Möglichkeit, die Sitzungslebensdauer zu verlängern, ermöglichte, den Speicher vollständig mit Verbindungen zu füllen, und die Arbeit neuer Benutzer im System wäre unmöglich);
- mangelnder Schutz gegen Brute Force (die Anzahl der fehlgeschlagenen Anmeldeversuche wird nicht erkannt, nicht gespeichert, nicht überprüft;
- mangelnde Kontrolle über Aktionen mit Entitäten (z. B. wurde die Liste der vom Benutzer angeforderten Dokumente unter Berücksichtigung der Organisation ausgestellt, an die der Benutzer angehängt ist. Wenn Sie die ID des Dokuments kennen, können Sie die Anforderung zum Aktualisieren / Löschen des Dokuments erfolgreich abschließen, und die Liste der Benutzer ist auch ohne gut Passwörter, die übrigens ohne Hashing im Klartext in der Datenbank gespeichert waren, konnten von niemandem empfangen werden.
Ein weiteres ernstes Problem ist kein formalisiertes Datenspeicherungsschema. Wie bereits versprochen, spreche ich über das Speichern von "beliebigen Feldern" aus JSON. Nein, sie wurden nicht als Zeile in der Tabelle gespeichert. Sie wurden in Schlüssel-Wert-Paare unterteilt und in einer separaten Tabelle gespeichert. Für Benutzer gab es beispielsweise zwei Tabellen - Benutzer und Benutzerdaten (Zeichenfolgenschlüssel, Zeichenfolgenwert) -, in denen die Daten tatsächlich gespeichert wurden. Das Ergebnis dieses Ansatzes war eine Verlängerung der Zeit mit komplexen Stichproben aus der Datenbank.
Tatsächlich reichte dies aus, um die Entscheidung zu treffen und umzusetzen, das System auf eine neue API zu übertragen, die verständlich, dokumentiert und unterstützt ist.
Moral
Vielleicht ist dieses System ein „Vermächtnis“, und der Programmierer der „alten Schule“, der es geschaffen hat, ist der Hüter des Vermächtnisses.
Die Schlussfolgerungen lauten jedoch wie folgt:
- Wenn Ihnen gesagt wird, dass es schwierig ist, werden Sie es nicht verstehen, bedeutet dies, dass es einen vollständigen Atas gibt
- Wenn sie von Autorität niedergeschlagen werden, ist etwas unrein
- Vertrauen, aber überprüfen - Sicherheit ist kein Zustand, Sicherheit ist ein Prozess, der darüber hinaus kontinuierlich ist. Daher ist es besser, sicherzustellen, dass die deklarierten Eigenschaften wahr sind, als später herauszufinden, dass alle Benutzer plötzlich zu „Ivanov Ivanov Ivanitch“ werden, aber es gibt keine Rückschläge.