Mopidy statt Spotify unter Linux

Seit längerem bin ich Spotify-Nutzer - anfangs noch unter Windows, inzwischen unter iOS und Mac OS X. Vor kurzem habe ich mein altes MacBook White mit einer SSD und einem neuen Akku ausgerüstet und Debian aufgespielt. So steht das Gerät nun als pseudostationärer Rechner bei mir rum.

Natürlich wollte ich auch dort gern Spotify verwenden, allerdings hatte ich damit durchaus Schwierigkeiten - denn Spotify bietet für Linux keinen stabilen Player an. Es gibt zwar einen experimentellen Ableger, der hatte bei meinen letzten Versuchen jedoch gar nicht funktioniert.

Alternativ hat man die Möglichkeit, die Windows-Version des Players via Wine laufen zu lassen. Das funktioniert zwar halbwegs gut, hat bei mir aber den Effekt, dass meine (Dual-Core-) CPU zu 100% ausgelastet wird.

Spotify via Wine unter Linux Spotify via Wine unter Linux

Spotify via Wine unter Linux

@Tainnor machte mich nun gestern darauf aufmerksam, dass ich doch einmal Mopidy ausprobieren könnte. Dabei handelt es sich zwar eigentlich um einen Musik-Streamingserver, dank einer Spotify-Extension und eines Webclients kann man es jedoch auch als alternativen Spotify-Client verwenden. :)

Die eigentliche Einrichtung geht dabei schnell von der Hand. Zuerst einmal muss man das Mopidy-Repository in seine Sources-Liste mit aufnehmen und den zugehörigen Public-Key importieren.

1
2
sudo wget -q -O /etc/apt/sources.list.d/mopidy.list http://apt.mopidy.com/mopidy.list
wget -q -O - http://apt.mopidy.com/mopidy.gpg | sudo apt-key add -

Anschließend kann man bereits die nötigen Pakete installieren - darunter ist der Mopidy Streamingserver, die Spotify-Extension und PIP, das wir für die Installation der Mopidy Weboberfläche benötigen werden.

1
2
sudo apt-get update
sudo apt-get install mopidy mopidy-spotify python-pip

Als nächstes kann dann die Weboberfläche installiert werden. Hier habe ich mich für den Mopidy MusicBox Webclient entschieden. Er ist einfach aufgebaut, hat eine intuitive Oberfläche und unterstützt die Spotify-Playlists und auch die Suche im Spotify-Repertoire. Die Installation ist ziemlich einfach:

1
sudo pip install Mopidy-MusicBox-Webclient

Nun kann man sich die Mopidy-Konfigurationsdatei erstellen lassen, um diese zu bearbeiten. Dazu startet man Mopidy über einen Terminal und beendet ihn einfach direkt wieder. Die Datei wird dann unter "~/.config/mopidy/mopidy.conf" angelegt. Mopidy selbst muss nicht als Root gestartet werden und kann via Ctrl+C wieder geschlossen werden:

1
2
mopidy
vi ~/.config/mopidy/mopidy.conf

Die Konfigurationsdatei enthält bereits Konfigurationsblöcke. Diese sind zwar auskommentiert, zeigen jedoch, wie die Default-Werte der Konfiguration aussehen. Am Ende der Konfigurationsdatei fügt man nun einfach einen weiteren Konfigurationsblock für die Spotify-Extension ein, der wie folgt aussehen sollte:

1
2
3
4
5
6
7
[spotify]
enabled = true
username = <Spotify-Username>
password = <Spotify-Password>
bitrate = 320
timeout = 10
cache_dir = $XDG_CACHE_DIR/mopidy/spotify

