LoRaWAN Workshop in Ulm

In Ulm entsteht momentan ein spannendes neues Projekt: Ein LoRaWAN in Kooperation mit The Things Network aus Amsterdam.

Das LoRaWAN – kurz für Long Range Wide Area Network – ist dem WLAN zwar ähnlich, hat jedoch gravierende Unterschiede: Eine Antenne kann Daten von seinen Sendern bis auf eine Distanz von 10 km empfangen. Dafür wird mit einer sehr geringen Bandbreite gesendet – optimal für kleine Sensoren und Geräte mit wenig Datenaustausch. Die Technologie steht jedem Ulmer und Besucher offen und soll somit zum Machen und Experimentieren einladen.

„Der Kreativität sind keine Grenzen gesetzt, wir sind gespannt auf die Umsetzungen der Ulmer: Ob Feinstaubmessgeräte an den Straßen, Standortsuche für das gestohlene Fahrrad oder Feuchtigkeitssensoren im Schrebergarten, die besten Ideen entstehen aus ganz persönlichen Problemen im Alltag!“, freut sich Andreas Buchenscheit, Initiator des LoRaWAN-Projekts und stellvertretender Vorsitzender der initiative.ulm.digital, sowie Geschäftsführer der Cortex Media GmbH.

Die initiative.ulm.digital e.V., von der wir als Cortex Media ein Teil sind, wird zusammen mit unserem Ulmer Hackerspace/Fablab Verschwörhaus am 9. und 10. Dezember Workshops ausrichten, bei denen die Macher des The Things Network Projekts zu Gast sein werden. Alle Interessenten sind eingeladen, direkt am Gerät auszuprobieren und sich in diversen Workshops tiefergehend mit der Materie auseinander zu setzen.

Bis dahin werden in der Ulmer Innenstadt sechs Gateways zur Verfügung stehen, um erste Experimente und Ideen bei den Workshops testen zu können. Weitere Gateways sind in den kommenden Monaten fest geplant.

Für das gesamte Projekt LoRaWAN in Ulm haben wir unter http://lora.ulm-digital.com eine Webseite mit weiterführenden Informationen für alle Interessieren bereitgestellt. Ein frei editierbares Wiki und ein Gruppenchat sollen die Kommunikation sowie das gemeinsame Erarbeiten von Ideen, Lösungen und Projekten erleichtern.

In Unsere Projekte veröffentlicht

jQuery Listener und AJAX

Ein klassisches Problem, wenn man HTML Elemente dynamisch durch die HTML-Response eines AJAX Calls ersetzt, ist, dass danach die auf diesen Elementen gebundenen Listener nicht mehr gebunden sind. Das liegt natürlich in der Natur der Sache und führt sehr häufig zu komplexem und schwer zu wartbaren Code, der die Listener im Success-Callback neu bindet.

Für dieses Problem gibt es eine einfache und elegante Lösung, die aber recht oft übersehen wird. Mit einem dritten Argument in der .on()-Function und dem initialen Selektor auf ‚document‘, wird der Listener auch weiterhin gebunden sein, wenn der entsprechende HTML darunter durch ein .html(‚…‘) ersetzt wird.

$(document).on('click', '.your-css-selector', function(event){
 // ... your code here.
}); 

Sehr einfach und auch sehr praktisch.

In Unsere Projekte veröffentlicht

OKHttp für Android

Ein Klassiker der Android Entwicklung, mit dem man immer wieder konfrontiert wird, sind elegante und gut gemachte HTTP Requests, vor allem seit diese ab Android 4 in der synchronen Variante mit Exceptions um sich werfen.

Lange haben wir intern eine selbst entwickelte, asynchrone HTTP Bibliothek verwaltet, die zuerst auf die Apache HttpGet/Post etc. Methoden gesetzt und später mit HTTPUrlConnection gearbeitet hat. Aber richtig zufrieden waren wir damit nie, vor allem da sich die Anforderungen an diese oft von Projekt zu Projekt geändert haben.

Für die letzten Projekte nutzen wir verstärkt OKHttp, eine HTTP Library von Square, die uns in fast allen Belangen überzeugt hat.

Ein Beispiel für einen asynchronen Requests ist schnell zusammengebaut:

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
    .url("http://api.cortex-media.de/data/foo/1337")
    .build();

