Programming Database Applications

  • Naja, wir haben jetzt einiges an zeit damit verbracht zu verstehen wie "tolle" datenbanken funktionieren
  • aber mit diesem wissen können wir irgendwie nicht viel machen ausser daten speichern, was irgendwie nen bissl langweillig ist
  • Deswegen schauen wir uns jetzt mal das Programmieren mit DBsses an

Erstmal ein Beispiel

  • Wir wollen ein tolles neues Stripe entwickeln
  • Das ist ein sehhhhr naives beispiel
  • Wirspeichern user mit diesen Feldern
    • uuid
    • username
    • balance
  • Ebenso speichern wir jeden Transfer
    • uuid
    • sending user
    • receiving user
    • transferred amount
  • Das speichern wir (wie man sich warscheinlich schon denken kann) in einer massiv coolen datenbank
  • 06

Wie würden wir sowas umsetzen

  • naja bisher haben wir uns immer alles mit der CLI angeschaut, also könnten wir das hier auch machen
  • allerdings müssten wir hier DREI SQL queries ausführen
  • Das ist halt für unsere nutzer ziemlich doof, weil naja, wir können die ja nicht einfach an unsere database lassen
  • Wir haben generell drei grundlegende fragestellungen
      1. Wie stellen wir sicher, dass jeder nutzer wirklich die drei queries ausführt
      • wenn nicht alle queries ausgeführt werden könnten wir einfach unendlich geld erstellen
      1. Wie gehen wir sicher, dass nur der nutzer selber auf seine Daten zugreifen kann?
      • Jeder nutzer sollte nur auf sein Konto zugreifen können, und eben nicht auf das von anderen leuten
      • Dementsprechend müssen wir dann authentifizieren, aber wie machen wir das?
      1. Die UX muss gut sein, denn user müssen alles irgendwie "einfach" ausführen können
      • Denn wenn alles nicht gut und sicher ausgeführt wird, funktioniert unsere app nicht
      • und wenn unsere app nicht die schönste Prinzessin im ganzen land ist, dann wird sie nicht erfolgreich

Lösungsansätze

Two-Tier Architecture with Stored Procedures

  1. Wir implementieren die Logik mithilfe von stored procedures
    • Diese werden in der Datenbank hinterlegt und sorgen dafür, dass immer alle drei sachen ausgeführt werden, auch wenn wir nur einen Aufruf haben
  2. Wir limitieren den Beispiel zum DBMs mit dem integrierten DBMs access management
    • Das können wir machen, indem wir jedem user seinen eigenen User erstellen, womit er nur auf SEINE daten zugreifen kann, und auf daten die mit ihm geteilt sind
  3. Wir entwickeln einen Client der die Datenbank wegabstrahiert und den Useren erlaubt "einfach" die App zu nutzen, ohne das sie fähigkeiten oder wissen zu den DBMS oder auch SQL haben müssen
    • Wir entwickeln einen DB driver (oder naja, nutzen einen bestehenden) der dann das CLI ersetzt.
    • Dann können wir auch "einfach" eine App oder ein Webinterface oder so schreiben, was dann alles implementiert etc.
  • Das hört sich doch erstmal alles super toll an, oder?
    • Wir haben wenige bewegliche teile
      • wir brauchen nur das DBMs und seine clienten aufrecht zu erhalten
      • der Client ist relativ leicht
    • Wir haben Sicherheit indem wir die Privilegien gut rausseperieren können
      • Damit ein Angreifer etwas "sinnvolles" erreichen kann, muss er das DBMS selber angreifen
      • Das ist sichergemacht dadurch, dass wir unseren Nutzern generell nicht vertrauen
    • Und wir sind gegen datenverlust gesichert
      • Denn wenn eitrwa fehlt, werden unsere daten wieder schön zurückgerollt
      • weil das eben so durch das DBMs überwacht wird
  • Ganz so rosig ist das dann aber doch nicht
    • Für entwickler ist das ein bissl sehr nervig
      • Stored Procedured sind halt schon nen bissl massiv nervig
      • Testen wird sehr schwer, weil die stored Procedures halt nicht teil des programms selber sind, sondern irgendwie anders im DBMS liegen und so schlecht getestet werden können
    • Ebenfalls wird das dadurch sehr schwer das zu debuggen
      • Denn die stored procedures nerven ein wenig und sind halt irgendwie nicht besonders "toll" und loggen auch nicht wirklich
      • Leider sind die errors die wir hierfür bekommen dementsprechend besonders kryptisch und machen generell keinen spaß zu debuggen
    • Ausserdem muss die Datenbank eben an die clients exposed werden, was inhärent ein Sicherheitsrisiko ist, was dazu führt, dass wir uns direkt wieder ein weniger unsicher machen
      • Zudem entsteht dadurch eben eine vermeindlich große angriffsfläche, die halt für uns dann wieder nen bissl schlecht sind
      • wenn eine Datenbank direkt ins Internet exposed ist, macht uns das direkt angreifbar und ist generell ne ziemliche bad practise