Wenn das erledigt wurde, kann man nun Mopidy erneut über einen Terminal starten. Die Ausgabe von Mopidy sollte dann so ähnlich aussehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
INFO     Starting Mopidy 0.19.4
INFO     Loading config from: builtin defaults, /etc/xdg/mopidy/mopidy.conf, /home/kenny/.config/mopidy/mopidy.conf, command line options
INFO     Enabled extensions: spotify, mpd, http, stream, softwaremixer, musicbox_webclient, local
INFO     Disabled extensions: none
INFO     Starting Mopidy mixer: SoftwareMixer
INFO     Mixing using GStreamer software mixing
INFO     Starting Mopidy audio
INFO     Starting Mopidy backends: SpotifyBackend, LocalBackend, StreamBackend
INFO     Mopidy uses SPOTIFY(R) CORE
INFO     Loaded 0 local playlists from /home/kenny/.local/share/mopidy/local/playlists
INFO     No local library metadata cache found at /home/kenny/.local/share/mopidy/local/library.json.gz. Please run `mopidy local scan` to index your local music library. If you do not have a local music collection, you can disable the local backend to hide this message.
INFO     Loaded 0 local tracks using json
INFO     Audio output set to "autoaudiosink"
INFO     Starting Mopidy core
INFO     Starting Mopidy frontends: HttpFrontend, MpdFrontend
INFO     MPD server running at [::ffff:127.0.0.1]:6600
INFO     HTTP server running at [::ffff:127.0.0.1]:6680
INFO     Connected to Spotify
INFO     Loaded 36 Spotify playlists

Das Mopidy-Frontend findet ihr unter http://localhost:6680/. Dort wird euch auch eine Liste der installierten Webclients angezeigt - samt Verlinkung zur jeweiligen Oberfläche.

Mopidy Einstiegsseite

Mopidy Einstiegsseite

Mit einem Klick auf den angezeigten Link gelangt ihr in den Webclient. Wählt dort ein Lied aus und lasst es abspielen. Klappt das bei euch, dann seid ihr auch schon fertig mit der Installation von Mopidy samt der Konfiguration vom Webclient und von Spotify.

Mopidy unter Linux Mopidy unter Linux

Mopidy unter Linux

Bei mir hat der Einsatz von Mopidy anstelle des offiziellen Spotify-Clients jedenfalls seinen Zweck erfüllt. Meine CPU läuft nun nicht mehr auf Hochtouren, nur um Musik zu streamen. Gleichzeitig kann Mopidy so konfiguriert werden, dass es auch lokale Musikbibliotheken bereitstellen und sogar remote verwendet werden könnte. :)

Streamende Grüße, Kenny

Grillen im Alltag: Weber Q1200 Gasgrill

Der Sommer ist fast vorbei - die Lust auf gegrillte Leckereien hingegen nicht. Was tut man also, wenn man nicht darauf verzichten mag? Unsere Idee: ein Gasgrill muss her. Doch auch andere Gründe haben uns dazu bewogen. Da wäre zum einen die Rauchentwicklung: Bis die Kohle durchgeglüht ist, kann es schon mal qualmen. Mehrfach ist diesen Sommer die Feuerwehr in unserer Nähe ausgerückt. So einen Besuch muss man nicht an der Tür haben. Und es ist aufwändig - einfach mal angrillen macht man nicht so nebenbei.

Weber Q1200 Gasgrill

Weber Q1200 Gasgrill Karton

Bei der Wahl der Grills gab es eigentlich keine langen Überlegungen: Sowohl @inte als auch @kventil schienen mit ihrem Weber Q1200 sehr zufrieden zu sein. Wir hatten zudem den Vorteil, dass es hier in Berlin einen Weber Shop gibt, sodass wir das gute Gerät erst einmal im Laden ansehen, anfassen und auch direkt mitnehmen konnten. :)

IMG_9147

Weber Q1200 Gasgrill aufgebaut