client.newCall(request).enqueue(new Callback() {
  @Override
  public void onFailure(Request request, Throwable throwable) {
    throwable.printStackTrace();
  }

  @Override
  public void onResponse(Response response) throws IOException {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    String body = response.body().string();
    // do something with the body.
  }
});

Einfach den Client initialisieren, Request bauen und Call via enqueue() asynchron ausführen. Die Antwort wird dann im Callback behandelt, je nachdem ob der Request erfolgreich war (onResponse) oder nicht (onFailure).

Ein synchroner Request ist noch einfacher, man ersetze einfach die newCall()-Zeile durch folgende und verzichte auf den Callback:

Response response = client.newCall(request).execute();

Der Response ist dann direkt im entsprechenden Objekt gespeichert. Aufpassen muss man hier nur, dass man das nicht im UI Thread macht, sonst fängt man sich eine Exception ein.

Authentication

Will oder muss man seine HTTP Requests mit HTTP Authentication versehen, was wir gerne bei HTTPS-basierten APIs der schlichten Einfachheit wegen tun, setzt man einfach die entsprechenden Header mit den Ergebnis der Credentials-Klasse:

String credential = Credentials.basic("matthias", "extremely-secr3t");
Request request = new Request.Builder()
    .url("http://api.cortex-media.de/data/foo/1337")
    .header("Authorization", credential)
    .build();
 ...

Bisher sind wir wirklich sehr gut mit dieser Library gefahren. Solider Code, alle wichtigen Features dabei, SNI kompatibel (was gerade für uns sehr wichtig war) sowie aktive (Weiter-)Entwicklung war genau dass, was wir gesucht haben. Danke, Square!

In Technischer Hintergrund veröffentlicht | Getaggt: ,

Cortex Media Push-Server und die Frag-Mutti Apps

1402518737_bubbles

Für unsere Smartphone-Apps für Android und iOS benötigten wir schon relativ früh einen eigenen Push-Server, um diverse Nachrichten und Statusmessages direkt in die Apps pushen zu können. Die damals vorhandenen kommerziellen und Open-Source Angebote haben uns nicht überzeugt, so dass wir einen eigenen Push-Server entwickelt haben.

Der Push-Server

Wir haben uns für ein Konzept entschieden, das beide für uns wichtigen Push-Dienste vereint: Den Apple Push Notification Service (APNs) sowie sowohl das Google Cloud to Device Messaging Framework (C2DM) und als auch später den Google Cloud Messaging (GCM) Dienst. Der Push-Server empfängt über die entsprechenden APIs unserer Apps die Nachrichten und pusht, je nach Quelle, die Nachrichten weiter an die von Google und Apple angebotenen Schnittstellen. Hierbei kümmert sich der Server um ein logisches Buffering der Push-Messages, um beispielsweise bei einem Newsletter die Schnittstellen übermäßig zu beanspruchen. Um auch später für andere Pushes-Services bereit zu sein (vielleicht doch nochmal Windows Phone?), lässt sich der Server auch um Plugins erweitern, die andere Schnittstellen ansprechen können.

Anbindung der APIs

Für die Kommunikation mit dem Push-Server haben wir uns bewusst für eine direkte Socketverbindung zum Server entschieden. Dadurch erreichen wir vor allem bei sehr hohem Traffic oder der massenhaften Versendung von Pushes (kein Spam, dazu unten mehr) einen höheren Durchsatz als über eine HTTP-API oder ähnliches. Und da unsere APIs in der Regeln direkt mit dem Server sprechen können, und der Server auch nicht von externen Usern oder Diensten angesprochen wird, war dies die beste Alternative.

Der Server selbst verarbeitet die ankommenden Pushes über einen internen Buffer, der diesen nach und nach und je nach Schnittstelle abarbeitet. So kann die Anlieferung der Pushes von den APIs ohne Verzögerung geschehen und der Server kümmert sich asynchron um den Versand. Die Erfahrung zeigt, dass die Pushes oft schneller am Server ankommen wie die Service von Apple und Google diese entgegen nehmen, auch wenn diese Schnittstellen in der Regel sehr schnell reagieren. Falls ein Push nicht zugestellt werden kann, kann dieser auch wieder, je nach Response vom APNs oder GCM, zurück in den Buffer gelegt werden.

