Der grüne Punkt – Mythos Wiederverwendung

Als mir im Studium die Vorzüge der objektorientierten Programmierung mit Java schmackhaft gemacht wurden, war ein sehr beliebtes Argument die Wiederverwendung. Dass der Grundsatz „write once use everywhere“ in der Praxis dann doch nicht so leicht umzusetzen ist, wie es die Theorie suggeriert, haben die meisten Entwickler bereits am eigenen Leib erfahren. Woran liegt es also, dass die Idee der Wiederverwendung in realen Projekten so schwer umzusetzen ist? Machen wir also einen gemeinsamen Streifzug durch die Welt der Informatik und betrachten verschiedene Vorhaben aus sicherer Distanz.

(c) 2022 Elmar Dott, Java akuell Ausgabe 2, S.55 – 57

Wenn ich daran denke, wie viel Zeit ich während meines Studiums investiert habe, um eine Präsentationsvorlage für Referate zu erstellen. Voller Motivation habe ich alle erdenklichen Ansichten in weiser Voraussicht erstellt. Selbst rückblickend war das damalige Layout für einen Nichtgrafiker ganz gut gelungen. Trotzdem kam die tolle Vorlage nur wenige Male zum Einsatz und wenn ich im Nachhinein einmal Resümee ziehe, komme ich zu dem Schluss, dass die investierte Arbeitszeit in Bezug auf die tatsächliche Verwendung in keinem Verhältnis gestanden hat. Von den vielen verschiedenen Ansichten habe ich zum Schluss exakt zwei verwendet, das Deckblatt und eine allgemeine Inhaltsseite, mit der alle restlichen Darstellungen umgesetzt wurden. Die restlichen 15 waren halt da, falls man das künftig noch brauchen würde. Nach dieser Erfahrung plane ich keine eventuell zukünftig eintreffenden Anforderungen mehr im Voraus. Denn den wichtigsten Grundsatz in Sachen Wiederverwendung habe ich mit dieser Lektion für mich gelernt: Nichts ist so beständig wie die Änderung.

Diese kleine Anekdote trifft das Thema bereits im Kern. Denn viele Zeilen Code werden genau aus der gleichen Motivation heraus geschrieben. Der Kunde hat es noch nicht beauftragt, doch die Funktion wird er ganz sicher noch brauchen. Wenn wir in diesem Zusammenhang einmal den wirtschaftlichen Kontext ausblenden, gibt es immer noch ausreichend handfeste Gründe, durch die Fachabteilung noch nicht spezifizierte Funktionalität nicht eigenmächtig im Voraus zu implementieren. Für mich ist nicht verwendeter, auf Halde produzierter Code – sogenannter toter Code – in erster Linie ein Sicherheitsrisiko. Zusätzlich verursachen diese Fragmente auch Wartungskosten, da bei Änderungen auch diese Bereiche möglicherweise mit angepasst werden müssen. Schließlich muss die gesamte Codebasis kompilierfähig bleiben. Zu guter Letzt kommt noch hinzu, dass die Kollegen oft nicht wissen, dass bereits eine ähnliche Funktion entwickelt wurde, und diese somit ebenfalls nicht verwenden. Die Arbeit wird also auch noch doppelt ausgeführt. Nicht zu vergessen ist auch das von mir in großen und langjährig entwickelten Applikationen oft beobachtete Phänomen, dass ungenutzte Fragmente aus Angst, etwas Wichtiges zu löschen, über Jahre hinweg mitgeschleppt werden. Damit kommen wir auch schon zum zweiten Axiom der Wiederverwendung: Erstens kommt es anders und zweitens als man denkt.

Über die vielen Jahre, genauer gesagt Jahrzehnte, in denen ich nun verschiedenste IT- beziehungsweise Softwareprojekte begleitet habe, habe ich ein Füllhorn an Geschichten aus der Kategorie „Das hätte ich mir sparen können!“ angesammelt. Virtualisierung ist nicht erst seit Docker [1] auf der Bildfläche erschienen – es ist schon weitaus länger ein beliebtes Thema. Die Menge der von mir erstellten virtuellen Maschinen (VMs) kann ich kaum noch benennen – zumindest waren es sehr viele. Für alle erdenklichen Einsatzszenarien hatte ich etwas zusammengebaut. Auch bei diesen tollen Lösungen erging es mir letztlich nicht viel anders als bei meiner Office-Vorlage. Grundsätzlich gab es zwei Faktoren, die sich negativ ausgewirkt haben. Je mehr VMs erstellt wurden, desto mehr mussten dann auch gewertet werden. Ein Worst-Case-Szenario heutzutage wäre eine VM, die auf Windows 10 basiert, die dann jeweils als eine .NET- und eine Java-Entwicklungsumgebung oder Ähnliches spezialisiert wurde. Allein die Stunden, die man für Updates zubringt, wenn man die Systeme immer mal wieder hochfährt, summieren sich auf beachtliche Größen. Ein Grund für mich zudem, soweit es geht, einen großen Bogen um Windows 10 zu machen. Aus dieser Perspektive können selbsterstellte DockerContainer schnell vom Segen zum Fluch werden.

Dennoch darf man diese Dinge nicht gleich überbewerten, denn diese Aktivitäten können auch als Übung verbucht werden. Wichtig ist, dass solche „Spielereien“ nicht ausarten und man neben den technischen Erfahrungen auch den Blick für tatsächliche Bedürfnisse auf lange Sicht schärft.

Gerade bei Docker bin ich aus persönlicher Erfahrung dazu übergegangen, mir die für mich notwendigen Anpassungen zu notieren und zu archivieren. Komplizierte Skripte mit Docker-Compose spare ich mir in der Regel. Der Grund ist recht einfach: Die einzelnen Komponenten müssen zu oft aktualisiert werden und der Einsatz für jedes Skript findet in meinem Fall genau einmal statt. Bis man nun ein lauffähiges Skript zusammengestellt hat, benötigt man, je nach Erfahrung, mehrere oder weniger Anläufe. Also modifiziere ich das RUN-Kommando für einen Container, bis dieser das tut, was ich von ihm erwarte. Das vollständige Kommando hinterlege ich in einer Textdatei, um es bei Bedarf wiederverwenden zu können. Dieses Vorgehen nutze ich für jeden Dienst, den ich mit Docker virtualisiere. Dadurch habe ich die Möglichkeit, verschiedenste Konstellationen mit minimalen Änderungen nach dem „Klemmbaustein“-Prinzip zu rchestrieren. Wenn sich abzeichnet, dass ein Container sehr oft unter gleichen Bedienungen instanziiert wird, ist es sehr hilfreich, diese Konfiguration zu automatisieren. Nicht ohne Grund gilt für Docker-Container die Regel, möglichst nur einen Dienst pro Container zu virtualisieren.

Aus diesen beiden kleinen Geschichten lässt sich bereits einiges für Implementierungsarbeiten am Code ableiten. Ein klassischer Stolperstein, der mir bei der täglichen Projektarbeit regelmäßig unterkommt, ist, dass man mit der entwickelten Applikation eine eierlegende Wollmilchsau – oder, wie es in Österreich heißt: ein Wunderwutzi – kreieren möchte. Die Teams nehmen sich oft zu viel vor und das Projektmanagement versucht, den Product Owner auch nicht zu bekehren, lieber auf Qualität statt auf Quantität zu setzen. Was ich mit dieser Aussage deutlich machen möchte, lässt sich an einem kleinen Beispiel verständlich machen.