Der Aufbau ging relativ einfach, einen passenden Wagen hatten wir bereits auf dem Balkon stehen und das erste Fleisch lag auch schon bereit. Woran man sich gewöhnen muss, ist, dass man das Grillgut nicht permanent im Blick hat. Stattdessen bringt man den Grill auf die gewünschte Temperatur, packt das Fleisch auf den Rost, schließt den Deckel, wartet, wendet das Fleisch, wartet nochmal und ist nach wenigen Minuten fertig. Es entsteht lediglich weißer Rauch während der Zubereitung und auch das nur abhängig von der Feuchtigkeit des Essens.

IMG_9149

Fleischzeichnung

Durch den geschlossenen Deckel, der die Wärme hält, ist alles rasend schnell fertig und bleibt dadurch auch sehr saftig. Bei den Testwürsten ging es sogar einen Ticken zu schnell. Trotzdem erhält man am Ende ein leckeres Fleisch (durch die Maillard-Reaktion) inklusive schöner Zeichnung.

IMG_9151

Weber Q1200 Gasgrill verstaut

Die Reinigung des Grills ist kein Problem: Nachdem man mit der Essenszubereitung fertig ist, lässt man den Grill noch ein wenig auf höchster Stufe nachgrillen, sodass die Essensreste auf dem Rost verkohlen. Diese können dann einfach abgekratzt werden. Der Grill hat unter einer Öffnung in der Bodenmitte eine Fettauffangschale, in die man dann einfach den ganzen Dreck fegen kann. Nochmal kurz mit dem Lappen drüber gewischt und fertig ist der Grill.

Woran wir nicht gedacht hatten und was größere Probleme bereiten kann, ist die korrekte Handhabung und Lagerung der Gasflasche. Diese haben in Wohnräumen und auch in Kellern nichts zu suchen. Da das darin enthaltene Propan/Butan schwerer als Luft ist, sammelt es sich - sollte es mal ein Leck geben - an der tiefsten Stelle im Raum. Bereits ein einzelner Funke (z.B. durch das Einstecken eines Steckers in eine Steckdose) reicht dann zur Entzündung. Doch auch Erstickung ist möglich. Das einfachste und sicherste ist es daher, die Flasche auf dem Balkon im Freien zu lagern. Dort wird dauerhaft gut gelüftet und es gibt keine Zündquellen, die gefährlich werden könnten.

Gasförmige Grüße, Kenny

Behandlung depublizierter Artikel in WordPress

Vor kurzen wurde ich vor eine spannende Aufgabe gestellt: Eine Seite musste eine ganze Menge alter Artikel depublizieren. Die Besucher dieser Artikel sollten jedoch nicht einfach von einer 404-Fehlerseite abgefangen werden, sondern ihnen sollte mitgeteilt werden, weshalb der Fehler aufgetreten ist und sie sollten - möglichst automatisch - auf die Startseite umgelenkt werden.

Dabei gab es mehrere Hindernisse: Zum einen musste eine Datenbasis geschaffen werden, welche URLs nicht mehr verfügbar sind und es musste bei deren Aufruf der entsprechend angepasste Inhalt angezeigt werden. Anstatt jedoch anzufangen, die gesamte Seite abzugrasen, um an die URLs zu gelangen, hatte ich eine bessere Idee: Die Artikeldatenbank selbst würde diese Datenbasis darstellen.

