Login Mechanisms

By Karl Brodowsky IT Sky Consulting GmbH

Deutsch

E-Banking examples

  • „Calculator“
  • RSA-Number-Generator
  • SMS
  • Sheet with 100 Codes
  • Android-App
  • USB-Device
  • SmartCard
  • Not so common for banks: plain old username+password
  • Login with google, twitter, facebook

Calculator

  • Enter PIN Code
  • Enter Code from login page
  • Read Result from calculator
  • Enter Result in Login Page

Security Questions:

  • Depends on how the calculator works
  • Due to calculator PIN/Password some security agains theft/loss of device

Practical Questions:

  • Always forgotten calculator
  • Expensive hardware required

Summary:

This method is potentially quite good, if it is done well with good algorithms and good data.

Number Generator

  • On login page enter 6-digit code from RSA device in addition to username+password

Security issues

  • Looks good because it comes from RSA
  • Does the timing always work well enough in sync (apparently yes)?
  • Device can be stolen, no additional protection
  • No Challenge-Response, not as good as the calculator

Practical issues:

  • Device is small enough to be in the pocket all the time
  • Device is quite expensive

SMS

  • Enter username and password
  • Receive code by SMS
  • Enter Code

Security issues

  • How secure is the mobile network?
  • How secure is the phone? Not-so-smart phones are better. Ideally use the old Nokia phone for this with a prepaid SIM-card
  • For m-banking two phones are needed or the security is much lower

Practical issues

  • Phone is in the pocket
  • But if an additional phone is needed just for this not very practical any more
  • Sometimes SMS get lost
  • some people play with many SIM cards (not a common problem)
  • Battery of phone can be empty (only a problem for older generation)

Summary

This seems to be a solid mechanism, but is slightly inferior to the calculator.

Sheet with 100 codes

  • Login with username and password
  • Page requests code with a given number from a given sheet.
  • Each number is used only once
  • New sheet supplied when 80% used up

Security issues

  • Depends on quality of numbers
  • Paper can be lost or stolen
  • Printing and handling of numbers leaves a lot of vulnerability which is hard to control.

Practical issues

  • Paper needs to be managed by bank and customer
  • Needs to be in the luggage or as image on the phone
  • Needs to be stored carefully

Summary

Mechanism seems to be in the middle field. Apart from being uncool the mechanism is not so bad, but it is inferior to the calculator.

Android App

Can off course technically work for your favorite smart phone, even if it is not Android… ;-)

  • Login with username and password
  • A colored code is displayed:

code

  • Run Android App which takes a photo of the code and provides a 6 digit code plus some information.
  • Enter that for login
  • Remark: the App needs to be personalized, so it is only working for the own e-banking, not for someone elses.

Security issues

  • No password within App
  • Depends on keeping phone safe
  • Positive is that it is so easy that it can be used a lot, even for verifiying single bookings to an unknown receipient
  • The code can transport information that can be displayed in the

Practical issues

  • Requires Smart phone that supports the App
  • Phone is always in the pocket

USB-Device

An USB-device is plugged into the PC. It can be “smart” and communicate directly with the server. Can also provide secure browser or even boot PC into a specially safety-hardened Linux distribution.

I have only theoretical knowledge about this, not used it.

Security issues

  • Has a lot of potential
  • A “bad PC” can be a problem, but there are ways to implement this that are quite secure, as long as encrypted data traffic is considered secure at all. If not, we should forget the internet for anything that requires any non-trivial level of security.

Practical issues

  • requires special USB-stick
  • USB is disabled or “castrated” on many PCs, so it might be hard to let the PC accept the device
  • Are there “device driver issues”?

Smart Card

This is just like the USB device, but with a chip card instead of an USB device.

Username + Password

  • For ebanking not enough (some banks don’t care)
  • For „simple“ apps it is a pain to keep usernames and passwords safe on server
  • Still „easy default“
  • How about user management?

Security issues

  • This has the lowest security of all mechanisms presented here.
  • user and password database is always a risk

Login with Google

  • Use google+, facebook, twitter etc. for login
  • Assume Google here…
  • Login into google (you normally are anyway…)
  • Click login with google
  • First time google asks if it is ok to provide the identity information to web page xyz
  • In Google settings this can be removed…