Gehen wir einmal davon aus, dass für eigene Projekte eine kleine Basisbibliothek benötigt wird, in der immer wiederkehrende Problemstellungen zusammengefasst werden – konkret: das Verarbeiten von JSON-Objekten [2]. Nun könnte man versuchen, alle erdenklichen Variationen im Umgang mit JSON abzudecken. Abgesehen davon, dass viel Code produziert wird, erzielt ein solches Vorgehen wenig Nutzen. Denn für so etwas gibt es bereits fertige Lösungen – etwa die freie Bibliothek Jackson [3]. Anstelle sämtlicher denkbarer JSON-Manipulationen ist in Projekten vornehmlich das Serialisieren und das Deserialisieren gefragt. Also eine Möglichkeit, wie man aus einem Java-Objekt einen JSON-String erzeugt, und umgekehrt. Diese beiden Methoden lassen sich leicht über eine Wrapper-Klasse zentralisieren. Erfüllt nun künftig die verwendete JSON-Bibliothek die benötigten Anforderungen nicht mehr, kann sie leichter durch eine besser geeignete Bibliothek ersetzt werden. Ganz nebenbei erhöhen wir mit diesem Vorgehen auch die Kompatibilität [4] unserer Bibliothek für künftige Erweiterungen. Wenn JSON im Projekt eine neu eingeführte Technologie ist, kann durch die Minimal-Implementierung stückweise Wissen aufgebaut werden. Je stärker der JSONWrapper nun in eigenen Projekten zum Einsatz kommt, desto wahrscheinlicher ist es, dass neue Anforderungen hinzukommen, die dann erst umgesetzt werden, wenn sie durch ein Projekt angefragt werden. Denn wer kann schon abschätzen, wie der tatsächliche Bedarf einer Funktionalität ist, wenn so gut wie keine Erfahrungen zu der eingesetzten Technologie vorhanden sind?

Das soeben beschriebene Szenario läuft auf einen einfachen Merksatz hinaus: Eine neue Implementierung möglichst so allgemein wie möglich halten, um sie nach Bedarf immer weiter zu spezialisieren.

Bei komplexen Fachanwendungen hilft uns das Domain-driven Design (DDD) Paradigma, Abgrenzungen zu Domänen ausfindig zu machen. Auch hierfür lässt sich ein leicht verständliches, allgemein gefasstes Beispiel finden. Betrachten wir dazu einmal die Domäne einer Access Control List (ACL). In der ACL wird ein Nutzerkonto benötigt, mit dem Berechtigungen zu verschiedenen Ressourcen verknüpft werden. Nun könnte man auf die Idee kommen, im Account in der ACL sämtliche Benutzerinformationen wie Homepage, Postadresse und Ähnliches abzulegen. Genau dieser Fall würde die Domäne der ACL verletzen, denn das Benutzerkonto benötigt lediglich Informationen, die zur Authentifizierung benötigt werden, um eine entsprechende Autorisierung zu ermöglichen.

Jede Anwendung hat für das Erfassen der benötigten Nutzerinformationen andere Anforderungen, weshalb diese Dinge nicht in eine ACL gehören sollten. Das würde die ACL zu sehr spezialisieren und stetige Änderungen verursachen. Daraus resultiert dann auch, dass die ACL nicht mehr universell einsatzfähig ist.

Man könnte nun auf die Idee kommen, eine sehr generische Lösung für den Speicher zusätzlicher Nutzerinformationen zu entwerfen
und ihn in der ACL zu verwenden. Von diesem Ansatz möchte ich abraten. Ein wichtiger Grund ist, dass diese Lösung die Komplexität der ACL unnötig erhöht. Ich gehe obendrein so weit und möchte behaupten, dass unter ungünstigen Umständen sogar Code-Dubletten entstehen. Die Begründung dafür ist wie folgt: Ich sehe eine generische Lösung zum Speichern von Zusatzinformationen im klassischen Content Management (CMS) verortet. Die Verknüpfung zwischen ACL und CMS erfolgt über die Benutzer-ID aus der ACL. Somit haben wir gleichzeitig auch zwischen den einzelnen Domänen eine lose Kopplung etabliert, die uns bei der Umsetzung einer modularisierten Architektur sehr behilflich sein wird.

Zum Thema Modularisierung möchte ich auch kurz einwerfen, dass Monolithen [5] durchaus auch aus mehreren Modulen bestehen können und sogar sollten. Es ist nicht zwangsläufig eine Microservice-Architektur notwendig. Module können aus unterschiedlichen Blickwinkeln betrachtet werden. Einerseits erlauben sie es einem Team, in einem fest abgegrenzten Bereich ungestört zu arbeiten, zum anderen kann ein Modul mit einer klar abgegrenzten Domäne ohne viele Adaptionen tatsächlich in späteren Projekten wiederverwendet werden.

Nun ergibt sich klarerweise die Fragestellung, was mit dem Übergang von der Generalisierung zur Spezialisierung gemeint ist. Auch hier hilft uns das Beispiel der ACL weiter. Ein erster Entwurf könnte die Anforderung haben, dass, um unerwünschte Berechtigungen falsch konfigurierter Rollen zu vermeiden, die Vererbung von Rechten bestehender Rollen nicht erwünscht ist. Daraus ergibt sich dann der Umstand, dass jedem Nutzer genau eine Rolle zugewiesen werden kann. Nun könnte es sein, dass durch neue Anforderungen der Fachabteilung eine Mandantenfähigkeit eingeführt werden soll. Entsprechend muss nun in der ACL eine Möglichkeit geschaffen werden, um bestehende Rollen und auch Nutzeraccounts einem Mandanten zuzuordnen. Eine Domänen-Erweiterung dieser hinzugekommenen Anforderung ist nun basierend auf der bereits bestehenden Domäne durch das Hinzufügen neuer Tabellenspalten leicht umzusetzen.

Die bisher aufgeführten Beispiele beziehen sich ausschließlich auf die Implementierung der Fachlogik. Viel komplizierter verhält sich das Thema Wiederverwendung beim Punkt der grafischen Benutzerschnittelle (GUI). Das Problem, das sich hier ergibt, ist die Kurzlebigkeit vieler chnologien. Java Swing existiert zwar noch, aber vermutlich würde sich niemand, der heute eine neue Anwendung entwickelt, noch für Java Swing entscheiden. Der Grund liegt in veraltetem Look-and-Feel der Grafikkomponenten. Um eine Applikation auch verkaufen zu können, darf man den Aspekt der Optik nicht außen vor lassen. Denn auch das Auge isst bekanntlich mit. Gerade bei sogenannten Green-Field-Projekten ist der Wunsch, eine moderne, ansprechende Oberfläche anbieten zu können, implizit. Deswegen vertrete ich die Ansicht, dass das Thema Wiederverwendung für GUI – mit wenigen Ausnahmen – keine wirkliche Rolle spielt.

Lessons Learned

Sehr oft habe ich in der Vergangenheit erlebt, wie enthusiastisch bei Kick-off-Meetings die Möglichkeit der Wiederverwendung von Komponenten in Aussicht gestellt wurde. Dass dies bei den verantwortlichen Managern zu einem Glitzern in den Augen geführt hat, ist auch nicht verwunderlich. Als es dann allerdings zu ersten konkreten Anfragen gekommen ist, eine Komponente in einem anderen Projekt einzusetzen, mussten sich alle Beteiligten eingestehen, dass dieses Vorhaben gescheitert war. In den nachfolgenden Retrospektiven sind die Punkte, die ich in diesem Artikel vorgestellt habe, regelmäßig als Ursachen identifiziert worden. Im Übrigen genügt oft schon ein Blick in das Datenbankmodell oder auf die Architektur einer Anwendung, um eine Aussage treffen zu können, wie realistisch eine Wiederverwendung tatsächlich ist. Bei steigendem Komplexitätsgrad sinkt die Wahrscheinlichkeit, auch nur kleinste Segmente erfolgreich für eine Wiederverwendung herauslösen zu können.

Referenzen
[1] https://www.docker.com
[2] https://www.json.org
[3] https://github.com/FasterXML/jackson/
[4] https://entwickler.de/api/futurama-nichts-ist-so-bestandig-wiedie-veranderung/
[5] https://entwickler.de/microservices/slice-down-the-monolith-001/

Non-Functional Requirements: Quality

published also on DZone 02.2020

published also on DZone 02.2020