Die Frag-Mutti App

Die iOS und Android App für Frag-Mutti, die wir 2012 für Frag-Mutti.de entwickelt haben, nutzt unseren Push-Server zur Versendung diverser Nachrichten direkt an die Apps. Darunter z.B. praktische Tipps zu Pfingsten oder den Tipp der Woche.

Die Apps sind aktuell auf knapp 200.000 Geräten installiert (Stand Mitte 2014) und in etwa die Hälfte aller User haben die Push-Nachrichten aktiviert. Wenn Frag-Mutti Pushes verschickt, geschieht dies in der Regel gesammelt innerhalb eines kurzen Zeitfensters. Genau hier hat der oben beschrieben Buffer seine Stärken. Wenn Frag-Mutti mehrere 10.000 Pushes auf einmal anliefert, legt der Push-Server diese in den internen Buffer und arbeitet einen nach dem anderen ab. So kann der Client seine Daten in kurzer Zeit versenden, ohne weiter auf den Server und die dahinter liegenden Apple- und Google APIs zu achten.

In Technischer Hintergrund, Unsere Projekte veröffentlicht

Asynchrone Codeexecution beim App-Update

Hintergrund

Für ein Kundenprojekt benötigten wir eine Möglichkeit, direkt nach einem Update der Android App via Google Play, Code innerhalb der App auszuführen, ohne dass zuvor die App vom User manuell gestartet wird.

Genauer ging es um die Notwendigkeit, einen GCM Subscribe Call an die Google GCM API zu schicken. Und das direkt nach dem Update der App, da wir nicht sicher gehen konnten, dass die User die App direkt oder auch in näherer Zukunft öffnen werden. Trotzdem sollte die App sich auf GCM Pushes registrieren.

Für Probleme diese Art sendet Android nach dem Update einer App einen Broadcast mit den entsprechenden Infos darüber, welches App upgedated wurde. Wir können auf diese Events hören und entsprechend reagieren.

Auf den Broadcast hören

Im AndroidManifest.xml definieren wir einen <receiver>, der auf die android.intent.action.PACKAGE_REPLACED Action horcht:

<receiver android:name="de.cortex_media.gcm.GCMBootstrap">
  <intent-filter>
    <action android:name="android.intent.action.PACKAGE_REPLACED" />
    <data android:scheme="package" android:path="de.cortex_media.gcm" />
  </intent-filter>
</receiver>

Hiermit können wir alle Events dieser Art im Broadcast Receiver GCMBootstrap weiter verarbeiten.

Broadcast Receiver

Da alle Update-Events des Systems nun hier landen, müssen wir auf den entsprechenden Packagename filtern, den wir an dieser Stelle beobachten wollen:

public class GCMBootstrap extends WakefulBroadcastReceiver {
  @Override  
  public void onReceive(Context context, Intent intent) {

    if (intent != null) {
      String data = intent.getDataString();
      String action = intent.getAction();

        if (data.contains("de.cortex_media.gcm")) {    		
          // Your code ...
          // We are doing the GCM Subscribe here.
        } 
    } 
  }
}

Damit die App bei einem entsprechenden Broadcast auch zuverlässig aufwacht und den obigen Code ausführt, bietet es sich an, einen WakefulBroadcastReceiver zu verwenden. Hier ist nicht viel mehr zu tun, als von der entsprechenden Klasse zu erben (siehe oben) und die passende Permission zu verwenden:

<uses-permission android:name="android.permission.WAKE_LOCK" />

Der Code, den wir an einer solchen Stelle ausführen, sollte im Allgemeinen nicht zuviel Arbeit leisten. Andere Apps laufen eventuell parallel, das System selbst ist auch selten ohne Arbeit und wenn die eigene App dann verdeckt viel CPU Leistung zieht, sind die User selten begeistert.
Eventuell bietet sich an dieser Stelle auch ein eigener Service an, was aber eher ein Thema für den nächsten Blogeintrag wäre.

In Technischer Hintergrund veröffentlicht | Getaggt:

Android Event Bus for fun and profit

Was ist ein Event Bus und warum ist er so praktisch?

Eine typische Android-App besteht in der Regel aus einer nicht kleinen Menge an Activities, Fragments und Services. Diese Komponenten der App müssen in vielen Fällen miteinander kommunizieren, um sich zum Beispiel gegenseitig mit Events und Updates zu versorgen.