Security issues:

  • Do we want to have NSA-connected companies involved in our login process?
  • User management is something that the big guys claim to do well
  • OAuth2 is our friend for this, it is not so hard to do

Just remember this

  • Always use https when serious about security
  • http means transmitting password unencrypted and making some of our „friends“ who could intercept traffic very happy
  • Especially if it deals with money.
  • Serious https helps at least a little bit
  • Maybe, who knows what the NSA knows. :-(

As a task for me I have to get this page on https…

 

Share

Login-Verfahren

Karl Brodowsky IT Sky Consulting GmbH

English

Für die Authentisierung in Webapplikationen gibt es verschieden Ansätze.
Ich beschreibe einmal einige, die mir tatsächlich begegnet sind, und mache mir ein paar Gedanken, was man davon erwarten kann.

Nun ist die Frage, ob so etwas nicht sehr einfach ist, man muss nur irgendwo Namen und Passwörter speichern und dann beim login einen Abgleich machen. Das können die ganzen coolen Frameworks heute gut, egal ob Java, Ruby, Perl, PHP, Scala, F# oder Python.

Aber es kommen einige Fragen auf:
- Sicherheit
- Vergessene Passwörter
- Benutzerfreundlichkeit

Beispiele für E-Banking und anderes

  • „Taschenrechner“
  • RSA-Zahlen-Generator
  • SMS
  • Zettel mit 100 Codes
  • Android-App
  • USB-Device
  • SmartCard
  • Einfach Username + Password
  • Login mit Google, Twitter oder Facebook

Taschenrechner

  • Auf Login-Seite Username + Password eingeben
  • PIN Code in TR eingeben
  • Code von Login-Seite in TR eingeben
  • Ergebnis von Taschenrechner ablesen
  • In Login-Seite eingeben

Security.Fragen:

  • Abhängig davon, wie der Taschenrechner funktioniert
  • Weil der Taschenrechner einen PIN-Code braucht gewisse Sicherheit gegen Verlust + Diebstahl

Praktische Fragen:

  • Taschenrechner nie dabei
  • Wer bezahlt die teure Hardware
  • Distribution der Hardware muss organisiert werden

Zusammenfassung:

Diese Methode is potentiell recht gut, wenn sie sauber und mit guten Algorithmen und guten Daten implementiert ist.

RSA-Zahlen-Generator

  • Auf der Login-Seite wird zusätzlich ein vom Gerät abgelesener 6-stelliger Code eingegeben.
  • Alle paar Sekunden/Minuten ändert sich der angezeigte Code

Security-Fragen

  • Sieht gut aus weil es von RSA kommt
  • Sind die Uhren immer gut genug synchron? Es scheint so.
  • Kein zusätzlicher Schutz, wenn das Gerät gestohlen wird
  • Kein Challenge-Response, nicht so gut wie der Taschenrechner

Praktische Fragen:

  • Gerät ist klein genug um es immer dabei zu haben
  • Gerät ist sehr teuer

SMS

  • username und password eingeben
  • code kommt als SMS
  • Code eingeben

Security-Fragen

  • Wie sicher ist das Mobilnetz?
  • Wie sicher ist das Telefon? Altes Nokia mit Prepaid-SIM ist sicher, aber modernes Smartphone riskanter
  • Für m-banking problematisch, wenn beides auf demselben Telefon läuft

Praktische Fragen

  • Telefon ist immer dabei
  • Aber nicht, wenn man dem Smartphone nicht traut und ein zweites braucht
  • SMS gehen verloren
  • Manche Leute haben viele SIM-Karten. Ist aber nicht so verbreitete Gewohnheit, man zahlt sehr gerne große Roaming-Rechnungen
  • Batterie kann leer sein. (Nur ein Problem der älteren Generation)
  • SMS-Roaming-Gebühren spielen für diesen Anwendungsfall keine große Rolle

Zusammenfassung

Solider Mechanismus, aber etwas schlechter als Taschenrechner.

Zettel mit 100 Codes

  • Login mit Username und Password
  • Die Seite verlangt Code von einer bestimmten Seite und einer bestimmten Nummer
  • Jede Nummer wird nur einmal verwendet
  • Neuer Zettel, wenn alter ca. 80% aufgebraucht

Security-Fragen

  • Hängt von der Qualität der Zahlen ab
  • Papier kann verloren gehen oder gestohlen werden
  • Drucken und Handhaben der Zahlen bietet viele Schwachstellen im ganzen Prozess

Praktische Fragen

  • Papier muss von Kunde und Bank verwaltet und aufbewahrt werden
  • Muss immer dabei sein, eventuell als Foto auf dem Telefon
  • Muss sorgfältig aufbewahrt werden

Zusammenfassung

Mechanismus rangiert irgendwo im Mittelfeld. Abgesehen davon, dass es nicht so cool ist, ist es nicht wirklich schlecht, aber schlechter als der Taschenrechner.

Android App

Oder mit anderen Smartphones auch machbar…

  • Login mit Username und Password
  • Ein farbiger Code wird angezeigt:

code

  • Starte Android-App die den Code fotografiert und auswertet.
  • Eine 6-stellige Zahl und eventuell mehr Information wird angezeigt.
  • 6-stelligen Code auf Login-Seite eingeben
  • Bemerkung: Die App muss personalisiert werden und funktioniert nur für das eigene e-Banking

Security-Fragen

  • Kein Password innerhalb der App
  • Telefon muss sicher aufbewahrt werden und Password- oder PIN-Schutz haben
  • Positiv ist, dass es so einfach geht, dass man das Verfahren auch zur Verifizierung einzelner Buchungen mit neuem Empfänger verwenden kann.
  • Der Code kann Information transportieren, die man in der App anzeigen kann

Praktische Fragen

  • Braucht passendes Smartphone
  • Smartphone ist immer dabei

USB-Device

Ein USB-Gerät wird in den Rechner gesteckt. Dieses kann “smart” sein und direkt mit dem Server kommunizieren. Man kann sogar ein speziell sicheres Linux vom USB-Stick booten für das E-Banking.

Ich habe nur theoretisches Wissen dazu, keine Erfahrungen.

Security-Fragen

  • Hat sehr viel Potential
  • Ein manipulierter PC kann viel schaden, idealerweise aber den Traffic zwischen Device und Server nicht manipulieren.

Praktische Fragen

  • braucht speziellen USB-stick
  • USB ist in manchen Großbetrieben abgeschaltet, speziell das Booten von USB
  • Gerätetreiber sind immer ein Problem, wenn man sie braucht

Smart Card

Wie USB-Gerät, nur mit SmartCard statt USB-Stick.

Username + Password

  • Für E-Banking nicht genug, aber es soll Banken geben, denen das egal ist
  • Für einfache Applikationen ist es heikel, Username und Passörter auf dem eigenen Server zu haben
  • Immer noch der Default-Ansatz, einfach
  • Wie geht die User-Verwaltung??

Security-Fragen

  • Niedrigste Security
  • Benutzer- und Password-Datenbank ist immer ein Risiko, siehe iCloud

Login mit Google

  • Benutze google+, facebook, twitter etc. für login
  • Hier nehmen wir Google an um weniger schreiben und lesen zu müssen…
  • Bei Google einloggen. Sind wir sowieso normalerweise schon
  • Klick auf “Login mit Google”
  • Beim ersten Mal fragt Google, ob sie der Applikation die Identität geben dürfen.
  • In den Google-Settings kann das entfernt werden.

Security-Fragen:

  • Wie sehr lieben wir die NSA
  • User management kann Google besser als wir
  • OAuth2 ist unser Freund, nicht so schwierig zu verwenden

Immer dran denken

  • Wir sollten immer https verwenden
  • http bedeutet, dass Passwörter im Klartext übertragen werden, “Freunde” im Netz können sie mitlesen.
  • Wenn es um Geld geht, geht http gar nicht, nur https
  • Das hilft jedenfalls etwas.
  • Wer weiß, was die NSA über https weiß, was nicht veröffentlicht worden ist?

Ich möchte diese Seite bald auf https umstellen… ;-)