By experience, most of us know how difficult it is to express what we mean talking about quality. Why is that so?  There exist many different views on quality and every one of them has its importance. What has to be defined for our project is something that fits its needs and works with the budget. Trying to reach perfectionism can be counterproductive if a project is to be terminated successfully. We will start based on a research paper written by B. W. Boehm in 1976 called “Quantitative evaluation of software quality.” Boehm highlights the different aspects of software quality and the right context. Let’s have a look more deeply into this topic.

When we discuss quality, we should focus on three topics: code structure, implementation correctness, and maintainability. Many managers just care about the first two aspects, but not about maintenance. This is dangerous because enterprises will not invest in individual development just to use the application for only a few years. Depending on the complexity of the application the price for creation could reach hundreds of thousands of dollars. Then it’s understandable that the expected business value of such activities is often highly estimated. A lifetime of 10 years and more in production is very typical. To keep the benefits, adaptions will be mandatory. That implies also a strong focus on maintenance. Clean code doesn’t mean your application can simply change. A very easily understandable article that touches on this topic is written by Dan Abramov. Before we go further on how maintenance could be defined we will discuss the first point: the structure.

Scaffolding Your Project

An often underestimated aspect in development divisions is a missing standard for project structures. A fixed definition of where files have to be placed helps team members find points of interests quickly. Such a meta-structure for Java projects is defined by the build tool Maven. More than a decade ago, companies tested Maven and readily adopted the tool to their established folder structure used in the projects. This resulted in heavy maintenance tasks, given the reason that more and more infrastructure tools for software development were being used. Those tools operate on the standard that Maven defines, meaning that every customization affects the success of integrating new tools or exchanging an existing tool for another.

Another aspect to look at is the company-wide defined META architecture. When possible, every project should follow the same META architecture. This will reduce the time it takes a new developer to join an existing team and catch up with its productivity. This META architecture has to be open for adoptions which can be reached by two simple steps:

  1. Don’t be concerned with too many details;
  2. Follow the KISS (Keep it simple, stupid.) principle.

A classical pattern that violates the KISS principle is when standards heavily got customized. A very good example of the effects of strong customization is described by George Schlossnagle in his book “Advanced PHP Programming.” In chapter 21 he explains the problems created for the team when adopting the original PHP core and not following the recommended way via extensions. This resulted in the effect that every update of the PHP version had to be manually manipulated to include its own development adaptations to the core. In conjunction, structure, architecture, and KISS already define three quality gates, which are easy to implement.

The open-source project TP-CORE, hosted on GitHub, concerns itself with the afore-mentioned structure, architecture, and KISS. There you can find their approach on how to put it in practice. This small Java library rigidly defined the Maven convention with his directory structure. For fast compatibility detection, releases are defined by semantic versioning. The layer structure was chosen as its architecture and is fully described here. Examination of their main architectural decisions concludes as follows:

Each layer is defined by his own package and the files following also a strict rule. No special PRE or POST-fix is used. The functionality Logger, for example, is declared by an interface called Logger and the corresponding implementation LogbackLogger. The API interfaces can detect in the package “business” and the implementation classes located in the package “application.” Naming like ILogger and LoggerImpl should be avoided. Imagine a project that was started 10 years ago and the LoggerImpl was based on Log4J. Now a new requirement arises, and the log level needs to be updated during run time. To solve this challenge, the Log4J library could be replaced with Logback. Now it is understandable why it is a good idea to name the implementation class like the interface, combined with the implementation detail: it makes maintenance much easier! Equal conventions can also be found within the Java standard API. The interface List is implemented by an ArrayList. Obviously, again the interface is not labeled as something like IList and the implementation not as ListImpl .

Summarizing this short paragraph, a full measurement rule set was defined to describe our understanding of structural quality. By experience, this description should be short. If other people can easily comprehend your intentions, they willingly accept your guidance, deferring to your knowledge. In addition, the architect will be much faster in detecting rule violations.

Measure Your Success

The most difficult part is to keep a clean code. Some advice is not bad per se, but in the context of your project, may not prove as useful. In my opinion, the most important rule would be to always activate the compiler warning, no matter which programming language you use! All compiler warnings will have to be resolved when a release is prepared. Companies dealing with critical software, like NASA, strictly apply this rule in their projects resulting in utter success.

Coding conventions about naming, line length, and API documentation, like JavaDoc, can be simply defined and observed by tools like Checkstyle. This process can run fully automated during your build. Be careful; even if the code checkers pass without warnings, this does not mean that everything is working optimally. JavaDoc, for example, is problematic. With an automated Checkstyle, it can be assured that this API documentation exists, although we have no idea about the quality of those descriptions.

There should be no need to discuss the benefits of testing in this case; let us rather take a walkthrough of test coverage. The industry standard of 85% of covered code in test cases should be followed because coverage at less than 85% will not reach the complex parts of your application. 100% coverage just burns down your budget fast without resulting in higher benefits. A prime example of this is the TP-CORE project, whose test coverage is mostly between 92% to 95%. This was done to see real possibilities.

As already explained, the business layer contains just interfaces, defining the API. This layer is explicitly excluded from the coverage checks. Another package is called internal and it contains hidden implementations, like the SAX DocumentHandler. Because of the dependencies the DocumentHandler is bound to, it is very difficult to test this class directly, even with Mocks. This is unproblematic given that the purpose of this class is only for internal usage. In addition, the class is implicitly tested by the implementation using the DocumentHandler. To reach higher coverage, it also could be an option to exclude all internal implementations from checks. But it is always a good idea to observe the implicit coverage of those classes to detect aspects you may be unaware of.

Besides the low-level unit tests, automated acceptance tests should also be run. Paying close attention to these points may avoid a variety of problems. But never trust those fully automated checks blindly! Regularly repeated manual code inspections will always be mandatory, especially when working with external vendors. In our talk at JCON 2019, we demonstrated how simply test coverage could be faked. To detect other vulnerabilities you can additionally run checkers like SpotBugs and others more.

Tests don’t indicate that an application is free of failures, but they indicate a defined behavior for implemented functionality.

For a while now, SCM suites like GitLab or Microsoft Azure support pull requests, introduced long ago in GitHub. Those workflows are nothing new; IBM Synergy used to apply the same technique. A Build Manager was responsible to merge the developers’ changes into the codebase. In a rapid manner, all the revisions performed by the developer are just added into the repository by the Build Manager, who does not hold a sufficiently profound knowledge to decide about the implementation quality. It was the usual practice to simply secure that the build is not broken and always the compile produce an artifact.

Enterprises have discovered this as a new strategy to handle pull requests. Now, managers often make the decision to use pull requests as a quality gate. In my personal experience, this slows down productivity because it takes time until the changes are available in the codebase. Understanding of the branch and merge mechanism helps you to decide for a simpler branch model, like release branch lines. On those branches tools like SonarQube operate to observe the overall quality goal.

If a project needs an orchestrated build, with a defined order how artifacts have to create, you have a strong hint for a refactoring.

The coupling between classes and modules is often underestimated. It is very difficult to have an automated visualization for the bindings of modules. You will find out very fast the effect it has when a light coupling is violated because of an increment of complexity in your build logic.

Repeat Your Success

Rest assured, changes will happen! It is a challenge to keep your application open for adjustments. Several of the previous recommendations have implicit effects on future maintenance. A good source quality simplifies the endeavor of being prepared. But there is no guarantee. In the worst cases the end of the product lifecycle, EOL is reached, when mandatory improvements or changes cannot be realized anymore because of an eroded code base, for example.

As already mentioned, light coupling brings with it numerous benefits with respect to maintenance and reutilization. To reach this goal is not that difficult as it might look. In the first place, try to avoid as much as possible the inclusion of third-party libraries. Just to check if a String is empty or NULL it is unnecessary to depend on an external library. These few lines are fast done by oneself. A second important point to be considered in relation to external libraries: “Only one library to solve a problem.” If your project deals with JSON then decide one one implementation and don’t incorporate various artifacts. These two points heavily impact on security: a third-party artifact we can avoid using will not be able to cause any security leaks.