Ein einfaches Beispiel dafür sind Push-Nachrichten. Angenommen die App empfängt über einen BroadcastReceiver einen Push und liefert diesen in einen Hintergrundprozess der App weiter (in der Regel ein IntentService). Jetzt steht man vor der Aufgabe, wie man von dieser Stelle aus genau die Teile der App informiert, für die der Empfang des Pushes von Bedeutung ist. Zum Beispiel weil man im User Interface durch ein grafisches Element andeuten will, dass eine neue Nachricht angekommen ist oder man den Datenbank-Layer auffordern möchste, die Daten des Pushes zu speichern.

Eine Möglichkeit dieses Problem zu lösen sind normale Referenzen auf die jeweiligen Fragments oder Activities. Aber will man das? Eher nicht. Man würde damit feste Beziehungen zwischen diesen Komponenten eingehen und ein späteter Austausch von Teilen der App wäre mehr als umständlich. Von der Null-Pointer-Gefahr mal ganz zu schweigen.

Android ohne Event Bus

Android ohne Event Bus und mit festen Beziehungen zwischen den Komponenten

Android selbst bringt von Haus aus ein Event-System mit, das als solches vielleicht zunächst gar nicht zu erkennen ist: Android bietet die Möglichkeit Activities über Intent-Extras und Fragments über Bundles mit den entsprechenden Daten zu starten. Aber auch hier ist man nicht wirklich flexibel: Man muss auch diese Activities & Fragments wieder fest verdrahten und ist auch etwas eingeschränkt bei der Wahl der Datentypen.

Und genau an dieser Stelle kommt der Event Bus ins Spiel. Ein Event Bus ist quasi nichts anderes als ein Kommunikationskanal, in den man zum einen reinhören und zum anderen reinrufen kann. Am Beispiel von oben erklärt: Eine Komponente der App, die wartet bis ein Push ankommt, horcht in den Bus hinein. Der Receiver, der den Push empfängt, ruft bei Empfang die jeweiligen Infos in den Bus. Sprich wir haben eine klassische Publish/Subscriber-Architektur, ähnlich den Listenern, aber generischer zu nutzen.

Android mit Event Bus

Android mit Event Bus. Loose Coupling und saubere interne Kommunikation

GreenRobot, Otto und Guava

Für Android gibt es drei Frameworks um mit einem Event Bus zu arbeiten. Guava von Google ist ein EventBus für Java, der auch, aber nicht nur, für Android genutzt werden kann. Der GreenRobot EventBus und Square’s Otto sind speziell für Android angepasste Variante von EventBus Systemen.

Die Frage, ob man Otto oder den GreenRobot einsetzt, ist teilweise mehr ein Glaubenskrieg als ein faktisches Abwegen. GreenRobot wirbt zwar mit vielen Features, die Otto nicht unterstützt, teilweise sind diese aber auch aus Gründen der Einfachheit nicht in Otto enthalten (z.B. lässt sich die Delivery der Events von Background Threads in den Main Thread mit recht wenig Code auch hier verwirklichen).

Was man nun verwendet, ist unserer Meinung nach Geschmackssache. Wir arbeiten sehr gerne mit Otto, da die einfache Verwendung in Kombination mit dem guten Ruf, den die Android Bibliotheken von Square haben, uns immer wieder überzeugt haben. Ich bin mir aber relativ sicher, dass wir genauso mit Greenrobot glücklich geworden wären.

Beispiel: Ein EventBus mit Otto

Greifen wir das Beispiel von oben auf und implementieren beispielhaft einen EventBus, der in einem Fragment generierte Events an die darunter liegende Activity schickt.

Es bietet sich an, den EventBus in einem Singleton bereit zu stellen, um nicht bei jeder Verwendung das Objekt neu zu initialisieren.

public class CortexBus {
    private static Bus instance;	

    private CortexBus() {}

    public synchronized static Bus getInstance() {
        if (instance == null) {
            instance = new Bus();
        }
        return instance;		
    }
}