Share

Auf GPU rechnen

Heutige Rechner haben bekanntlich sehr leistungsfähige CPUs mit “Quadcore” und mehr.
Aber auch die Grafikkarten sind ziemlich toll geworden und haben zum Teil auch so große eigene Lüfter wie die CPU.
Sie haben eine Menge eigenes Memory und sie können recht komplizierte Operationen selbsttätig rechnen, natürlich nur für die grafische Darstellung.

Nun stellt sich die Frage, ob man das auch sonst zum Rechnen benutzen kann. Man hört Geschichten, dass die Grafikkarte um Größenordnungen leistungsfähiger als die CPU sei. Warum schaffen das die Grafikkartenhersteller so leicht nebenbei, besser als die CPU in deren Kernaufgabe zu sein und außerdem noch Grafik darzustellen?

Grafikkarten bieten die Möglichkeit, massiv parallel zu rechnen, aber nach dem Prinzip SIMD. Das heißt, dass ein oder sehr wenige Befehlsströme verarbeitet werden und eine große Zahl von Recheneinheiten jeweils den identischen Code mit verschiedenen Daten ausführt. Für Vektoroperationen ist das optimal. Und es erklärt auch die brachiale Leistung der Grafikchips, weil man den aufwändigen Teil, Maschinencode zu interpretieren, nur einmal (oder wenige Male) baut und nur die eigentliche Recheneinheit mehrfach. Man bekommt so mit derselben Siliziummenge mehr Rechenleistung, aber mit der Einschränkung, SIMD (oder fast SIMD) zu sein. Aber man hat diese Geräte in den Rechnern drin. Es gibt sogar “Grafikkarten”, die nur rechnen können, aber bei denen man die eigentlichen Grafikchips weggelassen hat. Und viele Rechner haben den Grafikkern mit auf dem selben Chip wie die CPU-Kerne, so dass man eine einfache Grafik zur Verfügung hat, aber man baut dann doch zusätzlich noch eine externe Grafikkarte ein, die noch besser ist. Selbst ohne die Grafikfähigkeit zu gefährden hat man also eine GPU zum Rechnen übrig.

