13 August 2018 Monday

Jenkins, CSRF und das Crumb

Jenkins bietet seine Dienste neben der Weboberfläche ebenfalls via Rest-Api an. Wer bei Verwendung dieser die Sicherheit erhöhen möchte, kann in den Einstellungen von Jenkins u.a. die CSRF Protection aktivieren um, Cross Site Request Forgery Angriffe zu verhindern. Jenkins akzeptiert nach Aktivierung des Features Rest-Service Aufrufe dann nur, wenn dem Request ein Token im Header beigefügt wird (in der Jenkins Dokumentation wird dies 'crumb' genannt). Als quick & dirty Lösung kann man mittels wget, wie in der Dokumentation [1] beschrieben, ein Token generieren. Dieses kann man dann z.B. über Properties in die Anwendung einfließen lassen. Das ist eine schnelle Lösung für erste Tests, hat aber neben sicherheitstechnischen Bedenken den Nachteil, dass das Token an die Session gebunden ist: wird die Anwendung, die die Rest-Aufrufe durchführt in der Testumgebung beispielsweise auf verschiedene Applicationservern parallel getestet und dabei das gleiche Token verwendet, so erhält der zweite Aufrufer einen Status 403 zurück: invalid crumb.

Viel besser ist es vor jedem Rest-Aufruf ein gültiges Token von Jenkins anzufordern - Jenkins bietet hierfür einen eigenen Rest-Service, der wahlweise im json oder xml Format ein Token zurückliefert. Es bietet sich an hierfür eine eigene Funktion zu schreiben, die bei Bedarf den Tokennamen sowie das Token selbst zurückliefert:


/**
    * Before a jenkins rest service can be called, we need to get a crumb from jenkins. This crumb needs to be added to the http-header when calling a rest service.
    *
    * @return m - a Map containg the name of the crumb and the value of the crumb as Strings.
    * @throws Exception
    */
   private Map getCrumbFromJenkins() throws Exception
   {
       HashMap m = new HashMap();
   
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization", "Basic " + getAuthenticationCode());

       HttpEntity request = new HttpEntity(headers);
       RestTemplate restTemplate = new RestTemplate();
       
       ResponseEntity response = restTemplate.exchange("https://dev.meine-domain.de:8090/crumbIssuer/api/json", HttpMethod.GET, request, Object.class);
       CrumbJson crumbJson = new Gson().fromJson(response.getBody().toString(), CrumbJson.class);

       m.put("name", crumbJson.crumbRequestField);
       m.put("value", crumbJson.crumb);
       
       return m;
   }
   
   public class CrumbJson {
       public String crumb;
       public String crumbRequestField;
   }


[1] https://wiki.jenkins.io/display/JENKINS/Remote+access+API#RemoteaccessAPI-CSRFProtection