Um die Artikel offline zu nehmen, sollten diese nicht gelöscht, sondern lediglich deren Sichtbarkeit auf "privat" gestellt werden. Damit könnte man eine generalisierte Lösung erstellen, die für alle privaten Artikelseiten funktioniert. Das gute: Private Artikel führen bei externen Besuchern zu einer 404-Seite. Der Inhalt der 404-Seite wird durch die Datei "404.php" des Themes repräsentiert. Dort könnte man sich einklinken und vor dem Anzeigen des Fehlers prüfen, ob der Fehler aufgetreten ist, weil der Artikel nicht existiert oder weil er privat ist. Je nach Ergebnis könnte man dann den einen oder den anderen Inhalt anzeigen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
  $private_404_args    = wp_parse_args($wp->matched_query);
  $private_404_content = "";
  $private_404_done    = false;
  $private_404_result  = 0;

  if (isset($private_404_args["p"]) || isset($private_404_args["name"])) {

    if ((!$private_404_done) && isset($private_404_args["p"])) {
      $private_404_result = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE id = %d AND LOWER(post_status) = 'private' AND LOWER(post_type) = 'post';"), intval($private_404_args["p"]));

      $private_404_done = (intval($private_404_result) === 1);
    }

    if ((!$private_404_done) && isset($private_404_args["name"])) {
      $private_404_result = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE LOWER(post_name) = LOWER(%s) AND LOWER(post_status) = 'private' AND LOWER(post_type) = 'post';", $private_404_args["name"]));

      $private_404_done = (intval($private_404_result) === 1);
    }

  }

  if ($private_404_done) {
    $private_404_content = $wpdb->get_var("SELECT post_content FROM $wpdb->posts WHERE LOWER(post_title) = '[410]' AND LOWER(post_status) = 'private' AND LOWER(post_type) = 'page';");

    if ($private_404_content != null) {
      print(apply_filters('the_content', $private_404_content));
    }
  } else {
    $private_404_content = $wpdb->get_var("SELECT post_content FROM $wpdb->posts WHERE LOWER(post_title) = '[404]' AND LOWER(post_status) = 'private' AND LOWER(post_type) = 'page';");

    if ($private_404_content != null) {
      print(apply_filters('the_content', $private_404_content));
    }
  }
?>

Die schlussendliche Lösung ist etwas komplizierter geworden. Eigentlich hatte ich vor, einfach per "url_to_postid()" herauszufinden, ob unter der URL, die aufgerufen wurde, ein Artikel existiert. Leider funktioniert die Funktion offenbar nicht bei privaten Artikeln, wenn sie von außen aufgerufen wurden.

Im Feld "$wp->matched_query" stehen glücklicherweise die Teile der erfolgreichen erkannten URL-Teile, wie sie die Bildungsregeln in der WordPress-Konfiguration vorgeben. In meinen Fall wäre das z.B. "/%year%/%postname%/". Der Inhalt von matched_query wäre demnach für den aktuellen Artikel hier "year=2014&name=behandlung-depublizierter-artikel-in-wordpress". Anhand des Namen, der in WordPress eindeutig ist, kann dann der zugehörige Artikel in der "wp_posts"-Tabelle gefunden werden.

Da ich sowieso gerade dabei war, habe ich auch noch eine weitere Vereinfachung vorgenommen - nämlich die, den angezeigten Inhalt der entsprechenden Fehlerseiten über das WordPress-Backend bestimmen zu können. Dazu müssen zwei Seiten (nicht Artikel!) erstellt werden, deren Sichtbarkeit ebenfalls auf "privat" gesetzt wird. Die Seite, die für generelle 404-Fehlermeldungen genutzt werden soll, muss den Titel "[404]" erhalten (ohne Anführungszeichen). Die Seite, die für depublizierte Fehlermeldungen genutzt werden soll, muss hingegen den Titel "[410]" erhalten (ebenfalls ohne Anführungszeichen). Um das ganze einmal zu visualisieren, habe ich hier zwei URLs:

Ich hoffe, die Erläuterungen sind den für den ein oder anderen hilfreich. :)

Depublizierende Grüße, Kenny

Monat der WordPress-Sicherheit ist beendet

In den letzten Wochen habe ich einige Artikel zur Sicherheit ausgewählter WordPress-Plugins geschrieben. Nun wollte ich abschließend noch einmal ein Fazit ziehen.

Begonnen hatte alles damit, dass auf Golem.de im Abstand von wenigen Wochen über zwei weit verbreitete Plugins mit leicht auffindbaren und dennoch schwerwiegenden Fehlern berichtet wurde. Es hatte mich daher interessiert, wie schlecht es wirklich um die Codequalität bei weit verbreiteten Plugins bestellt ist.