Nun ist das Programmieren dieser Geräte nicht so einfach. Grundsätzlich muss man sich mit einem völlig neuen Programmiermodell herumschlagen. Viele Algorithmen lassen sich speziell für SIMD entwickeln, aber sie sind anders als was wir gewohnt sind und automatisch lässt sich da nur beschränkt etwas optimieren. In der Regel muss man also mit C das Programm schreiben und kann dabei auf OpenCL bauen. Das ist ein nützlicher Standard, der einiges erleichtert, aber es bleibt schwierig. Für manche Zwecke lohnt es sich aber, den Entwicklungsaufwand zu treiben.

Vor ein paar Tagen habe ich drei Vorträge über Ansätze gehört, F# zu verwenden, um die GPU zu programmieren. Man kann also unter Einhaltung gewisser Regeln F#-Programme schreiben, die sich etwa in OpenCL übersetzen lassen und dann compiliert die GPU nutzen:

  • FSCL: Homogeneous Programming and Execution on Heterogeneous Platforms (by Gabriele Cocco, Pisa University)
  • Professional GPU Development with F# and the Alea Compiler Suite (by Daniel Egloff, QuantAlea)
  • New Abstractions for Radically Simplified GPU Programming in F# and .NET (by Luc Bläser, HSR, based on joint work with QuantAlea)

All diese Ansätze sollten schon heute oder zumindest in zukünftigen Implementierungen auch für Mono laufen und damit für Linux zur Verfügung stehen.

Es würde wohl den Rahmen dieses Artikels sprengen, im Einzelnen darauf einzugehen, aber vielleicht kann ich noch Links bekommen, die auf weiterführendes Material zu den einzelnen Lösungsansätzen führen.

Share

Alle zwei Wochen

Ich werde den Rhythmus dieses Blog auf alle 14 Tage umstellen.

Share

Einheiten mitführen