Im Fragment wollen wir die Events verschicken. Als Beispiel nehmen wir eine Kurznachricht, die dieses Fragment z.B. über eine HTTP Schnittstelle empfangen hat. Hier sind beliebig komplexe Events möglich, da jeder Event über eine eigenen Klasse (ein einfaches POJO) repräsentiert wird. Diese sieht in unserem Beispiel wie folgt aus:

public class MessageReceivedEvent {	
    public long messageId;
    public String message;

    public MessageReceivedEvent(long messageId, String message) {
        this.messageId = messageId;
        this.message = message;
    }
}

Diesen Event können wir nun über den Bus an alle registierten (siehe unten) Subscriber schicken, ohne diese direkt kennen zu müssen:

public class CardFragment extends Fragment {
    private CortexBus bus;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bus = CortexBus.getInstance();

        // code, um die nachricht zu empfangen, usw.
        // long messageId = ...
        // String message = ...

       bus.post(new MessageReceivedEvent(messageId, message));

        // ... weiterer Code ...
    }
}

In der Activity wollen wir auf eingehende Events hören. Wir initialisieren den Bus über den Singleton und erstellen eine Methode mit der @Subscribe Annotation, die die jeweiligen Events empfängt. Welchen Event diese Methode empfängt, wird über die Methodensignatur und die Annotation entschieden. Der Name der Methode ist hierbei nicht wichtig und kann frei gewählt werden. Die Activity muss sich dazu im onResume() und onPause() jeweils an- bzw. abmelden, damit die Events überhaupt empfangen werden.

public class MainActivity extends FragmentActivity {
    private Bus bus;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bus = CortexBus.getInstance();
    }

    @Override
    protected void onResume() {
        super.onResume();		
        bus.register(this);
    }		

    @Override
    protected void onPause() {
        super.onPause();
        bus.unregister(this);
    }

    @Subscribe 
    public void onPushReceived(PushReceivedEvent event) {
        // dein event handling passiert an dieser Stelle
        // ....
    }
}

Das ist alles, was für ein einfaches Event Bus System nötig ist. Man erreicht damit ein sehr angenehmes und stabiles Loose Coupling der Komponenten seiner App. Vor allem wenn die eigene Anwendung komplexer wird, kann man sich damit sehr viel Ärger und NPE-Schmerz ersparen.

In Technischer Hintergrund veröffentlicht | Getaggt: ,

SSL Pinning und Android

Wenn Apps über das Internet kommunizieren, verschlüsseln heute alle seriösen App-Entwickler ihre Daten per SSL (das war nicht immer so). Und das ist sehr sinnvoll, nicht nur um Zuge der NSA-Enthüllungen der letzten Monate. Niemand möchte, dass beispielsweise die eigenen Google-Suchanfragen für jeden im freien WLAN des Cafés nebenan mitlesbar sind. Nicht selten sind diese äußerst privater Natur.

Trotzdem sind viele kleine Details zu beachten, damit SSL sinnvoll und nicht falsch angewandt wird. Und hier spielt SSL Pinning eine wichtige Rolle.

SSL Pinning

Mit SSL Pinning gibt man dem Client (in unserem Fall ist dies die App) fest vor, welches Zertifikat er auf dem Server zu erwarten hat. Findet er ein anderes Zertifikat vor, wird die Verbindung nicht akzeptiert. Ein großer Vorteil bei Entwicklung von Apps ist der Fakt, dass man wieder Kontrolle über den Client bekommt, und nicht nur über den Server. So kann man im Client das auf dem Server zu erwartende Zertifikat fest definieren und damit jedes andere (böse) Zertifikat ausschließen. Bei Webapplikationen ist die Kontrolle des Browsers des Users je nach Kundenstamm deutlich schwerer bis unmöglich.

SSL Pinning bringt nun gleich mehrere Vorteile:

  • Man-In-The-Middle-Angriffe sind nicht möglich, da dem Nutzer gar kein falsches Zertifikat untergeschoben werden kann. Es wird im Code, und nicht über eine Auswahl des Users, genau ein gegebenes Zertifikat akzeptiert. Andere Zertifikate werden abgelehnt.
  • Probleme mit kompromittierten Root-Zertifikate sind ausgeschlossen (was schon öfter der Fall war, als man annehmen sollte).
  • Angreifer können (ihren eigenen) Traffic zwischen der App nicht analysieren, um damit eventuell bisher unbekannte Lücken zu finden oder mit gefälschten Anfragen Exploits zu finden. Mit Tools wie dem mitmproxy sind Analysen dieser Art in Browsern sehr einfach durchzuführen.