Also begab ich mich zur Liste der beliebtesten Plugins und begann, mir Plugins anzusehen. Dabei habe ich mich auf halbwegs aktuelle Plugins konzentriert, die mindestens 25.000 Downloads hatten. Außerdem sollte die Analyse schnell gehen: Es ging mir nicht darum, Fehler in der Ablauflogik der Plugins zu finden, sondern ich wollte die groben Schnitzer finden - also die Fehler, die einem sofort ins Auge springen. Glücklicherweise verfügt jedes Plugin über ein eigenes Trac-Repository, sodass man direkt online in den Quelltext gucken kann. Damit entfällt das langwierige downloaden, entpacken und im Editor öffnen. So kann man relativ schnell einzelne Plugins abarbeiten.

Ich habe mir etwa 100 Plugins angesehen, die sich vom Namen oder von der versprochenen Funktionalität spannend anhörten. Dabei sind die 12 unten aufgelisteten Plugins aufgefallen, die solche nennenswerten Löcher enthielten (wobei 2 davon Beifang sind - hatte ich ein Plugin mit einer Lücke gefunden, habe ich mir auch die anderen Plugins des gleichen Autors angesehen).

Plugin Angriff Anzahl
WP RSS Aggregator XSS 141.960*
WP CSV Information Leakage 31.675*
WP Advanced Importer Unprivileged File Upload 12.041*
WP Ultimate CSV Importer Unprivileged File Upload 112.793*
Simplr Registration Form Plus+ Information Leakage 105.325*
Form Builder Unprivileged File Upload 88.218*
WordPress File Upload XSS 25.060*
WP Modal Login Privilege Escalation 34.763*
Quick Chat SQL Injection 207,488**
Quick Count SQL Injection 14,268**
Login With Ajax Privilege Escalation 273.635**
FormGet Contact Form XSS 159.545**
Total: 1.206.771
(*Stand vom 16.08.2014)
(**Stand vom 26.08.2014)

Bei der Durchsicht der Plugins sind mir mehrere Dinge aufgefallen:

  1. Viele Plugins sind offenbar eher zufällig "sicher" und nicht etwa, weil der Programmierer das geplant hätte. Oft scheitert ein Angriff nur daran, dass vor einem anfälligen Code eine WordPress-eigene Funktion aufgerufen wird, die (aufgrund ihres Fehlens) zu einer Exception führt, wodurch die Programmausführung beendet wird. Würden solche Aufrufe in eigene Funktion gekapselt werden, wären viel mehr Angriffe möglich.
  2. Nur wenige Plugins schützen sich gegen Cross-Site-Request-Forgeries ab. Sie enthalten derzeit eventuell keine Lücken, sollten diese jedoch durch eine Codeänderung eingeführt werden, ließen sie sich dadurch wesentlich leichter ausnutzen.
  3. Die Punkte 1.) und 2.) rühren oftmals daher, dass die Entwickler sich nicht darum sorgen, ob ihre PHP-Scripte direkt aufgerufen werden dürfen. Vielen Entwicklern ist offenbar nicht bewusst, dass sie ihre Entrypoints und Codepfade kennen und absichern sollten. Dies war die häufigste Fehlerkorrektur, die von mir angeschriebene Plugin-Entwickler umgesetzt haben.
  4. Viele Plugin-Entwicklern verlassen sich darauf, dass eingeloggte Nutzer gutartig sind. Während sie den Eingaben von nicht-eingeloggten Besuchern meist gänzlich misstrauen, werden Eingaben von eingeloggten Nutzern häufig weniger oder gar nicht geprüft. Daher sollte man sich beim Einsatz von Plugins gut überlegen, ob man "Subscriber" in seiner WordPress-Installation zulässt.
  5. WordPress bietet Entwicklern viele fertige Funktionen an, mit denen sie ihre Plugins sicherer machen können - das reicht bis zu fertigen Referer- und Nonce-Prüfungen gegen XSRF-Angriffe. Mir ist jedoch auch aufgefallen, dass WordPress-Nonces relativ lange gültig sind, sich nur an der Zeit orientieren, statt an nutzergebundenen Werten und damit potentiell mehrfach und von verschiedenen Nutzern verwendet werden könnten (was dazu führt, dass ein Angreifer sich regelmäßig einen neuen Nonce holen und diesen für laufende Attacken verwenden könnte).