Typische Programme rechnen mit Größen, also mit Zahlwerten, die mit irgendeiner Einheit kombiniert sind. Das können Temperaturen, Längen, Zeitdauern, Geldbeträge und vieles mehr sein. Weil früher jedes Byte teuer war, hat sich etabliert, dass man nur mit den Zahlwerten arbeitet und die Einheiten implizit durch die Programmlogik ins Spiel kommen. Das funktioniert in einfache Fällen, wo die Einheit wirklich klar ist. Wenn aber mehrere Einheiten im Spiel sind, ist es besser, diese auch mitzuführe, außer bei dieser massiv strapazierten inneren Schleife, die man in Wirklichkeit im ganzen Programm doch nicht findet.

Man kann das durch Typisierung erreichen, also etwa Typen wie “KelvinTemperatur” definieren oder man kann einen Typ “Temperatur” definieren, der den Betrag und die Einheit enthält. So können bestimmte Fehler zur Laufzeit oder sogar zur Compilezeit entdeckt werden oder sogar passende Umrechnungen verwendet werden, wenn man eine Fahrenheit und eine Kelvintemperatur addiert.

Java bekommt hier einen Minuspunkt, weil man dort nicht vernünftig die elementaren Operationen für solche Typen definieren kann.

Die meisten anderen modernen Programmiersprachen können das natürlich.

Share

Verbindung verschiedener Verabeitungsschritte

Häufig beinhaltet eine Software verschiedene Verarbeitungsschritte, die von den Daten nacheinander durchlaufen werden.

Im einfachsten Fall kann man mit Verabeitungsschritten f, g und h man so etwas machen wie (Pseudocode):


for x in input_data {
y = h(g(f(x));
  store_partial_result(y);
}

Das stimmt, wenn die Datenmengen jeweils übereinstimmen. Häufig ist aber in der Praxis der Fall, dass die Datengrößen nicht so übereinstimmen. Man muss also Daten zwischenspeichern, bis für den jeweiligen Verarbeitungsschritt genug Eingaben da sind. Im Grunde genommen ist ein sehr einfacher und robuster Mechanismuns seit Jahrhunderten bewährt, zumindest wenn man in Jahrhunderten von zehn Jahren Dauer rechnet.. ;-)


cat input_data_file| process_f | process_g | process_h > output_data_file

Der Ansatz lässt sich in einem Programm direkt nachbauen. Man muss named oder anonymous Pipes, TCP/IP, Message-Queues oder Messaging (wie JMS, RabbitMQ, MQ-Series,…) verwenden, um die Programmteile miteinander zu verbinden. Wenn man mit Akka arbeitet, kann man für jeden Verarbeitungsschritt einen (oder mehrere parallele) Aktor verwenden und diese mit Messages verknüpfen. Die Aktoren müssen und Daten sammeln, bis sie genug haben, um sie sinnvoll zu verarbeiten. Etwas unschön ist, dass man hier plötzlich “state” im Aktor braucht.

Mehrere Threads sind auch möglich, wenn man für die Daten jeweils den augenblicklichen “Owner” kennt und nur diesem Zugriff darauf erlaubt. Die zu implementierende Logik passt eigentlich sehr gut dazu, da definiert sein sollte, welchem Verarbeitungsschritt die Daten gerade unterzogen werden.

Es lässt sich aber auch mit einem Thread etwas machen, etwa entlang dieser Linie:

while (true) {
  if (enough_for_h(g_out)) {
    h_in = get_block(g_out, required_size_for_h)
    h_out = h(h_in)
    store_result(h_out)
  } else if (enough_for_g(f_out)) {
    g_in = get_block(f_out, required_size_for_g)
    append(g_out, g(g_in));
  } else if (enough_for_f(new_data)) {
    f_in = get_block(new_data, required_size_for_f)
    append(f_out, f(f_in));
  } else {
    append(new_data, read_block(input_data));
  }
}

Man muss immer aufpassen, dass sich das System nicht totläuft, was bei schlechten Implementierungen leicht passieren kann, weil entweder die Eingabedaten ausgehen oder die Buffer überlaufen. Die obige Implementierung strebt an, die Buffer immer auf der minimalen Füllung zu belassen.

Oft hat man natürlich die Möglichkeit und auch die Notwendigkeit, alle Daten auf einmal ins Memory zu laden. Dann gehen viele Dinge einfacher, man denke nur an Sortierfunktionen, die ja alle Daten sehen müssen.