Dirk Poerschke   |   13 August 2018 17:30:19   |  
  • Jenkins
  • Security
  • Java
  •   |    [0]

    Zur Zeit arbeite ich an einem spannenden Projekt: Ziel ist die Erstellung einer Java basierten Webanwendung, die verschiedene Workflows inkl. LDAP-basiertem Rechtemanagement für Softwarereleases abbildet. Im Kontext dieser Workflows müssen Daten aus unterschiedlichen Quellen (z.B. SVN, Git, Jenkins) abgerufen, aufbereitet, paketiert und in definierten Formaten an bestimmte Ziele (FTP-Server, Network-shares etc.) geschickt werden. U.a. ist als Format hier das ZIP-Format (mit Verschlüsselung) vorgesehen.

    Das klingt zunächst sehr simpel, jedoch unterstützt Java in der Version 8 (Projektvorgabe) zwar das Erstellen von zip Files (package: java.util.zip), jedoch keine Verschlüsselung. Ein Blick auf mvnrepository.com liefert hierzu einige Treffer: https://mvnrepository.com/open-source/compression-libraries

    Mit am populärsten dürfte sicherlich 'Apache Commons Compress' sein, jedoch zeigt ein Blick in die Dokumentation, dass die Verschlüsselung von Zip-files nicht unterstützt wird (wohl aber von anderen Formaten wie 7z, was mir jedoch leider nicht weiter hilft). Gleiches gilt für 'Snappy Java', hier lässt sich bereits in der Liste der Features kein Wort zum Thema Verschlüsselung finden. Nach einem kurzen Vergleich der unterschiedlichen Bibliotheken habe ich mich schließlich für Zip4J von Lingala entschieden. Die Verwendung ist simpel, jedoch enthält der Konstruktor der Klasse ZipFile, der einen String als Argument entgegen nimmt, meiner Ansicht nach einen Fehler:

    ZipFile zip = new ZipFile("C:\\test\\test.zip");
    zip.addFile(srcFile, params);


    Exception:
    net.lingala.zip4j.exception.ZipException: Probably not a zip file or a corrupted zip file

    Wie ich im Dateisystem sehen kann, wird zwar eine Datei test.zip erzeugt, diese ist jedoch leer und besitzt eine Größe von 0kb.
    Nach einigen Tests habe ich festgestellt, dass der andere Konstruktor, der ein File-Objekt als Argument erwartet, besser funktioniert. Zunächst liefert

    File ziel = new File("C:\\test\\", "test.zip");
    ZipFile zip = new ZipFile(ziel.getAbsoluteFile());


    jedoch wieder die gleiche Exception. Mangelnde Rechte im Dateisystem seitens der Anwendung habe ich durch einen kurzen Test ausgeschlossen, ein

    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("C:\\test\\test.zip"))

    legt problemlos die gewünschte Zip-Datei (allerdings unverschlüsselt) an und auch ein anschließendes Befüllen des Zips ist möglich. Nach einigen Tests hat sich herausgestellt, dass das Problem mit dem Pfad zu tun hat, denn die Verwendung des temp-Dirs funktioniert hier einwandfrei:

    File ziel = new File(System.getProperty("java.io.tmpdir"), "test.zip");
    ZipFile zip = new ZipFile(ziel.getAbsoluteFile());
    zip.addFile(srcFile, params);


    Wie erwähnt, ermöglicht es Zip4J verschlüsselte Zip-Archive zu erstellen. Hierzu muss lediglich ein ZipParameters-Objekt erzeugt und als zweites Argument z.B. an die addFile()-Funktion übergeben werden:

    ZipParameters params = new ZipParameters();
    params.setCompressionLevel(Zip4jConstants.COMP_DEFLATE);
    params.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_MAXIMUM);
    params.setEncryptFiles(true);
    params.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
    params.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
    params.setPassword("Passw0rt");


    Die maximal unterstützte Schlüssellänge im Fall von AES beträgt 256 bit. In Bezug auf das verschlüsselte zip-File ist zu beachten, dass nur die einzelnen im Archiv enthaltenen Dateien verschlüsselt werden, nicht jedoch der Index, d.h. eine Auflistung der Dateinamen ist auch ohne Passworteingabe möglich.

    Sofern jemand noch eine bessere Lösung für das Generieren von verschlüsselten Zip-Archiven kennt, würde ich mich über einen entsprechenden Hinweisen in den Kommentaren freuen!
    Dirk Poerschke   |   25 May 2018 20:53:15   |  
  • Java
  • Zip
  • Verschlüsselung
  •   |   Comments [0]

    23 March 2017 Thursday

    Hello Alexa!

    Am 21.03 und 22.03 fand in München das Hello Alexa Bootcamp von Amazon statt. Da wir bereits seit einiger Zeit mit Alexa Skills sowie der Integration von Alexa in verschiedene Produkte experimentieren, waren wir natürlich vor Ort. Die Veranstaltung war gut besucht, sehr interessant und ich habe einige gute neue Ideen für unsere Projekte mitgenommen. Woran wir bei der SIT aktuell unter anderem arbeiten, ist  hier nachzulesen:
    https://www.sit.de/SIT/hvblog.nsf/dx/23022017125034HVOFW7.htm"

    Ich denke, dass Amazon mit dem Konzept der Erweiterbarkeit von Alexa durch Drittanbieter gegenüber anderen Sprachassistenten einen signifikanten Vorteil hat. Wenn man berücksichtigt, für welche neuen Geräte Alexa bereits angekündigt wurde, dürfte das alles noch sehr spannend werden!
    Dirk Poerschke   |   23 March 2017 15:50:51   |     |    [0]

    Vom 28.11 bis 30.11 fand in Köln der Darwino Partner Enablement Workshop unter der Leitung von Philipp Riand und Jesse Gallagher statt. Was mir im Vorfeld wie ein weiteres Entwicklungstool im Mobile- und Domino-Umfeld erschien, stellt sich nun als zeitgemäße, moderne Plattform heraus, die das Potential hat IBM Domino zu beerben und dabei vieles besser zu machen.

    Das Attraktive an Darwino ist nicht nur die Kompatibilität zu Domino, die eine Überführung von Daten aus der Domino-Welt in die offene Darwino-Welt ermöglicht, sondern auch die Möglichkeit, Darwino parallel mit Domino - inkl. Datenabgleich (Synchronisation / Replikation) - betreiben zu können, so dass eine sanfte Migration möglich ist. Eine Darwino-Anwendung ist sowohl als Webanwendung im Browser, als auch plattformunabhängig (und natürlich responsive) im Mobile-Umfeld sowie betriebssystemunabhängig auf dem Desktop lauffähig. Die Entwicklungsumgebung Darwino Studio setzt auf etablierte Standardkomponenten wie Eclipse, Maven, Git etc. so dass man sich als Entwickler direkt heimisch fühlt. Darwino bringt aber noch weitere Vorteile, mit am beeindruckendsten fand ich die Übertragung des Repliken-Konzepts aus der Domino-Welt in die Mobile-Welt inkl. clientseitige Verschlüsselung sowie die vielfältigen Connectoren (‚Darwino Connectors‘) zu anderen Plattformen wie beispielsweise IBM Connections. Auch in Bezug auf die Datenhaltung ist Darwino wenig wählerisch: Darwino DB ist JSON-basiert und lässt sich mit vielen relationalen Datenbanken betreiben – sowohl server- als auch clientseitig sind die Daten über die gleiche API abrufbar. Wer bisher viel im Notesumfeld entwickelt hat wird so einige Parallelen finden – nur eben in besserer und zeitgemäßer Version.

    Darwino ist noch relativ jung und daher sicher noch nicht perfekt – der dreitägige Workshop hat zeitlich nur knapp gereicht um einen kurzen Einblick in die vielfältigen Funktionen und Features der Plattform zu geben sowie eine eigene Demo-Anwendungen zu erstellen. All diejenigen, die aktuell dabei sind für die Zukunft eine andere Plattform als Domino zu suchen, sollten sich unbedingt Darwino genauer ansehen. Vom Konzept her sowie den verwendeten Softwarekomponenten ist es nämlich meiner Meinung nach eins auf jeden Fall: Zukunftssicher.

    Darwino gibt es in zwei Versionen: Community und Enterprise. Have fun!

    Anmerkung: Es ist natürlich auch möglich, Darwino zu verwenden um mobile Plattformen zu bedienen und parallel weiterhin die bestehende Domino Infrastruktur zu betreiben.
    Dirk Poerschke   |   5 December 2016 15:41:09   |     |   Comments [0]