After the decision is taken for an external implementation, try to cover the usage in your project by applying design patterns like proxy, facade, or wrapper. This allows for a replacement more easily because the code changes are not spread around the whole codebase. You don’t need to change everything at once if you follow the advice on how to name the implementation class and provide an interface. Even though a SCM is designed for collaboration, there are limitations when more than one person is editing the same file. Using a design pattern to hide information allows you an iterative update of your changes.

Conclusion

As we have seen: a nonfunctional requirement is not that difficult to describe. With a short checklist, you can clearly define the important aspects for your project. It is not necessary to check all points for every code commit in the repository, this would with all probability just elevate costs and doesn’t result in higher benefits. Running a full check around a day before the release represents an effective solution to keep quality in an agile context and will help recognizing where optimization is necessary. Points of Interests (POI) to secure quality are the revisions in the code base for a release. This gives you a comparable statistic and helps increasing estimations.

Of course, in this short article, it is almost impossible to cover all aspects regarding quality. We hope our explanation helps you to link theory by examples to best practice. In conclusion, this should be your main takeaway: a high level of automation within your infrastructure, like continuous integration, is extremely helpful, but doesn’t prevent you from manual code reviews and audits.

Checklist

  • Follow common standards
  • KISS – keep it simple, stupid!
  • Equal directory structure for different projects
  • Simple META architecture, which can reuse as much as possible in other projects
  • Defined and follow coding styles
  • If a release got prepared – no compiler warnings are accepted
  • Have test coverage up to 85%
  • Avoid third-party libraries as much as possible
  • Don’t support more than one technology for a specific problem (e. g., JSON)
  • Cover foreign code by a design pattern
  • Avoid strong object/module coupling

Acceptance Tests in Java With JGiven

published also on DZone 01.2020

published also on DZone 01.2020

Most of the developer community know what a unit test is, even they don’t write them. But there is still hope. The situation is changing. More and more projects hosted on GitHub contain unit tests.

In a standard set-up for Java projects like NetBeans, Maven, and JUnit, it is not that difficult to produce your first test code. Besides, this approach is used in Test Driven Development (TDD) and exists in other technologies like Behavioral Driven Development (BDD), also known as acceptance tests, which is what we will focus on in this article.

Difference Between Unit and Acceptance Tests

The easiest way to become familiar with this topic is to look at a simple comparison between unit and acceptance tests. In this context, unit tests are very low level. They execute a function and compare the output with an expected result. Some people think differently about it, but in our example, the only one responsible for a unit test is the developer.

Keep in mind that the test code is placed in the project and always gets executed when the build is running. This provides quick feedback as to whether or not something went wrong. As long the test doesn’t cover too many aspects, we are able to identify the problem quickly and provide a solution. The design principle of those tests follows the AAA paradigm. Define a precondition (Arrange), execute the invariant (Act), and check the postconditions (Assume). We will come back to this approach a little later.

When we check the test coverage with tools like JaCoCo and cover more than 85 percent of our code with test cases, we can expect good quality. During the increasing test coverage, we specify our test cases more precisely and are able to identify some optimizations. This can be removing or inverting conditions because during the tests we find out, it is almost impossible to reach those sections. Of course, the topic is a bit more complicated, but those details could be discussed in another article.

Acceptance test are same classified like unit tests. They belong to the family of regression tests. This means we want to observe if changes we made on the code have no effects on already worked functionality. In other words, we want to secure that nothing already is working got broken, because of some side effects of our changes. The tool of our choice is JGiven [1]. Before we look at some examples, first, we need to touch on a bit of theory.

JGiven In-Depth

The test cases we define in JGiven is called a scenario. A scenario is a collection of four classes, the scenario itself, the Given displayed as given (Arrange), the Action displayed as when (Act) and Outcome displayed as then (Assume).

In most projects, especially when there is a huge amount of scenarios and the execution consumes a lot of time, acceptance tests got organized in a separate project. With a build job on your CI server, you can execute those tests once a day to get fast feedback and to react early if something is broken. The code example we demonstrate contains everything in one project on GitHub [2] because it is just a small library and a separation would just over-engineer the project. Usually, the one responsible for acceptance tests is the test center, not the developer.

The sample project TP-CORE is organized by a layered architecture. For our example, we picked out the functionality for sending e-mails. The basic functionality to compose an e-mail is realized in the application layer and has a test coverage of up to 90 percent. The functionality to send the e-mail is defined in the service layer.

In our architecture, we decided that the service layer is our center of attention to defining acceptance tests. Here, we want to see if our requirement to send an e-mail is working well. Supporting this layer with our own unit tests is not that efficient because, in commercial projects, it just produces costs without winning benefits. Also, having also unit tests means we have to do double the work because our JGiven tests already demonstrate and prove that our function is well working. For those reasons, it makes no sense to generate test coverage for the test scenarios of the acceptance test.

Let’s start with a practice example. At first, we need to include our acceptance test framework into our Maven build. In case you prefer Gradle, you can use the same GAV parameters to define the dependencies in your build script.

<dependency>
   <groupId>com.tngtech.jgiven</groupId>
   <artifactId>jgiven-junit</artifactId>
   <version>0.18.2</version>
   <scope>test</scope>
</dependency>

Listing 1: Dependency for Maven.

As you can see in listing 1, JGiven works well together with JUnit. An integration to TestNG also exists , you just need to replace the artifactId for jgiven-testng. To enable the HTML reports, you need to configure the Maven plugin in the build lifecycle, like it is shown in Listing 2.

<build> 
   <plugins>
      <plugin>
         <groupId>com.tngtech.jgiven</groupId>
         <artifactId>jgiven-maven-plugin</artifactId>
         <version>0.18.2</version>
         <executions>
            <execution>
               <goals>
                  <goal>report</goal>
               </goals>
            </execution>
         </executions>
         <configuration>
            <format>html</format>
         </configuration>
      </plugin>
   </plugins>
</build>

Listing 2: Maven Plugin Configuration for JGiven.

The report of our scenarios in the TP-CORE project is shown in image 1. As we can see, the output is very descriptive and human-readable. This result will be explained by following some naming conventions for our methods and classes, which will be explained in detail below. First, let’s discuss what we can see in our test scenario. We defined five preconditions:

  1. The configuration for the SMPT server is readable
  2. The SMTP server is available
  3. The mail has a recipient
  4. The mail has attachments
  5. The mail is full composed

If all these conditions are true, the action will send a single e-mail got performed. Afterward, after the SMTP server is checked, we see that the mail has arrived. For the SMTP service, we use the small Java library greenmail [3] to emulate an SMTP server. Now it is understandable why it is advantageous for acceptance tests if they are written by other people. This increases the quality as early on conceptional inconsistencies appear. Because as long as the tester with the available implementations cannot map the required scenario, the requirement is not fully implemented.

Producing Descriptive Scenarios

Now is the a good time to dive deeper into the implementation details of our send e-mail test scenario. Our object under test is the class MailClientService. The corresponding test class is  MailClientScenarioTest, defined in the test packages. The scenario class definition is shown in listing 3.

@RunWith(JUnitPlatform.class)
public class MailClientScenarioTest
       extends ScenarioTest<MailServiceGiven, MailServiceAction, MailServiceOutcome> { 
    // do something 
}

Listing 3: Acceptance Test Scenario for JGiven.

As we can see, we execute the test framework with JUnit5. In the  ScenarioTest, we can see the three classes: Given, Action, and Outcome in a special naming convention. It is also possible to reuse already defined classes, but be careful with such practices. This can cost some side effects. Before we now implement the test method, we need to define the execution steps. The procedure for the three classes are equivalent.

@RunWith(JUnitPlatform.class)
public class MailServiceGiven 
       extends Stage<MailServiceGiven> { 

    public MailServiceGiven email_has_recipient(MailClient client) {
        try { 
            assertEquals(1, client.getRecipentList().size());
        } catch (Exception ex) {
            System.err.println(ex.getMessage);
        }
        return self(); 
    } 
} 

@RunWith(JUnitPlatform.class)
public class MailServiceAction
       extends Stage<MailServiceAction> { 

    public MailServiceAction send_email(MailClient client) {
        MailClientService service = new MailClientService();
        try {
            assertEquals(1, client.getRecipentList().size());
            service.sendEmail(client);
        } catch (Exception ex) { 
            System.err.println(ex.getMessage);
        }
        return self();
    }
}