Für große Datenmengen ist das aber nicht möglich und da sind die richtigen Ansätze nützlich, um nur eine verträgliche Datenmenge im Speicher zu halten.

Ein recht radikaler Ansatz ist es, alle Zwischenergebnisse in Dateien oder Datenbanken zu speichern. Dann kann man die Schritte sogar nacheinander ausführen.

In jedem Fall sollte man sich über die richtige Architektur Gedanken machen, bevor man munter drauf los programmiert.

In der Regel fährt man am besten, wenn die Quelldaten den Prozess treiben, also wenn diese sequentiell gelesen werden und zwar jeweils in den Mengen, die auch verabeitet werden. Das ist das Prinzip des “Backpressure”.

Share

Non-Blocking I/O

In Posix-Systemen (Linux, Unix, MacOS X,…) basieren die I/O-Operationen hauptsächlich auf den Systemaufrufen read(..) und write(..). Die meisten anderen I/O-Operationen lassen sich darauf zurückführen und auch I/O von anderen Programmiersprachen als C dürfte letztlich indirekt zu read() und write() führen. read() ist eine Funktion, die einen (numerischen) Filedeskriptor, einen Pointer auf einen hinreichend großen Speicherbereich (“Buffer”) und eine Größenangabe annimmt und im Idealfall die angegebene Anzahl von Bytes liest. Das funktioniert gemäß dem Unix-Prinzip “everything is a file” ganz einfach mit Dateien (Files), Geräten (Devices), (Pseudo-)terminals, Pipes (Fifos), TCP-Streams etc. Wenn ein Fehler auftritt, wird ein negativer Wert zurückgegeben und man muss errno abfragen, um den Fehler zu finden. Den negativen Fehlercode direkt zurückzugeben wäre besser gewesen, aber das kann man nun nicht mehr ändern. Eine 0 bedeutet, dass man das Ende erreicht hat. Dies wird nicht durch den Inhalt der übertragenen Daten ausgedrückt, denn jede beliebige Bytesquenz wird akzeptiert und verarbeitet, sondern separat übermittel. Wenn weniger als die gewünschten Bytes da sind, dann wird entsprechend weniger zurückgegeben. Der Rückgabewert gibt an, wieviele Bytes tatsächlich gelesen wurdn. Das sind alles Situationen, in denen das read sofort anfangen kann, auch wenn die eigentliche Operation etwas länger dauern kann. Bei Dateien kann der Fall, dass 0 Bytes gelesen werden, nur am Dateiende und natürlich bei Fehlern auftreten. Bei Pipes und TCP-Streams ist aber möglich, dass man auf Daten wartet und keine da sind. Mit 0 Bytes gibt sich das read normalerweise nicht zufrieden und wwartet stattdessen auf Daten. Das ist eine vernünftige Sache, da es in der Regel das ist, was man will. So vereinfacht sich die Programmierung. Write verhält sich entsprechend, bei Pipes kann man auch nur schreiben, wenn auf der Gegenseite die Daten abgenommen werden. Ein Prinzip das heute in der Scala- und Akka-Welt als die größte Innovation des Jahres gerühmt wird und den Namen “Backpressure” bekommen hat.

Non-Blocking funktioniert auch mit read(). Mit open() oder nachträglich mit fcntl() wird eingestellt, dass das I/O non-blocking ist. read() versucht nun, Bytes zu lesen und wenn das klappt ist alles wie beim blocking I/O. Wenn das read() keine Daten findet, wartet es nicht auf Daten, sondern kommt sofort mit -1 zurück und in errno steht EAGAIN oder EWOULDBLOCK o.ä. write() entsprechend.

Nun hätte man die Möglichkeit, regelmäßig einen Filedescriptor abzufragen und wenn Daten kommen, darauf zu reagieren und sonst etwas anderes zu tun. Ohne Multithreading…