SSL Pinning für Android

Für Android Apps ist SSL Pinning recht einfach umzusetzen. Hier eignet sich das exzellente Library Project AndroidPinning von Moxie Marlinspike. Dieser Weg ist vor allem dann interessant, wenn man auf ein von einer CA signiertes Zertifikat angewiesen ist, weil man z.B. ein gemeinsames Zertifikat mit dem Webfrontend teilt. Ohne diesen Constraint wäre es natürlich auch möglich, komplett auf CA-signierte Zertifikate zu verzichten und eigene Zertifikate zu erstellen und die CA Chain damit komplett zu umgehen. Dieser Weg mag sogar der sicherere sein, ist aber nicht immer praktikabel.

Wir clonen zuerst die Library via git:

git clone https://github.com/moxie0/AndroidPinning

und binden das Library Project wie gewohnt in unsere App ein. Hilfe bieten hier auch die Android Developer Docs.

Dann benötigen wir eine Information im Zertifikat, die wir pinnen können. Hier eignet sich die SubjectPublicKeyInfom, die wir wie folgt aus dem entsprechenden Zertifikat extrahieren:

$ tools/pin.py dein-cert.pem
Calculating PIN for certificate: O=api.cortex-media.de, OU=Go to https://www.thawte.com/repository/index.html, OU=Thawte SSL123 certificate, OU=Domain Validated, CN=api.cortex-media.de
Pin Value: 425c3f8c27c44ab1e0818812cb7115e30140a31b

(oder alternative auch eine .crt Datei, je nachdem was vorliegt.)

Der PIN Value ist die Informationen, die wir in den nachfolgenden Schritten in der Android App hart verdrahten. Im unteren Beispiel nutzen wir die „425c3f8c27c44ab1e0818812cb7115e30140a31b“ als Beispiel für eine PIN.

Wenn für die HTTP Kommunikation via HttpClient gearbeitet wird, kann der Client wie folgt initialisiert werden:

String[] pins          = new String[] {"f30012bbc18c231ac1a44b788e410ce754182513"};
HttpClient httpClient  = PinningHelper.getPinnedHttpClient(context, pins);
HttpResponse response  = httpClient.execute(new HttpGet("https://www.google.com/"));

Für die HttpsUrlConnection läuft die Initialisierung nahezu analog ab:

String[] pins          = new String[] {"425c3f8c27c44ab1e0818812cb7115e30140a31b"};
URL url                = new URL("https://www.google.com");
HttpsURLConnection con = PinningHelper.getPinnedHttpsURLConnection(context, pins, url);

Und das wäre dann auch schon alles: Kein anderes Zertifikat als angegebene wird akzeptiert und die SSL Kommunikation ist ein Stück weit sicherer.

In Technischer Hintergrund veröffentlicht | Getaggt: ,

Die perfekte SSL Cipher Suite für Apache (und andere)

„SSL ist nicht mehr sicher!“ konnte man in den letzten Wochen das ein oder andere mal in den einschlägigen Nachrichtenmagazinen lesen. Aber stimmt das? Wir denken nicht.

Es ist korrekt, dass manche der von SSL verwendeten Verschlüsselungsalgorithmen nicht (mehr) stark genug sind (z.B. RC4) oder als von der NSA kompromittiert vermutet werden. Aber das gilt nicht für alle. Vor allem nicht für die mathematischen Grundlagen, die hinter den Algorithmen stecken.

Wer einen Apache Webserver mit SSL betreibt, und unter allen Cipher-Suiten die schwarzen Schafe ausschließen, Perfect Forward Secrecy aktivieren und damit deutlich an Sicherheit gewinnen will, kann sich mit der folgenden Direktive von allen Algorithmen verabschieden, die als nicht (mehr) sicher gelten.

Für den Apache sieht dies wie folgt aus, für Nginx analog, da die Syntax für die Auswahl der Suiten analog läuft.

SSLEngine On
SSLProtocol all -SSLv2
SSLHonorCipherOrder On
SSLCipherSuite EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+RC4:EDH+aRSA:EECDH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS
In Technischer Hintergrund veröffentlicht | Getaggt: ,