Two-Tier Architecture with Fat Clients

  1. Wir implementieren unsere Business Logic innerhalb des clients in der Sprache des clients
    • Hier können wir dann unsere lieblingssprache nehmen und dann dementsprechend ggf. auch schöner einbinden
    • Die clienten können ausserdem schneller und genauer auf daten zugreifen, weil sie durch die direkte implementation einfacher zum bearbeiten sind
  2. jetzt können wir deutlich sicherere permissionsverteilung machen
    • leider müssen die clients immernoch "direkten" zugriff haben, dennoch sollte das dann einfacher möglich sein
    • wir müssen allerdings weiterhin "einfach vertrauen" denn irgendwie kommen wir da nicht herum
    • Wenn leute Zugriff auf den client haben, können sie ausserdem das passwort o.ä. "einfach" direkt aus dem client auslesen, und dann halt dementsprechend auch einfach blöde dinge tun
    • wenn unsere Datenbank nicht richtig gesichert ist, könnten sie zudem auch einfach die daten von anderen leuten anschauen oder traffic snoopen
    • Leider funktioniert das alles nur, wenn der Client wirklich vertrauenswürdig ist
  3. die GUI ist weiterhin einfach eine GUI, genau so wie vorher
  • Ist das die lösung für uns alle?
    • Es ist immernoch sehr simpel
      • wir brauchen immernoch nur das RDBMS und müssen nur clienten accounts machen
    • Jetzt ist die Development Experience deutlich schöner
      • die logik ist in "richtigen" programmiersprachen deutlich durchsichtiger und wir können dementsprechend echt schön alles filtern etc.
    • Das debuggen ist auch einfacher, denn alles ist ja wie gesagt einfach direkt integriert
  • Leider immernoch nicht das Rezept für den Weltfrieden
    • Wir müssen weiterhin unseren clients vertrauen, was wir irgendwie immernoch nicht machen wollen
    • Da die user immernoch von ihrem gerät aus zugreifen, könnten die einfach irgendwas machen, usw.
    • Daher müssen wir auch jedes mal, wenn irgendetwas an der DB geändert wird, den Client updaten, und alle bisherigen clients gehen einfach kaputt, was halt auch nen bissl blöd ist
    • Ausserdem muss jetzt alles in jedem client gespeichert werden, was irgendwie nicht so geil ist
      • Vor allem wenn wir ein cross-plattform system nutzen, ist das echt irgendwie nicht besonders toll, und wir müssen dementsprechend das jedes mal irgendwie neu machen
    • Wie gesagt muss die DB immernoch ans offene Internet exposed werden, was halt am ende dann doof ist

Three-Tier Architecture

  • Yay, wir haben noch ein weiteres tier
  1. Wir implementieren weiterhin die Business Logic in einer "tollen Programmiersprache"
    • Hier nutzen wir weiterhin die vorteile davon, alles integriert zu machen
  2. Wir autorisieren jetzt auf einem application server
    • Der Server ist von uns trusted, und kann so "mehr" machen
    • Wir steuern den über HTTP an und müssen so nicht mehr die DB exposen
    • Wir können deutlich feiner seperieren und mit deutlich mehr genauigkeit autorisieren
  3. Der Client ist jetzt "simpler" und interfaced nur mit dem Server
    • So müssen wir die Datenbank nicht mehr ans internet exposen, und alles wird für uns unendlich einfacher (yay)
    • Bei updates an der datenstruktur etc müssen wir nicht immer den Client updaten, denn jetzt kann der Server etwas als "übersetzer" fungieren
    • Der client enthält nichts mehr von der "richtigen Datenbank" und kann dementsprechend auch nicht mehr dadrauf zugreifen oder durch irgendwas dummes die Datenbank compromisen
    • Gutes beispiel sind hier websiten
  • Jetzt ist das wohl unser Erfolgsrezept?
    • Die development experience ist wie gesagt weiterhin toll
    • Das debuggen ist noch einfacher, denn wir wissen direkt in welchem teil der "Kette" das alles passiert
    • Wir müssen das DBMS nicht exposen
      • Der Server ist warscheinlich auch generell einfacher zu sichern, als eben das DBMS
    • Änderungen in der Datenbank macht uns nicht direkt den Client kaputt, denn der Server kann hier übersetzen
    • Dementsprechend müssen wir nur eine sache updaten, und nicht wieder jeden client der überall auf der welt sein kann
    • Wir können permission sehr schön ausseinanderhalten
  • Naja nichts im leben ist "einfach" nur toll#
    • Wir haben einen höheren entwicklungsaufwand, denn wir müssen jetzt auch noch einen Server entwickeln und die Clients müssen irgendwie auch mit dem Server kommunizieren
    • Wir haben mehrere sachen die kaputt gehen können, denn jetzt haben wir ja auch noch den client
    • Wenn der Server aus irgendwelchen gründen übernommen werden kann, ist unsere datenbank trotzdem hiii
      • Das ist halt wieder doof, weil wir denken, dass wir unserem server vertrauen können
      • Allerdings ist hier auch wieder die best practise wichtig, einfach generell so wenig daten zu verteilen wie möglich