@RunWith(JUnitPlatform.class)
public class MailServiceOutcome 
       extends Stage<MailServiceOutcome> {

    public MailServiceOutcome email_is_arrived(MimeMessage msg) { 
         try {
             Address adr = msg.getAllRecipients()[0];
             assertEquals("JGiven Test E-Mail", msg.getSubject());
             assertEquals("noreply@sample.org", msg.getSender().toString());
             assertEquals("otto@sample.org", adr.toString());
             assertNotNull(msg.getSize());
         } catch (Exception ex) {
             System.err.println(ex.getMessage);
         }
         return self();
    }
}

Listing 4: Implementing the AAA Principle for Behavioral Driven Development.

Now, we completed the cycle and we can see how the test steps got stuck together. JGiven supports a bigger vocabulary to fit more necessities. To explore the full possibilities, please consult the documentation.

Lessons Learned

In this short workshop, we passed all the important details to start with automated acceptance tests. Besides JGiven exist other frameworks, like Concordion or FitNesse fighting for usage. Our choice for JGiven was its helpful documentation, simple integration into Maven builds and JUnit tests, and the descriptive human-readable reports.

As negative point, which could people keep away from JGiven, could be the detail that you need to describe the tests in the Java programming language. That means the test engineer needs to be able to develop in Java, if they want to use JGiven. Besides this small detail, our experience with JGiven is absolutely positive.

Resources

[1] http://jgiven.org
[2] https://github.com/ElmarDott/TP-CORE
[3] http://www.icegreen.com/greenmail

Sicherheit und Datenschutz in verteilten Netzwerken

Der Erfolg des Internet-Service Cloud umfasst verschiedene Aspekte, von denen der schwergewichtigste Grund für diese neue Technologie, mit weitem Abstand monetär motiviert ist. Die finanzielle Planungssicherheit bei den Investitionskosten für die Infrastruktur seitens der Nutzer besticht ebenso zur Cloud Migration, wie das zentralisierte Ausliefern der Dienste seitens der Anbieter. Dennoch gibt es bei all den positiven Sichtweisen auf die Cloud einen wichtigen Faktor, der eine rasante Verbreitung flächendeckender Lösungen abmildert. Die im Jahre 2013 durch Edward Snowden veröffentlichten Informationen über die amerikanische Spionage gegenüber der gesamten Welt, veranlasste besonders im europäischen Raum, heftige Debatten seitens der Politik über Sicherheit und Datenschutz. Da Cloud Services zum größten Teil auf Netzwerktechnologie basiert und die derzeit größten Anbieter amerikanische Unternehmen sind, haben die Enthüllungen Snowdens das Vertrauen in die neue Technologie nachhaltig erschüttert. Die Forderungen nach einer Europäischen Lösung sollte die Problematik des Datenschutzes neu adressieren und für zukünftige Innovationen der IT Branche die Signale in Richtung freier Fahrt stellen. In der nachfolgenden Ausarbeitung werden unterschiedliche Facetten der IT Sicherheit betrachtet. Dabei ist das Augenmerk größtenteils auf Hintergründe, Zusammenhänge und politischem Background gerichtet. Technische Konzepte werden an den entsprechenden Stellen nur kurz angedeutet und auf die entsprechenden Quellen verwiesen, da besonders im Bereich der Web – Technologien viele Gefahren bereits gelöst sind.

(c) 2014 – Marco Schulz & Pascal Werkl, Seminararbeit der TU Graz

Datenschutz und Überwachung

Als im Sommer 1989 die Bürger der Deutschen Demokratischen Republik auf die Straßen gegangen sind, haben sie ihren Unmut gegen ein System zum Ausdruck gebracht, dass die Rechte des Einzelnen auf persönliche Selbstbestimmung und Meinungsfreiheit auf das sträflichste missachtet hat. Mit der Organisation Staatssicherheit (STASI) wurde über Jahre eine allmächtige Institution geschaffen, die abseits aller Legitimation, willkürlich die Schicksale vieler Menschen negativ beeinflusst hat. Unter dem Deckmantel der Friedenssicherung wurde die gesamte Bevölkerung systematisch erfasst und überwacht. Mit den gewonnenen Erkenntnissen wurden gegen Kritiker der Diktatur verschiedenste Maßnahmen zur Willensbrechung in die Wege geleitet. Mit gezielten Aktionen, die beruflichen und privaten Misserfolg förderten, sollten so die “subversiven Elemente” gebrochen werden. Die Intensität solcher Maßnahmen wurde nicht selten so weit getrieben, dass die Opfer keinen anderen Ausweg als den Suizid fanden. Langjährige Haftstrafen für Gegner des totalitären Regimes waren die traurige Regel und selbst vor Mord schreckte das System nicht zurück. Man sollte sich dessen bewusst sein, dass die Vorgehensweisen der STASI lediglich eine Weiterentwicklung der Methoden sind, derer sich die deutsche Gestapo bediente um den Holocaust zu inszenieren. Bereits diese historisch dokumentierten Tatsachen aus jüngster Geschichte sollte der breiten Öffentlichkeit die Augen öffnen um alle Möglichkeiten auszuschöpfen, eine totale Überwachung durch staatliche oder private Institutionen zu verhindern. Wohin ein solcher Weg führen kann hat George Orwell in seinem Roman “1984” aufgezeigt, der in den 1940 ‘er Jahren unter den Eindrücken des 2. Weltkrieges entstanden ist.

Mit den schlimmen Terroranschlägen vom 11. September 2001 gegen die USA und dem damit verbundenen Kampf gegen den Terror, haben viele Staaten in jüngster Vergangenheit eine Reihe an Gesetzen auf den Weg gebracht, die die Persönlichkeitsrechte des Einzelnen missachten und somit auch die Demokratie gefährden. Die Vorratsdatenspeicherung, welche sämtliche Kommunikationsdaten wie E-Mail, SMS und Mobiltelefonie für mindestens 6 Monate vorhält, ist ein verehrendes Beispiel, wie eine gesamte Bevölkerung unter Generalverdacht gestellt wird. Moderne Algorithmen1, die auf solchen Informationen operieren, können Menschen vollautomatisch klassifizieren. Die gewonnenen Erkenntnisse können dann wiederum herangezogen werden, um beispielsweise einzelnen Gruppen den Zugang zu Bildung, korrekte medizinische Betreuung oder den Erwerb von Eigentum zu erschweren oder ganz zu versagen. Wie bereits aufgezeigt, hat die Geschichte mehrfach bewiesen, dass solche Überlegungen nicht aus der Luft gegriffen sind. Im heutigen Informationszeitalter besteht zusätzlich die Möglichkeit einer gezielten Manipulation des Einzelnen. Das heißt diesen der öffentlichen Meinung anzugliedern, indem er anhand der gesammelten Daten über Psychologie- als auch Marketing-Strategien mit der Masse gleichgeschaltet wird. In der heutigen Gesellschaft sind die meisten Protagonisten vollständig digital vernetzt. Über online Einkäufe, Kreditkarten, elektronische Abonnements für Multimedia wie Spiele, Musik, Bücher und Filme, Soziale Netzwerke, Telefonate und E-Mail generiert ein jeder Mensch unbedacht eine Vielzahl von Daten, an denen Industrie und Politik aus unterschiedlichsten Gründen gesteigertes Interesse haben. Sofern es einen, wenn auch vorerst stark eingeschränkten, gesetzlichen Rahmen zur Verknüpfung verschiedenster Informationen zu einer zentralen Datenbank gibt, ist es eine Frage der Zeit bis Institutionen, für die ein Zugriff nie vorgesehen war, ihren Anspruch auf diese Information fordern und höchstwahrscheinlich auch erhalten werden. Ein erschreckend Beispiel dafür ist die in Deutschland eingeführte Maut für LKW und Bus auf Autobahnen. Regelmäßig flammt die Debatte darüber auf, ob die durch die Maut erhobenen Daten auch zur Verkehrsüberwachung heran gezogen werden dürfen. Der Gesetzgeber erhofft sich dadurch eine leichtere Kontrolle und Ahndung von Verstößen wie Geschwindigkeitsüberschreitungen und zulange Lenkzeiten. Auf den ersten Blick mag dies wie eine sehr wirkungsvolle Maßnahme zur Unfallvorbeugung erscheinen. Bei näherer Betrachtung ist die ein weiterer Schritt zu einer technokratischen Gesellschaft, die lediglich zwischen schwarz und weiß unterscheidet und jeglichen Individualismus unterbindet. Ein Beamter, der vor Ort mit einer Situation konfrontiert wird, kann individuell das Geschehen beeinflussen. Eine Entscheidung nach Aktenlage, ist eine so starke Abstraktion das sie kaum den Menschen im Mittelpunkt haben kann, sondern eher dazu dient eine schnelle und kostengünstige Abwicklung zu ermöglichen. In vielen Bereichen unseres täglichen Lebens werden bereits Entscheidungen von Computersystemen getroffen, ohne das uns dies tatsächlich bewusst ist.