Interessant ist der Fall, dass man mehrere Filedescriptoren gleichzeitig hat und von diesen Daten lesen oder schreiben will. Man weiß nicht wann welche Zugriffe möglich sind. Das wäre mit einem Thread pro Filedescriptor machbar. Oder mit non-Blocking-I/O und Polling, also einer Schleife, die dauernd alle Filedescriptoren durchprobiert.. Besser ist es aber select() oder pselect() oder poll() zu verwenden, die semantisch sehr ähnlich funktionieren, aber aus historischen Gründen koexistieren. Wer eigenen Code schreibt, kann sich eine der drei Funktionen aussuchen und damit arbeiten, wer aber Code von anderen Leuten lesen und ändern muss, sollte alle drei kennen. Dies ist eine sehr elegante Lösung, weil man noch ein Timeout dazunehmen kann. Die Spezialfälle 0 und unendlich für das Timeout werden auch sinnvoll unterstützt.

Das “non-Blocking-Prinzip” wird bei der Posix-Systemprogrammierung oft angeboten, z.B. beim Locken von Mutexen, Semaphoren oder beim File-Locking.

Man sollte non-blocking-I/O nicht mit assynchronem I/O verwechseln. Dazu kommt vielleicht ein anderer Artikel..

Share

MoSQL

Bei einem Meetup-Treffen in Zürich wurde ein neues Backend für mysql-Datenbanken vorgestellt, das die Skalierung erleichtern soll:
MoSQL.
Zur Zeit wird daran noch entwickelt, aber man kann Vorabversionen schon testen.

Nun stellt sich die Frage, ob man so etwas braucht, weil die meisten mysql-Installationen ja klein sind und für größere Datenbanken
PostgreSQL, Oracle, DB2 etc. existieren.
Dem kann man entgegensetzen, dass die wenigen großen mysql-Installationen eine besondere Bedeutung haben und deren Betreiber auch oft bereit sind, etwas zu investieren. Der Wechsel des Datenbankprodukts ist in der Praxis oft schwierig, deshalb bleibt man in der Regel bei dem DB-Produkt, mit dem man angefangen hat. Ein Argument am Anfang genau nachzudenken. Dummerweise werden kommerzielle DB-Systeme für richtig große Installationen oft richtig teuer. Nur die Einstiegsversionen sind günstig geworden.

Die andere Frage ist, warum man nicht auf die NoSQL-Schiene setzt. Das hängt vom Problem ab. Oft kann man NoSQL und SQL gut kombinieren für verschiedene Aufgaben. SQ_L als Abfragesprache ist recht mächtig und weit verbreitet und hat deshalb einen gewissen Reiz. NoSQL-Datenbanken haben oft “viel einfachere” Abfragesprachen, wobei es Geschmackssache ist, ob die wirklich einfacher sind und wobei sich die Frage stellt, wie einfach komplizierte Dinge sind, die mit SQL zumindest möglich sind. Die andere Frage ist die Transaktionalität, die bei SQL-DB-Architektueren allerdings auch oft verwäsert wird, vor allem durch Caching. Gerade bei MySQL mit Clustering muss man hier aufpassen und auch bei MoSQL ist das ein Thema, wenn zueinander inkompatible Transaktionen parallel laufen. Durch die massive Parallelisierbarkeit kann so etwas passieren und dann muss eine Transaktion scheitern. Bei sequentieller Abarbeitung der Transaktionen (Modus SERIALZE) wäre das nicht passiert, aber man bekäme auch nicht die Performance. Umgekehrt können viele NoSQL-Datenbanken Transaktionen. Oft nur einfache oder Kombinationen aus zwei Statements, aber wer es wirklich wüst will kann die mit Two-Phase-Commit als “verteilte Transaktion” kombinieren und hat dann sogennante “volle Transaktionalität”. Nur ist two-phase-commit nicht so wasserdicht wie ein lokales Commit….

Share

Ist Ruby funktional

Wenn die Liste der funktionalen Sprachen erstellt wird, dann tauchen Haskell, Scala, Erlang, F#, Clojure und einige andere Lisp-Varianten auf.

Wenn man sich anschaut, welche Merkmale funktionale Sprachen auszeichnen, dann stellt sich die Frage, ob das nicht alles mit Ruby auch machbar ist.

