1
 
 
Account
In your account you can view the status of your application, save incomplete applications and view current news and events
August 15, 2016

Full Disclosure: Securing Internal Communication

What is the article about?

Current e-commerce systems often consist of several servers that communicate with users' browsers via SSL-secured lines. However, the customers' computers are not the only ones with which a store has to exchange data: it is almost always necessary for the system's individual servers to communicate with each other as well. It doesn't matter whether it's a microservice or a vertical architecture; even the aging monoliths are happy to use multiple servers in a multi-tier approach.

What these backend requests have in common (in contrast to the network traffic between the customer's browser and otto.de) is that generally no confidential data goes over the line here, but rather, for example, the update of a product availability or configuration requests. This means that these contents do not have to be kept secret, which saves us the effort and complexity of encrypting them.

Nevertheless, of course not everyone is allowed to change the texts in the store or adjust product information, so we need a method for authenticating and authorizing the requests.

Authentication & Authorization


These terms are often used colloquially as synonyms, but to technicians they conceptually mean different things:

Authentication means a process of establishing the authenticity or identity of something. In this context, a request should be able to verify that it was initiated by a known user or computer.

Authorization, which is generally the next step, checks whether the user or computer has the necessary authorization to perform the desired action.

HMAC


The HMAC method is standardized in various RFCs. Like all MACs, it is based on signing a message with a pre-shared key (PSK) - in this case, a hash function is used. In the variant used at OTTO, the signature is added to the original HTTP request as a signature header, and the request can then either be allowed or rejected by the server.

Client


To sign the request, the client must combine the username and PSK with the current timestamp and the actual message (i.e. HTTP-Verb, URI, Body etc.) in the correct way. The signature is added to the request as a header, as is the timestamp. Since the client and server share the PSK, it is of course not transmitted as well.

The diagram shows which information is used for the signature. As an example, a GET request to the resourcegetLastOrderforthe userexample.usewassigned with the secretPSKonJune 22, 2016, around half past midnight Central European time.

disclosure01
disclosure01

Server


With the transmitted user name, the server can determine the associated PSK from its user database. It is used to generate the same signature again with the remaining information from the request (timestamp, HTTP verb, etc.) and compare it with the one transmitted in the header. If the two signatures differ, it is because the information used for signing differs. So either the PSK is wrong or it is a replay attack where parts of the original request have been changed. Obviously, in both cases the request can be rejected.

Otherwise, the system now checks whether the verified user also has the authorization to perform the requested action. If the user is not authorized to do so, the request can also be denied, otherwise the action is performed.

Use


A Java implementation of this method for JVM-based programs can be found at https://github.com/otto-de/hmac-auth.

To generate correct headers, theHMACJerseyClientortheHmacJersey2ClientRequestFiltercanbe used, which get the user name and the PSK when instantiated. The current time of the system is used as timestamp and the further information comes from the request, which the client generates anyway. The headers are then appended to each request without any further action.

final WebTarget webTarget = jerseyRestClientFactory.getClient().target(url); webTarget.register(new HmacJersey2ClientRequestFilter(userName, preSharedKey)); 

To check headers in application containers on the server side, the other component of the library can be used: theAuthenticationFilterchecks each incoming request for the HMAC headers and performs authentication if they are present. The filter is simply made known to the application container.

@Bean public AuthenticationFilter authenticationFilter() { return new AuthenticationFilter(new AuthenticationService(new PropertyUserRepository())); } 

Incoming requests are now rejected with HTTP status 401 if the HMAC headers are present but the signature is wrong or the timestamp is too old.

In applications that use Spring, the@AllowedForRolesAnnotationis available for authorization, which can be attached to service methods, for example. An aspect interrupts the call of the method and checks whether the user specified in the request has one of the roles that are in the annotation before the method is executed. If not, anAuthorizationExceptionis thrown, which must be handled by the library user, but generally also results in an HTTP status of 401:

@RequestMapping(value = {"/priceOfIPhone"}, method = RequestMethod.PUT) @AllowedForRoles(value = "iPhonePriceManager") @ResponseBody public void updateHealth(@RequestParam final Integer newIphonePrice) { final IPhoneData currentIPhoneData = allPhonesService.getIPhoneData(); newIPhoneData = currentIPhoneData.withPrice(newIphonePrice); allPhonesService.setIPhoneData(newIPhoneData); }

Implementation properties

Secure


Since the HMAC method does not specify which hash algorithms to use when signing the request, it is up to the concrete implementation to choose a secure function. We have chosen HmacSHA256 which was developed exactly for these purposes and is widely used and well tested as a state-of-the-art hash.

Other conceivable mechanisms, such as HTTP-Basic, are so vulnerable that their use without additional SSL encryption, which we want to avoid, is grossly negligent.

Stateless


The vast majority of our web servers are Docker containers, which should be able to constantly emerge and expire in their Mesos environment, while the machines are dynamically scaled according to load. So for two consecutive requests, it is by no means a given that they will end up on the same server. Also, for Blue-Green deployments, the machines must not share any state that changes.

Since our approach re-signs each request, there is no need to keep a session or the like in the server that would have to be laboriously synchronized between instances.

In the case of HTTP digest which would actually be a natural candidate for the problem, either the nonce must be synchronized for each user in all servers or the slow initial handshake must be re-executed for each request.

Resilient to replay attacks


These attacks attempt to re-send previously intercepted requests. This is promising because the intercepted request was probably successful and the attacker can hope that the resent request will also be successful. Although these attacks are still theoretically possible, they are considerably more difficult due to the time limit - requests have a built-in expiration time.

If attackers succeed in performing the replay within the short validity period, the attack possibilities for individual requests are quite limited: a POST is pointless (create a new resource again), a PUT will re-perform the performed change to the resource and thus cement it, a DELETE will delete the already deleted resource again, and a GET will reveal the non-confidential data. Only in the case of directly consecutive requests is it conceivable that the attacker has enough time after a request to resubmit the previous one and thus, for example, restore recently deleted resources or roll back a configuration to an earlier point in time. However, our business processes are very tolerant of human error in this regard anyway, so at best an attacker could hope to cause a bit of confusion.

A variation of this is to modify the original request. For example, an attacker might try to change a GET to a DELETE request in order to delete a previously requested resource. However, since both the HTTP verb and the request body are part of the signature, manipulating them will only cause the signature to no longer match the request and it will be rejected.

Only the uninvolved request headers remain for attackers to manipulate, and there is usually no interesting data there.

Performance


The method is extremely fast, especially compared to methods with integrated encryption. If confidential data is to be transmitted, the combination of HMAC with a transport encryption like SSL is still considerably faster than methods in the application layer like oAuth2 or the various wsSecurity variants.

Simple ...


Security is hard but we still want no bugs in this area if possible.

Especially in our multilingual environment, where teams may have to implement their own implementations of the standard, it is of great advantage to use a method that can be described comprehensively in a few words and that can be implemented especially without advanced mathematical methods.

... but only for machines


Due to the cumbersome calculation of headers and the time limit on request validity, it is very difficult to generate correct requests manually. To be able to send test requests to HMAC-secured interfaces anyway, there is a proxy in the Github project that can be started locally and that accepts requests, generates the necessary headers and forwards the correct request. However, the usage remains quite bulky and is not suitable for technically inexperienced users.

4Comments

  • 18.08.2016 11:23 Clock

    >Gegen oAuth spricht für uns hauptsächlich die Session, die standardmäßig als lokaler State im Speicher der Server liegt.

    Ich bin im Zuge von oAuth noch nicht mit Sessions in Berührung gekommen. Requests auf den Resource Server sind auch bei oAuth stateless und enthalten lediglich einen Token. Requests müssen allerdings per HTTPS transportiert werden, da die Token geheim sind. Neben den JWT Tokens kann man auch einfache Bearer Tokens verwenden. Bearer Tokens sind einfach nur zufällige Zeichenketten. Ein Bearer Token muss dann allerdings vom Resource Server am Authorization Server verifiziert werden [RFC7662].

    >Aber wenn ich https://jwt.io/introduction/ richtig lese, benutzt auch dieses Protokoll HMAC-Signaturen, wenn die Nachrichten nicht verschlüsselt werden müssen.

    Ein JWT Token ist vom Authorization Server signiert. Für diese Signatur kann man symmetrische oder asymmetrische Verfahren einsetzen. Der Vorteil bei asymmetrischen Verfahren ist, dass die Resource Server, welche das Token verifizieren müssen nicht über einen geheimen Schlüssel verfügen müssen. Man kann JWT's auch zusätzlich verschlüsseln, was in meinen Augen allerdings kaum notwendig ist, da das Token in jedem Fall über HTTPS transportiert werden muss.

    Die Idee von JWT's ist, dass diese Claims beinhalten, welche vom Authorization Server signiert sind und besagen, dass der Inhaber dieses Tokens bestimmte Rechte hat. Dadurch muss ein Resource Server keinerlei Benutzerdatenbank führen. Der Resource Server vertraut einfach dem Authorization Server und handelt entsprechend. Der Vorteil ist, dass man nur einen Punkt (den Authorization Server) hat, der Benutzer und Rechte kennen muss.

    Bei OAuth hat man allerdings immer den Aufwand sich ein Token vom Authorization Server holen zu müssen. Diese Token ist dann üblicherweise auch nur eine begrenzte Zeit gültig, so dass man diese Tokens auch noch refreshen muss. Es gibt also auch hier nichts umsonst.

  • Alexander Kiel
    16.08.2016 13:19 Clock

    Danke für den fundierten Artikel. Ich habe zwei Anmerkungen: Wie stellt Ihr sicher, dass der PSK geheim bleibt und wie bewertet Ihr die Performance Eures Ansatzes, der ein Zugriff auf ein Userrepository benötigt, im Vergleich zu Ansätzen wie oAuth + JWT, die dies nicht benötigen? Bzgl. meines ersten Punktes, würde mich speziell interessieren, wie Ihr PSK's verteilt und im Falle eines Exploids invalidiert.

  • TomVollerthun
    17.08.2016 14:58 Clock

    Das "Userrepository" ist bei uns üblicherweise lediglich eine HashMap, in der die höchstens 10-15 PSKs der anderen Systeme liegen, mit denen zur Laufzeit kommuniziert werden muss. Da die Daten also im lokalen Speicher liegen, gibt es keinen nennenswerten Performance-Einfluss. Es handelt sich halt nicht um öffentliche APIs, zu denen Endbenutzer Zugriff benötigen würden.

    Gegen oAuth spricht für uns hauptsächlich die Session, die standardmäßig als lokaler State im Speicher der Server liegt. Um das zu vermeiden, müsste sie in der Datenbank gehalten werden, was die Authentifizierung erheblich komplexer (und langsamer) macht. Alternativ könnte man versuchen, bei jedem Zugriff eine neue Session zu beginnen, aber der Handshake ist vermutlich nochmal um einige Größenordnungen langsamer und ehrlich gesagt haben wir das gar nicht erst versucht.

    JWT war vor rund vier Jahren, als wir uns mit dem Thema beschäftigen mussten, noch nicht einsatzfähig - ich glaube, ich habe letztes Jahr überhaupt zum ersten Mal davon gehört. Aber wenn ich https://jwt.io/introduction/ richtig lese, benutzt auch dieses Protokoll HMAC-Signaturen, wenn die Nachrichten nicht verschlüsselt werden müssen.
    Allerdings habe ich keine tiefergehende Analyse oder gar praktische Erfahrungen dazu - vielleicht möchte mich also jemand in dieser Hinsicht korrigieren?


    Unsere PSKs werden wie alle anderen Geheimnisse in <a href="https://www.vaultproject.io/" rel="nofollow">Vault</a> gehalten und beim Hochfahren der Applikation in den Speicher geladen.
    Da die Schnittstellen nur für interne Kommunikation gedacht sind, benutzen wir häufig Jabber mit <a href="https://de.wikipedia.org/wiki/Off-the-Record_Messaging" rel="nofollow">OTR</a>-Verschlüsselung um die Keys zum Nachbarteam zu transportieren, aber nur weil sie zu lang für einen Zettel sind - wir versuchen nicht allzu paranoid zu werden :)
    Das ist im wesentlich auch die aktuelle Strategie, um Geheimnisse zu ändern. Zwar beginnnen wir uns Gedanken zu automatischen regelmäßigen PSK-Änderungen zu machen, aber das ist noch nicht weit fortgeschritten.

  • TomVollerthun
    24.08.2016 09:18 Clock

    > ... und enthalten lediglich einen Token ...

    Mit "Session" meinte ich jede Art von veränderlichem Zustand, der serverseitig gehalten werden muss, also nicht zwangsläufig eine HTTP-Session - das ist zugegebenermaßen etwas unscharf formuliert.

    Ich habe zwar noch keinen produktiven Einsatz von oAuth begleitet, aber für mich hört sich das so an, als wäre der Token, den der Client vom Authorization Server bekommt, genau so ein mutable state, weil der Server immer wissen muss, ob und wann der Token abläuft oder ob er überhaupt jemals gültig war.
    Zwar kann diese Authorisierung in einen Authorization Server ausgelagert werden, aber den müssten wir ja auch wieder ausfallsicher clustern - zu einem "Authorization Cluster". Wenn ich das jetzt alles richtig verstanden habe hieße das doch, dass der Zustand des Tokens im "Authorization Cluster" synchronisiert werden muss und genau das wollen wir ja verhindern.

    Aber da du offenbar schon tiefergehende Erfahrung mit oAuth hast, kannst du mir vielleicht sagen, ob es bei oAuth auch eine zustandslose Variante gibt?


    > ... Requests müssen allerdings per HTTPS transportiert werden ...

    Das schließt oAuth für unseren Anwendungsfall leider ohnehin aus. Aber wenn eh HTTPS eingesetzt werden muss, würde ich vermutlich HTTP-Basic für die interne Kommunikation bevorzugen und den Authorization-Header direkt mitschicken.
    Auf mich wirkt es so, als seien öffentliche APIs ein passenderer Anwendungsfall für oAuth: SSL Transport, zentrale Verwaltung von Benutzern und Rollen, "Single Sign-On"-Fähigkeit über den zentralen Auth-Server ...


    Vielen Dank übrigens für die aufschlussreiche Erklärung von JWT im Zusammenhang mit oAuth - ich hatte bislang gedacht, dass das konkurrierende Konzepte sind :)

    Da Vault auch die Generierung von Passworten mit Ablaufzeitpunkt anbietet, könnten wir uns interessanterweise ja auch durchaus noch in die Richtung eines Authorization Servers bewegen: beim Überfliegen der Doku hatte ich den Eindruck, dass die Gültigkeitsdauer Teil des Tokens ist und damit stateless, was genau zu uns passen würde.
    Dann hätten wir allerdings auch den zusätzlichen Request den du in deinem letzten Absatz beschrieben hast und ich fürchte fast, dass das dann zu teuer würde.

Write a comment
Answer to: Reply directly to the topic

Written by

Tom Vollerthun
Tom Vollerthun
Hacker

Similar Articles

We want to improve out content with your feedback.

How interesting is this blogpost?

We have received your feedback.

Allow cookies?

OTTO and three partners need your consent (click on "OK") for individual data uses in order to store and/or retrieve information on your device (IP address, user ID, browser information).
Data is used for personalized ads and content, ad and content measurement, and to gain insights about target groups and product development. More information on consent can be found here at any time. You can refuse your consent at any time by clicking on the link "refuse cookies".

Data uses

OTTO works with partners who also process data retrieved from your end device (tracking data) for their own purposes (e.g. profiling) / for the purposes of third parties. Against this background, not only the collection of tracking data, but also its further processing by these providers requires consent. The tracking data will only be collected when you click on the "OK" button in the banner on otto.de. The partners are the following companies:
Google Ireland Limited, Meta Platforms Ireland Limited, LinkedIn Ireland Unlimited Company
For more information on the data processing by these partners, please see the privacy policy at otto.de/jobs. The information can also be accessed via a link in the banner.