Um ein tieferes Verständnis der vorhandenen Probleme zu erhalten, begeben wir uns im nächsten Abschnitt auf die technische Ebene.

Das kleine Hacker Einmaleins

Betrachten wir als Beispiel den Service E-Mail, über den mittlerweile der größte Teil unserer Kommunikation läuft. Die gängigen Protokolle für E-Mail sind SMTP, POP3 und IMAP, die selbst keinen direkten Verschlüsselungsstandard verwenden und die gesamte Nachricht im Klartext übertragen. Ohne zusätzliche Maßnahmen ist es möglich, das eine dritte unbekannte Partei, durch Mitschneiden des Netzwerkverkehres die Nachricht liest. Die nachfolgende Abbildung zeigt die üblichen Stufen beim E-Mail-Versand.

Phasen der E-Mailkommunikation

Abbildung 1: Phasen der E-Mail Kommunikation

Im ersten Schritt verbindet sich ein Nutzer mit seinem E-Mail Provider über einen Client wie Thunderbird und Outlook oder er nutzt über den Browser das Webinterface des Providers. Um nun eine gesicherte Verbindung zwischen Sender und Empfänger zu etablieren nutzt der Provider die Transportschicht des TCP / IP Protokolls in dem er auf den Secure Socket Layer (SSL) zugreift. Diese Maßnahme verschlüsselt die Kommunikation zwischen Sender und Empfänger, so dass die im Netzwerk übertragenen TCP Pakete nicht mehr im Klartext zu lesen sind. Das ist auch der klassische Weg, wie in öffentlich genutzten Netzwerken Login Daten vor dem Ausspähen mittels TCP Sniffing geschützt werden. Eine SSL Verbindung erkennt man in der Adresszeile des Browsers durch das https Protokoll. Sobald der Provider die E-Mail zu Weiterleitung erhalten hat, ist er in der Lage den Inhalt auszulesen. Auf dem Server des Providers durchläuft die E-Mail nun verschiedene Filter, die sicherstellen sollen dass es sich weder um Spam noch um Maleware handelt, was im Übergang von Schritt 1 auf 2 dargestellt ist. Erst bei erfolgreichem Passieren der Filter wird Schritt 3 ausgeführt und die E-Mail zum Empfänger Provider weiter geleitet. Auch bei diesem Transfer erfolgt die Übertragung über eine SSL Verbindung. Der Provider des Empfängers filtert die E-Mail ebenfalls und stellt diese dann endgültig zu. Während des gesamten Übertragungsprozesses sind auch Server Systeme beteiligt, die nicht direkt mit dem verwendeten Dienst in Verbindung stehen und einzig als Relay-Station die E-Mail zum nächsten Knoten weiter transportieren.

Inländische Internet Service Provider (ISP) sind von der Europäischen Union per Gesetz dazu verpflichtet für mindestens 6 Monate die IP Adresse des Senders und des Empfängers als auch Datum und Uhrzeit der Kommunikation zu speichern. Damit wurde die EU Richtlinie 2006/24/EG2 in geltendes nationales Recht umgesetzt. Anhand der gesammelten Informationen besteht nun die Möglichkeit ein umfassendes Bewegungsprofil und Kontaktprofil der betroffenen Personen zu generieren, denn die gespeicherten IP Adressen beinhalten automatisch die Standortinformation der Kommunikationspartner. Auch wenn die Nachricht selbst verschlüsselt Kist, besteht die Möglichkeit die META-Informationen des Senders und Empfängers auszulesen. Eine mögliche Option zumindest den Sender gegen neugierige Augen zu verschleiern wäre es, diesen im verschlüsselten Bereich der Nachricht zu übertragen. Eine solche Maßnahme hat allerdings nur geringe, beziehungsweise keine Wirkung, da durch die Natur von TCP /IP der Sender bekannt sein muss um eine Verbindung etablieren zu können. Zudem müsste die gesamte Dechiffrierung und anschließende Scans gegen Maleware direkt beim Empfänger vorgenommen werden. Das bedeutet für die Einzelnen Nutzer wiederum eine enorme Verantwortung die eigenen Systeme aktuell zu halten.

E-Mail Header

Abbildung 2: E-Mail Header

Für den Fall das die Nachricht direkt über das Web Interface des Providers gelesen wird bieten moderne Kryptographie Verfahren geringe Sicherheit, da man auf die Rechtschaffenheit des Providers angewiesen ist. Es besteht hier die Gefahr, dass im Code des Providers Schadcode versteckt ist, der die dechiffrierte Nachricht im Browser auslesen kann und an beliebige dritte Personen weiterleitet. Das Verstecken von Exploits auf viel besuchten Onlinediensten gehört zu den Grundfähigkeiten geübter Hacker.

Besonders Problematisch ist die Verwendung von Smart Devices wie Tablets und Telefonen im lokalen Netzwerk. Oft begreifen Endanwender das Risiko, welches von dieser Geräteklasse ausgeht nicht und haben kaum einen sicheren Schutz gegen unbefugte Benutzung eingerichtet. So sucht man auf diesen Geräten Virenscanner oder Firewalls, wie sie auf herkömmlichen Desktop Computern üblich sind vergebens. Das Risiko wird durch die Verwendung gerooteter Geräte maßgeblich verschärft, da durch diese Modifikation der Sicherheitsmechanismus des Betriebssystems ausgehebelt wird. Dieses Verhalten ist meist der Tatsache geschuldet, dass Benutzerfreundlichkeit und Sicherheit konträre Anforderungen sind. Die Gefahr besteht nun darin, das durch den mangelnden Schutz der Smart Devices, diese sehr leicht korrumpiert werden können, um dann im Netzwerk bei der Datensynchronisation weitere Geräte zu infizieren.

Abbildung 3: Typen der Cloud.

Abbildung 3: Typen der Cloud.

Ein minimaler Schutz gegen diese Bedrohung ist das Erkennen ob die im Netzwerk miteinander kommunizierenden Geräte bereits Modifizierungen wie ROOTEN aufweisen. Die Verweigerung Geräte im Netzwerk zu etablieren, die in der eigenen Umgebung mit einem besonders privilegierten Benutzer operieren, sollte gerade für Unternehmen die Minimalanforderung der internen Sicherheitsregeln sein. Aus diesem Grund ist es unerheblich ob es sich um eine private, public, community oder hybrid Cloud handelt, wie sie in Abbildung 3 dargestellt sind. Wegen der starken Synchronisierung zwischen den verschiedensten Geräten ist auch in geschlossenen Clouds ein hoher Sicherheitsstandard erforderlich. Gerade kabellose Funktechniken wie Wireless LAN (W-LAN) oder Powernet, dem Internet aus der Steckdose, ermöglichen bei unzureichender Konfiguration einiges an Angriffsmöglichkeiten. Als beispielsweise W-LAN etabliert wurde, waren viele Netzwerke nicht geschützt so das zu dieser Zeit das sogenannte War Driving eine populäre Freizeitbeschäftigung für technikbegeisterte Jugendliche darstellte. Auch heute existieren noch immer viele Netzwerke, bei denen man ohne eine erzwungene Authentifizierung bereits für den vollen Zugriff autorisiert ist. In den letzten Jahren haben die Betreiber auf diese Problematik reagiert und liefern an ihre Kunden sichere vorkonfigurierte Geräte aus.