Die Kommunikation mit den einzelnen Plugin-Entwicklern war bisher durchweg positiv. Viele haben sich gefreut, dass sich jemand tiefer mit ihren Plugins auseinandersetzt und sie über die Probleme informiert. Die meisten haben in weniger als einer Woche das Problem behoben, bei anderen Plugins warte ich darauf hingegen immernoch - dort wurde ein Fix lediglich in Aussicht gestellt. Überrascht hat mich die Reaktion eines Entwicklers, der sein Plugin aufgrund von Zeitmangel aus dem Plugin-Repository entfernt hat. Ich fand, dass dies ein mutiger Schritt war.

Ich fand es spannend, mich in die verschiedenen Programmierstile der einzelnen Plugin-Entwickler einzuarbeiten. Ich habe mich deshalb dazu entschlossen, solche Aktionen in den nächsten Monaten mit den Plugins anderer Content-Management-Systeme zu wiederholen.

Für Leute und Firmen, die WordPress professionell einsetzen, kann ich aufgrund der Ergebnisse eigentlich nur den Rat geben: Wenn Fremdplugins eingesetzt werden, empfiehlt es sich, wenigstens kurz in deren Quelltext zu blicken. Guckt euch an, ob sichergestellt wird, dass die PHP-Scripte nicht direkt aufgerufen werden können. Damit ist bereits viel gewonnen, da der Plugin-Entwickler so viel mehr Kontrolle über den Programmablauf hat.

WordPress-Grüße, Kenny

Bug in FormGet Contact Form Plugin erlaubt Persistive XSS

Heute habe ich noch etwas spannendes für euch: eine Persistive-XSS-Lücke. Das bedeutet, dass man nicht jedem Benutzer den manipulierten Seiteninhalt unterschieben muss, sondern dass diese permanent gespeichert wird und so bei jedem Aufruf der Originalwebseite aktiv wird. Gefunden habe ich diesen Fehler im FormGet Contact Form Plugin.

Dieses besteht nur aus der Datei "index.php" und bietet einen AJAX-Endpunkt in der Funktion "cf_text_ajax_process_request()". Dort werden einfach ungeprüft POST-Parameter in die Plugin-Konfiguration übernommen. Besonders spannend ist die Option "fg_embed_code", die über den POST-Parameter "value" gesetzt wird. Dabei handelt es sich um Code, der später über die Funktion "embeded_code()" in jeder Seite des WordPress-Blogs eingebunden wird: Jackpot!

Die Einbringung von eigenem Code funktioniert relativ einfach über ein HTML-Formular, das ein Angreifer einmalig benutzen muss:

1
2
3
4
5
6
7
<form action="http://example.com/wp-admin/admin-ajax.php" method="POST">
  <input type="hidden" name="action" value="request_response" />
  <input type="hidden" name="value" value="<script>alert('XSS.');//sideBar</script>" />
  <input type="hidden" name="value_hide" value="" />
  <input type="hidden" name="page_id" value="" />
  <input type="submit" />
</form>

Ich stand mit den Entwicklern in Kontakt, die mir auch mitgeteilt hatten, etwas auf der Serverseite gefixt zu haben. Auf meine Anmerkung hin, dass der Fehler im Plugin sei und nicht in ihrer Serveranwendung, erhielt ich jedoch keine Antwort mehr. Solange kein Patch verfügbar ist, kann der Rat deshalb nur lauten, das Plugin umgehend zu deaktivieren.

Persistente Grüße, Kenny

Seite 1 von 81123...Letzte »