Schauen wir einmal was man so typischerweise findet, meist auf Englisch:

  • Functions as „first class citizen“
  • Closures
  • Pure Functions
  • Higher Order functions
  • Everything returns a value
  • Immutability
  • No hidden state
  • Prefer recursion over iteration (immutable loop variable…)

Funktionen existieren in Ruby also losgelöste Objekte, in verschiedenen Formen, z.B. als proc, als lambda, als anonymer Block und durch Referenzierung einer Methode über ihren Namen (Reflection).

Closures werden von Ruby problemlos unterstützt, bei all diesen Formen der Funktionen werden Variablen aus dem definierenden Kontext eingebunden, wo sie referenziert werden. Methoden kann man übrigens als Spezialfall von Closures ansehen, weil sie als Kontext das Objekt einbinden, zu dem sie gehören.

Unter “pure function” versteht man Funktionen, die den Funktionen aus der Mathematik entsprechen. Sie haben absolut keinen Seiteneffekt, sind reproduzierbar, können in beliebiger Zahl parallel ausgeführt werden und geben bei mehrfachen Aufrufen immer dasselbe Resultat. Sie eignen sich für Memoize, was nun eigentlich wieder ein Seiteneffekt ist, aber sozusagen ein transparenter. Man denke an sort() und sort!() in Ruby. Dies verlangt Disziplin vom Entwickler und gute Dokumentation.

“Higher Order Functions” sind Funktionen höherer Ordnung, die also selbst Funktionen als Parameter oder Rückgabewert haben. Das ist in der funktionalen Programmierung Routine und nicht so ein spezieller Spezialfall, den man mal alle paar Jahre benutzt, wie Funktionspointer in C. Typische Beispiele sind Methoden wie inject(), map(), group_by()… each() sollte man nur verwenden, wenn man die Seiteneffekte wirklich braucht.

Alle Ausdrücke haben einen Wert. Das ist recht gut in Ruby umgesetzt, z.B. x = if (...) ... else ... end kann man verwenden…

Immutibility (Unveränderbarkeit) ist die Achillesferse. Es wird nicht wirklich gut unterstützt. Man soll Variablen nicht neu zuweisen und auch nicht irgendwas mutieren. Warum nimmt man nicht freeze()? Wir brauchen ein deepfreeze(), aber was bedeutet das? Wie sieht es mit collections aus? Es gibt immer Möglichkeiten, diese unverändert zu lassen und mit jedem Schritt eine neue Collection zu produzieren. Dasselbe gilt für Zeichenketten.

“No hidden State”, also kein versteckter Zustand. Man kann Kontextobjekte haben und herumreichen. Zustand ist unerwünscht und sollte kontrolliert an wenigen Orten gehandhabt werden.

“Recursion instead of Iteration” steht für Rekursion statt Iteration. Funktionale Sprachen unterstützen Optimierung bei Tailrekursion (Endrekursion). Einige andere Sprachen, z.B. C mit gcc, Scala und viele Lisp-Dialekte, aber nicht Java, unterstützen diese Optimierung und erlauben es, zumindest Endrekursion ohne zu große Furcht einzusetzen. Bei Ruby hängt es von der Version und den Einstellungen ab, ist also mit Vorsicht zu genießen. Der Ruby-Ansatz ist es eher, die Iteratorn zu verwenden.

Fazit: Man kann mit ein paar Einschränkungen in Ruby funktional programmieren, aber es erfordert etwas mehr Disziplin, weil einige Dinge vom Entwickler beachtet werden müssen und nicht von der Sprache unterstützt werden.

Share

Zufällige Zeichenkette erzeugen

Oft braucht man so eine zufällige Zeichenkette, die nur aus bestimmten Zeichen bestehen darf.

Hier ist eine einfache Ruby-implementierung dafür:


#!/usr/bin/ruby

arr = ('a'...'z').to_a + ('A'...'Z').to_a + ('0'...'9').to_a + ['.', '/']
val = (0..16).inject("") do |a, x| i = (arr.size() * rand()).to_i;a + arr[i] end
puts val

Es wird eine 16-Zeichen lange Zeichenkette generiert, die aus den Zeichen [a-zA-Z0-9./] besteht.

Share