Gründe für die Cloud

Cloud computing is a new way of delivering computing resources, not a new technology.”

Cloud Dienste lassen sich in die Bereiche Infrastructure as a Service (IaaS), Platform as a Service (PaaS) und Software as a Service (SaaS) unterteilen. Die folgende Grafik zeigt dabei welche Sektionen durch den Anbieter beziehungsweise durch den Nutzer verwaltet werden. Für Sicherheitsbetrachtungen ist besonders SaaS von großem Interesse, da hier der Nutzer am wenigsten Einfluss nehmen kann.

Abbildung 4: Varianten der Cloud-Schichten

Abbildung 4: Varianten der Cloud-Schichten

Das komplexe Anwendungen keine reinen Desktop Lösungen mehr sein müssen, sondern durchaus als Thin Client im Browser umgesetzt werden können, hat Google mit seinem kostenfreien Dienst Docs unlängst bewiesen. So lässt es sich leicht vorstellen, dass Softwarehersteller Ihre Produkte in Zukunft vermehrt als Cloud Solution vertreiben. Diese Vision lässt sich hervorragend mit den Cloud Ebenen IaaS, PaaS sowie SaaS weiterentwickeln, wobei der Fokus vermehrt auf Software as a Service (SaaS) liegt. SaaS bietet für die verschiedensten Szenarien wie Business to Business (B2B) und Business to Customer (B2C) hervorragende Ansätze. So ist es ist denkbar, dass beispielsweise ein bekannter Hersteller für Bildbearbeitung sein Portfolio hin zur Cloud migriert. Damit eröffnen sich diesem Unternehmen verschiedenste Perspektiven ihre Dienste erfolgreich zu vermarkten.

Privatanwender, welche die enormen Anschaffungskosten für die Desktop Installation nicht aufbringen können, erhalten einen legalen Zugang über ein Pay per Use Lizenzmodell. Selbständige, Professionelle Nutzer und Unternehmen buchen die benötigten Dienste als Flatrate für verschiedene Zeiträume. Zudem besteht noch die Option eine Auswahl an Web-Services oder ganzen Prozessketten anderen Unternehmen bereitzustellen, die diese Dienste in Ihren eigenen Applikationen verwenden. Somit könnte beispielsweise eine Onlinedruckerei auf ihrem Onlineportal ihren Kunden einen zugekauften Service zur digitalen Bildbearbeitung anbieten, um damit das eigene Produkt aufwerten, ohne das eigentliche Kerngeschäft durch Nebenkriegsschauplätze zu vernachlässigen.

Neben diesen offensichtlichen Vorteilen ist besonders die nichtfunktionale Anforderung Sicherheit ein markanter Punkt, der gesonderte Aufmerksamkeit verlangt. Die nachfolgenden Abschnitte beleuchten die notwendigen Aspekte zu Risiken, Erkennung und Analyse detaillierter.

Angriffsvektoren für Web Applikationen

Es gibt verschieden Gründe, weswegen Attacken auf Webdienste ausgeführt werden. Der einfachste und harmloseste Grund ist der Spieltrieb des Angreifers. Ein anderer Zugang ist der Wunsch einen Service kostenlos konsumieren zu können, wie es Captain Crunch alias J. T. Draper mit einer Spielzeugpfeife geschafft hat bei AT & T kostenlos zu telefonieren. Steve Woizniak, Erfinder des Apple, entwickelte darauf hin die Little Blue Box. Mit diesem Frequenzgenerator konnten gezielt Töne erzeugt werden, mit denen sich Telefone manipulieren ließen. Diese Technologie wurde später zum fernsteuern von Anrufbeantwortern verwendet. In den 1990‘ern erreichte Kevin Mitnick über Social Engineering, Zugriff auf verschiedenste IT-Systeme, was ihm eine mehrjährige Freiheitsstrafe einbrachte.

Auf 125 Seiten klärt die European Network and Information Securitry Agency (enisa) in ihrem Paper aus dem Jahre 2009 über Nutzen, Risiken und Empfehlungen zu Datensicherheit im Cloud Computig3 auf. Auf der Webseite der enisa stehen alle bisherigen Veröffentlichungen unter dem Punkt Publications zur Verfügung. Mittlerweile ist die Liste der von der enisa herausgebrachten Publikationen auf eine beachtliche Länge herangewachsen. Eine sehr kompakte Übersicht dieser Thematik zeichnet das Paper von Gail-Joon Ahn et al „Security and Privacy Challenges in Cloud Computing Environments“ 4 aus dem Jahre 2010.

Problematisch sind Angriffe, die darauf abzielen ein einen Service einzudringen und diesen dann beispielsweise für Massen E-Mails zu missbrauchen oder Schadcode zu verbreiten. Manchmal dienen solche Angriffe auch dazu Kundendaten abzugreifen, die dann gern beispielsweise für Kreditkartenmissbrauch verwendet werden. Eine sehr einfach auszuführende Klasse von Angriffen sind Denail of Service (DoS), mit denen bewirkt wird, dass ein Dienst nicht erreichbar ist. Dieses Vorgehen ist besonders für Unternehmen interessant, die ihre Marktposition ausbauen wollen, in dem sie die Angebote des Konkurrenten für die Zielgruppe unerreichbar machen.

Das größte Gefährdungspotenzial ist beim Datenschutz angesiedelt und beinhaltet die Themen Wirtschafts – beziehungsweise Industrie- Spionage. Unternehmen in Deutschland haben die Möglichkeit beim Bundesamt für Sicherheit in der Informationstechnik, Abteilung 2 über Maßnahmen der Spionageabwehr beraten und bei der Umsetzung unterstützt zu werden. Die Problematik über die Wahrung von Firmengeheimnissen ist vielen Unternehmen kaum bewusst. Am Beispiel der Firma Enercon zeigt sich jedoch schnell, welche enormen finanziellen Schäden verursacht werden können. Es kommt allerdings sehr selten vor, das Fälle von Wirtschaftsspionage publik werden, da oft die betroffenen Unternehmen ungern zugeben dass sie Lücken im Sicherheitsmodell hatten. Im Fall Enercon wurde auf Jahre hinaus der Zugang zum amerikanischen Markt versagt und eine Klage wegen Patentrechtsverletzung konnte im letzten Moment abgewehrt werden.

Security Testing

Dieser Abschnitt beschreibt wie Faktoren erkannt werden können, die sich problematisch auf die Sicherheit von Web Anwendungen auswirken. Es wird nicht geklärt, weswegen bestimmte Umstände sich als Risikoreich erweisen können, dazu sei auf die umfangreich verfügbare Literatur verwiesen. Eine wichtige Tatsache ist die Unterscheidung zwischen Server Sicherheit und Applikationssicherheit. Gerade die Serverkonfigurationen zwischen Entwicklungsmaschinen und Live Systemen unterscheiden sich erheblich. Teilweise können Programme nicht ausgeführt werden weil Funktionen deaktiviert wurden. Aus diesem Grund ist eine optimale Testinstanz für ein Projekt ein exakter Clone des Live Systems. Was allerdings wegen mangelnder Lizenzen selten umgesetzt werden kann.

Ein kleines Beispiel zur Serverkonfiguration ist die Option register_globals der Scriptsprache PHP. Durch das Deaktivieren dieser Option können Variablen nicht mehr einfach per URL an das Script weiter gereicht werden. Dadurch wird der Entwickler gezwungen Formularparameter über $_GLOBALS, $_GET oder $_POST auszuwerten. Als Provider mit der Migration von PHP 4 auf PHP 5 abgeschlossen hatten, konnten wegen der geänderten Konfiguration über Nacht viele Webseiten nicht mehr korrekt ausgeführt werden konnten.