Was ist jetzt die Erkenntnis?

  • Die Architektur die wir uns aussuchen hat am ende dann doch noch wirklich große auswirkungen auf die Sicherheit unserer App und ist dementsprechend auch sehr wichtig
    • Leider kann man das auch irgendwie nicht später ändern und dementsprechend ist hier sehr gute überlegung vorher wichtig
  • Die Wahl der Architektur ist aber auch oft einfach mehr abhängig von den umständen in denen wir uns gerade befinden, als das wir wirklich einfach tolle sachen machen können
  • Manchmal kann man aber auch einfach mischen, was uns dann "the best of both worlds" liefern kann
    • Es kann aber auch einfach maximal schlimm sein (oh no)
  • Die meissten DBMS unterstützen alle oben erwähnten modelle, weil sich für die halt am ende auch nicht so viel ändert

Programmierte nutzung von Datenbanken

  • wenn wir programme schreiben haben die programme auch gleichzeitig die möglichkeit unsicher zu sein
  • das ist (meisstens) nichts was wir uns aussuchen, aber dennoch kann das einfach passieren
  • So können z.b. SQL Injecitons passieren, wenn man unvorsichtig ist
    • Die dinger kann man umgehen, wenn man seinen Input Sanitized
    • Das machen wir, indem wir nach schemata suchen, und hier ist nicht einfach nur "String" eine sinnvolle abfrage
    • Generell ist es eine dumme Idee, den nutzern überhaupt zu vertrauen
  • Wir sollten also vermeiden, direkt nutzersachen in die Datenbank zu kloppen
  • Wie machen wir das? mit prepared statements.
    • Die prepared Statements können dann für uns einfach die sanitazation machen lassen
    • Denn die können das bestimmt besser als du und ich (zusammen)
  • Allerdings schreiben wir hier immernoch unsere Queries selber
    • Das kann zu fehlern führen, die oft auch schwer zu finden sind
    • Zudem müssen wir die daten aus dem RDMS "von hand" in die SQL queries mappen, und andersrum
    • Ausserdem passen oft die datentypen nicht, schaut euch teilweise auch einfach mal JS an (da passt eh nie irgendwas, also naja...)
  • Das können wir übergehen, indem wir einen Object Relational Mapper oder auch ORM nutzen.
    • Das ding, kümmert sich darum, das sachen die wir "einfach" in unserer lieblingsprogrammiersprache anwenden können auch in der SQL datenbank dann funktionieren.
    • Dadurch können oft fehler und sicherheitsücken unterbunden werden
    • dann du interagiertst nicht mehr direkt mit der datenbank, denn eben das (was auch der "gefährliche" teil ist) macht jetzt der ORM
    • Ausserdem ist das dann von der developer experience deutlich schöner, da alles einfach innerhalb deiner programmiersprache passiert

Hibernate

  • Ein (scheinbar) bekanntes ORM-System ist "Hybernate"
  • Das ding ist nen framework für Java was von einer Firma namens "JBoss" (schon kranker name) entwickelt wurde und heutezutage (wie so vieles) von den menschen mit dem Roten Hut (RedHat) maintained wird
  • Hibernate verfolgt hauptsächlich den weg des "Data Mappers", denn es generiert einfach allen SQL kran selber, und macht dementsprechend auch alle sachen die bei umzügen anfallen selber
  • Es greift auf Annotations zurück die mit den Tablestrukturen populiert werden, was dann dme ding alle "Wichtigen infos gibt"