Mit der richtigen Netzwerkkarte und dem Aircrack Framework ist es möglich den WPA und WEP Schlüssel von W-Lan Netzwerken zu brechen. Diese Attacke ist besonders verheerend, wenn der Angreifer den Datenverkehr im Netzwerk mitschneidet. Allein diese Möglichkeit zeigt auch sehr eindrucksvoll, dass die Services einer private Cloud ebenso gut gesichert sein sollten wie in public Clouds.

Auch der Erfolg von DoS Angriffen ist abhängig von der Serverkonfiguration, mit network intrusion prevention and detection Systemen wie SNORT können viele Angriffe abgewehrt werden. Um sicherzustellen, dass Dienste eine Mindestmenge an Anfragen bewältigen können, werden diese mittels Penetration Tests bewertet. Mit den gewonnenen Erkenntnissen kann eine Aussage getroffen werden, ob die verfügbaren Ressourcen ausreichend sind. Mit Backtrack Linux existiert eine Distribution, die bereits eine Vielzahl an nützlichen Werkzeugen zusammen gestellt hat um Penetration Testing zu betreiben. Im Gegensatz zu einem Vulnerability-Scanner benötigt ein Penetration Test viele manuelle Anpassungen an den zu testenden Prüfling. Ein Vulnerability Scan läuft weitgehend automatisch. Ein bekannter und frei verfügbarer Scanner ist OpenVAS, welcher aus dem Nessus Projekt hervorgegangen ist.

Ausgabe des HTTP Requests durch FireBug

Abbildung 5: Ausgabe des HTTP Requests durch FireBug

Eine wichtige Voraussetzung zum Testen von Online Services ist der sichere Umgang mit einem Crawler. Dieses nützliche Werkzeug folgt den internen Links einer Domäne und wertet den HTTP Request aus. Dabei werden Informationen über Session und Cookie Variablen gesammelt und Formulare geparst. Gerade Sessions, die den Status eines Clients über mehrere Requests serverseitig aufrecht erhalten können, erlauben mit relativ überschaubarem Aufwand bestehende Accounts zu übernehmen. Leicht zu erratende Session ID’s gestatten einem Angreifer unter Umständen sogenanntes Session Riding oder Session Hijacking.

Mit einem Proxy wie WebScrab oder WireShark können Parametermanipulationen auf bequeme Weise durchgeführt werden. Das Open Web Application Security Projekt (OWSAP) stellt sowohl eine umfangreiche Ansammlung an Werkzeugen als auch Informationen zur Verfügung.

Qualitätsbewertung des Sicherheitsmodells

Eine optimale Bewertung über Sicherheit ergibt sich aus einer Mischung von White Box und Black Box Testing – dem sogenannten Grey Box Testing, das vielmals für Penetration Test herangezogen wird. Bereits eine einfache Checkliste erlaubt eine Qualifizierte Aussage über die Güteklasse des Sicherheitsmodelles. Wichtige Punkte sind dabei:

  • SSL Verbindung innerhalb der gesamten Domäne, dies verhindert das Auslesen kritischer Informationen aus TCP Paketen.
  • Passwörter werden nicht im Klartext gespeichert und durch Salt und Pepper verschleiert, dies verhindert Rainbow Table Attacken.
  • Keine versteckten Formularfelder um Informationen weiter zu reichen
  • Keine vertraulichen Daten in Cookies speichern
  • Cookies haben die gleiche Lebenszeit wie Sessions
  • Benutzereingaben werden über den Server validiert
  • generierte Session ID’s müssen schwer vorhersagbar sein
  • Sessions haben einen Timeout, üblicherweise 2 Stunden bei kritischen Anwendungen wie Onlinebanking deutlich kürzer
  • Session ID’s gehören nicht als Parameter in die URI, sondern werden in Cookies gespeichert

Ein weiterer Schritt besteht im Erzeugen der Graphen, deren Knoten die erreichbaren URL’s einer Domäne für alle Benutzerrollen sind. Solche Graphen können mit einfachen Webcrawlern beziehungsweise Agenten generiert werden. Diese Knoten werden mit Zusatzinformationen angereichert, die zur weiteren Analyse heranzuziehen sind. Knoten, die Formulare enthalten sind von besonderem Interesse. Dabei sind auf zwei Dinge zu achten. Die Variablen, beziehungsweise Formular Parameter müssen validiert werden und die Übertragung hat per SSL zu erfolgen. Daraus ergibt sich ein Modell, mit dem bestimmt werden kann welche Inhalte eine Benutzergruppe aufrufen kann. Enterprise Applikationen, welche RESTful Services unterstützen können über diese Methodik besonders gut getestet werden.

Resümee

Grundsätzlich existieren für Cloud Lösungen sehr ausgereifte Sicherheitsstandards, sofern diese auch durch die Entwickler mit berücksichtigt werden. Problematisch ist der Umgang mit den Daten der Kunden eines Cloud Providers. Unabhängige Prüfinstitute könnten diese Bedenken über Datenschutz durch Zertifizierungen ausräumen, dazu wäre eine transparente Vorgehensweise notwendig. Selbst wenn Provider nur die Besten Absichten hegen, besteht die Gefahr von nationalen Regierungen gezwungen zu werden Zugang zu Kundendaten zu gewähren. Das Risiko der Industriespionage ist ein erhebliches Argument gegen die Cloud. Auch wenn Amerika durch Edward Snowden gerade in den Mittelpunkt des öffentlichen Interesses gerückt ist, kann man davon ausgehen, dass auch andere Staaten Technologien besitzen die jenen der NSA ebenbürtig sind. In Europa haben die Aktivitäten der amerikanischen Regierung eine sehr hohe Gewichtung, da viele Unternehmen amerikanische Softwareprodukte verwenden. Die angeführten Gründe sind für viele europäische Unternehmen die Argumentation beispielsweise ein Buchhaltungsprogramm nicht als Cloud Service einzukaufen, sondern eine eigens gehostet Lösung zu bevorzugen. Auch wenn auf den ersten Blick viele Argumente eher pessimistisch klingen mögen, wird sich auch zukünftig die Cloud weiter im Unternehmenseinsatz durchsetzen. Die damit verbundene Flexibilität und wirtschaftlichen Vorteile überwiegen. Die Problematik des Datenschutzes kann durch bereits vorhandene und etablierte Standards gelöst werden, die in aller Wahrscheinlichkeit durch unabhängige Prüfinstitutionen kontrolliert werden. Es kann stark davon ausgegangen werden, dass sich in den nächsten Jahren ein neues Zertifikat für Datenschutz etablieren wird. Die Qualität eines solchen Siegels lässt sich schnell anhand der Transparenz zum getroffenen Urteil bewerten. So wird sich in der Zukunft zeigen ob eine solche Institution eine ähnliche Effizienz wie ein no spy Abkommen erreichen kann. Nicht umsonst ist Datensparsamkeit und Datenvermeidung ein Thema, dessen sich sogar Martin Fowler angenommen hat. Auch Josef Weizenbaum, ein wichtiger Gesellschaftkritiker der in diesem Zusammenhang nicht unerwähnt bleiben darf, mahnt in vielen seiner Bücher3 zum sorgsamen Umgang mit der Information Technologie.

Resources

[1] Forschungsfelder zur Verarbeitung großer Datenmengen: BigData, Information Search and Retrival.
[2] RICHTLINIE 2006/24/EG DES EUROPÄISCHEN PARLAMENTS UND DES RATES vom 15. März 2006
[3] D. Catteddu and G. Hogben, “Cloud Computing: Benefits, Risks and Recommendations for Information Security,” ENISA, 2009; www.enisa.europa.eu/act/rm/ files/deliverables/cloud-computing-risk-assessment/ at_download/fullReport
[4] Takabi, H.; Joshi, J.B.D.; Gail-Joon Ahn, “Security and Privacy Challenges in Cloud Computing Environments,” Security & Privacy, IEEE , vol.8, no.6, pp.24,31, Nov.-Dec. 2010