Categories
IT Web

WordPress – Bilder auf der eigenen Homepage schützen

In WordPress nutze ich zum Darstellen meiner Bilder das Gallery-Plugin “NextGEN Gallery“.

Da ich nicht will das Hinz und Kunz meine Bilder betrachten können, habe ich die Seiten vor Fremdzugriff geschützt.
Zuerst habe ich es mit den WordPress eigenen Bordmitteln (Visibility entsprechend geändert) und dann mit einem Plugin “Page Restrict” probiert.

Das Problem was mir dabei aufgefallen ist:
Es wird zwar die jeweilige Seite geschützt, man muss sich also entsprechend WordPress gegenüber authentifizieren, aber die eingebetteten Inhalte bleiben davon unberührt.
D.h. angemeldete Benutzer können zum Beispiel die Links zu Bildern per E-Mail verrschicken und den Schutz somit für Fremde umgehen.

Nach ein bisschen recherchieren habe ich leider festgestellt, dass das auch bei großen sozialen Netzwerken wie facebook und lokalisten der Fall ist!!!
Wenn hier jemand die URL zu einem in facebook oder lokalisten eingefügten Bild kopiert, sich ausloggt und in die URL aufruft, wird das Bild trotzdem angezeigt!!!

 

Dieses Sicherheitsproblem lässt sich meiner Meinung nach nur lösen, indem die Bilder nicht direkt aufgerufen werden, sondern eine php-Abfrage dazwischen gelegt wird.
Der php Code muss prüfen, ob das Bild ausgeliefert werden darf oder nicht.

Kurze Erklärung zu NextGEN Gallery:
Ich gehe bei meiner Ausführung davon aus, dass die NextGEN Gallerien im Verzeichnis wp-content/gallery abgelegt sind. Das ist die Standardeinstellung.

 

Folgendes Schritte müssen dafür durchgeführt werden:

1) Das Original-Verzeichnis zu den Gallerien muss per .htaccess Datei vor dem Zugriff geschützt werden.

# Disable Directory Browsing
Options All -Indexes

SetEnvIf Request_URI ".*/wp-content/gallery/.*" protected

# Auth directives
AuthUserFile <path_to_your_httpdocs_directory>/.htpasswd

AuthName "Password Protected"
AuthType Basic

# Setup a deny/allow
Order Deny,Allow

# Deny from everyone
Deny from all

# except if either of these are satisfied
Satisfy any

# 1. a valid authenticated user
Require valid-user

# 2. the "require_auth" var is NOT set
Allow from env=!protected

Die .htpasswd Datei müsst ihr natürlich noch anlegen und füllen.

Damit ist das Verzeichnis der kompletten Gallerien schon einmal geschützt.

Das könnte jetzt eigentlich auch schon so bleiben, nur ist die htaccess-Abfrage nicht wirklich schön. Aus diesem Grund müssen noch ein paar Schritte durchgeführt werden.

 

2) Beim Ausliefern des HTML-Codes schreiben wir via Apache-Modul alle URLs welche auf die Gallerien verweisen um, aber nur, wenn wir uns nicht im WordPress Admin-Bereich befinden.
(Dazu wird das Apache-Modul mod_substitute benötigt)

<LocationMatch  ^((?!wp-admin).)*$ >
  AddOutputFilterByType SUBSTITUTE text/html
  Substitute "s|wp-content/gallery/|wp-content/gallery-images/|ni"
</LocationMatch>

Mit dieser Regel wird aus dieser URL:
http://<your-domain>/wp-content/gallery/dummy_gallery/abc123.jpg
dann diese:
http://<your-domain>/wp-content/gallery-images/dummy_gallery/abc123.jpg

Wird jetzt eine so modifizierte HTML-Seite aufgerufen, bekommt der Besucher kein einziges Bild zu sehen. Das gallery-images Verzeichnis existiert nicht.
Darum brauchen wir eine zusätzliche Erweiterung in der .htaccess Datei.

 

3) gallery-images via .htaccess auf php-Datei mappen

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteCond %{HTTP_REFERER} wp-admin
  RewriteRule ^wp-content/gallery-images\/(.*) wp-content/gallery/$1 [NC]
  RewriteCond %{HTTP_REFERER} !wp-admin
  RewriteRule ^wp-content/gallery-images\/(.*) renderImageFromFile.php?imgUrl=$1 [NC] 
</IfModule>

 

 4) Jetzt noch die php-Datei erzeugen

<?php
    // include wordpress functions needed for user validation
    require_once('./wp-config.php');

    $dummy_image = "dummy_image.jpg";
    $rootpath = 'wp-content/gallery/';
    $imgpath = $rootpath . $_GET['imgUrl'];

    //check if file exists
    if(!file_exists($imgpath))
    {
       exit;
    }

    // visitor needs to be logged in
    if(!is_user_logged_in()) //this is a wordpress function
    {
       header('Content-Type: image/jpeg');
       header("Content-Length: " . filesize($dummy_image));
       $fp = fopen($dummy_image, 'rb');
       fpassthru($fp);
       fclose($fp);
       exit;
    }

    // check referer url domain
    $refURL = parse_url($_SERVER['HTTP_REFERER'],PHP_URL_HOST);

    if($refURL != '<your_domain>')
    {
       header('Content-Type: image/jpeg');
       header("Content-Length: " . filesize($dummy_image));
       $fp = fopen($dummy_image, 'rb');
       fpassthru($fp);
       fclose($fp);
       exit;
    }

    // Get the mimetype for the file
    $finfo = finfo_open(FILEINFO_MIME_TYPE);  

    // return mime type ala mimetype extension
    $mime_type = finfo_file($finfo, $imgpath);
    finfo_close($finfo);
     
    switch ($mime_type){
        case "image/jpeg":
            // Set the content type header - in this case image/jpg
            header('Content-Type: image/jpeg');
            break;
        case "image/png":
            // Set the content type header - in this case image/png
            header('Content-Type: image/png');
            break;
        case "image/gif":
            // Set the content type header - in this case image/gif
            header('Content-Type: image/gif');
            break;
    }
   
    header("Content-Length: " . filesize($imgpath));
    $fp = fopen($imgpath, 'rb');
    
    fpassthru($fp);
    fclose($fp);
     
?>

 

Mit diesen kleinen Änderungen sind alle Gallerie Bilder der WordPress Seite vor fremdem Zugriff geschützt, auch wenn Links zu den einzelnen Bildern verteilt werden.

 

 

 

 

Categories
IT Web

DokuWiki – Verschlüsselung auf dem Dateisystem hinzufügen

Um von überall aus auf diverse persönliche Informationen, Notizen, Kochrezepte usw. zugreifen zu können, habe ich mir DokuWiki auf dem Server installiert.

DokuWiki ist ein Wiki-System, welches in PHP umgesetzt ist, und sämtliche Daten auf dem Filesystem ablegt.

Ich habe das Wiki soweit “abgeschottet”, dass kein einziger Beitrag öffentlich einsehbar ist. Besucher müssen sich also mit einem Benutzernamen und Passwort einloggen um Beiträge zu sehen.
Der Button zum Registrieren neuer Benutzer wird ebenfalls nicht angezeigt.

Und natürlich ist der komplette Zugriff auf https umgebogen.
Das kann man einfach mit einer entsprechenden .htaccess Datei erledigen:

RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule ^(.*) https://<DOKUWIKI_URL>/$1 [R,L]

 Sicherheitstechnisch ist das schon einmal gar nicht so schlecht.

 

Allerdings:
Wenn sich ein Hacker Zugang zum Server verschafft hat, dann hält ihn die Login-Restriktion im Browser auch nicht mehr auf, sämtliche Daten einfach direkt im Filesystem zu lesen!

 

Lösung:
Um dieses Problem zu umgehen, habe ich DokuWiki ein wenig mit Code erweitert.
Per Knopfdruck kann man das komplette data-Verzeichnis (in dem sämtliche Wiki-Seiten abgelegt werden) auf dem Dateisystem verschlüsseln bzw. wieder entschlüsseln.
Dadurch ist der Zugriff auch auf Filesystem-Ebene abgesichert.

decrypt box
decrypt box

Für meine Lösung sind folgende Schritte notwendig:
(ich beschreibe das jetzt für die Standard-Installation, ich habe bei mir ein anderes Theme gewählt. Dann müssen die Anpassungen natürlich im jeweiligen Theme geschehen.)

1. Dojo Toolkit nachrüsten
Für einen schönen Dialog zur Passwort-Eingabe habe ich mich für das Dojo Toolkit entschieden.

cd lib/tpl/dokuwiki
wget http://download.dojotoolkit.org/release-1.9.2/dojo-release-1.9.2.tar.gz
tar xvfz dojo-release-1.9.2.tar.gz
ln -s dojo-release-1.9.2 dojo

2. Dojo über die main.php Datei auf jeder Seite laden. Dazu einfach vor dem </head> Tag folgenden Code einfügen:

  <link rel="StyleSheet" type="text/css" href="<?php echo DOKU_TPL?>dojo/dijit/themes/tundra/tundra.css">
    <script type="text/javascript">
      var djConfig = {
        parseOnLoad : true
      };
    </script>
    <script type="text/javascript" src="<?php echo DOKU_TPL?>dojo/dojo/dojo.js"></script>
    <script>
      dojo.require("dojo.parser");
      dojo.require("dijit.form.Button");
      dojo.require("dijit.Dialog");
      dojo.require("dijit.form.TextBox");
      //dojo.addOnLoad(hideDialog);

      function hideDialog()
      {
        dijit.byId('dialog1').hide();
      }

      function showDialog()
      {
        dijit.byId('dialog1').show();
      }
    </script>

3. Im <body> Tag noch die CSS Klasse einfügen

<body class="tundra">

 4. Den Code für den Button in die tpl_footer.php einbinden

<?php
/**
 * Template footer, included in the main and detail files
 */

// must be run from within DokuWiki
if (!defined('DOKU_INC')) die();
// check if encrypted file exists
 $msg="";
 $url="#";
 if(is_file("dokuwiki_data_backup.tar.gz.enc"))
 {
   $msg="Decrypt";
   $url="<cgi-path>/dec";
 }
 else
 {
   $msg="Encrypt";
   $url="<cgi-path>/enc";
 }
?>
<script language="javascript">
  function checkPw(dialogFields)
  {
     if (dialogFields.password.value != dialogFields.confpassword.value)
     {
        alert("Confirmation password is different.");
        return false;
     }
     dialogFields.type.value=dialogFields.password.value;
     return true;
  }
</script>

<!-- ********** FOOTER ********** -->
<div id="dokuwiki__footer"><div>
    <?php tpl_license(''); // license text ?>
<div>
  <div style="font-size:8pt;position:absolute;height:65px;"><a style="color:black" onclick="javascript:showDialog();" href="#"><?=$msg?></a>
</div>

    <div>
        <?php
            tpl_license('button', true, false, false); // license button, no wrapper
            $target = ($conf['target']['extern']) ? 'target="'.$conf['target']['extern'].'"' : '';
        ?>
        <a href="http://www.dokuwiki.org/donate" title="Donate" <?php echo $target?>><img
            src="<?php echo tpl_basedir(); ?>images/button-donate.gif" width="80" height="15" alt="Donate" /></a>
        <a href="http://www.php.net" title="Powered by PHP" <?php echo $target?>><img
            src="<?php echo tpl_basedir(); ?>images/button-php.gif" width="80" height="15" alt="Powered by PHP" /></a>
        <a href="http://validator.w3.org/check/referer" title="Valid HTML5" <?php echo $target?>><img
            src="<?php echo tpl_basedir(); ?>images/button-html5.png" width="80" height="15" alt="Valid HTML5" /></a>
        <a href="http://jigsaw.w3.org/css-validator/check/referer?profile=css3" title="Valid CSS" <?php echo $target?>><img
            src="<?php echo tpl_basedir(); ?>images/button-css.png" width="80" height="15" alt="Valid CSS" /></a>
        <a href="http://dokuwiki.org/" title="Driven by DokuWiki" <?php echo $target?>><img
            src="<?php echo tpl_basedir(); ?>images/button-dw.png" width="80" height="15" alt="Driven by DokuWiki" /></a>
    </div>
</div></div><!-- /footer -->
<div dojoType="dijit.Dialog" id="dialog1" title="<?=$msg?>">
      <form action="<?=$url?>" method="post" validate="true" id="encdec_form" onsubmit="return checkPw(this);">
        <input type="hidden" name="type" id="type" value="<?=$msg?>" />
        <table width="350">
          <tr>
            <td><label>Password:</label></td>
            <td><input type="password" trim="true" dojoType="dijit.form.TextBox" value="" name="password" id="password"/></td>
          </tr>
          <tr>
            <td><label>Confirm Password:</label></td>
            <td><input type="password" trim="true" dojoType="dijit.form.TextBox" value="" name="confpassword" id="confpassword"/></td>
          </tr>
          <tr><td colspan="2">&nbsp;</td></tr>
          <tr>
            <td colspan="2" align="center">
            <table border="0" cellspacing="0" cellpadding="0">
                <tr>
                  <td align="center" valign="top"><button dojoType="dijit.form.Button" type="submit" id="LoginButton">Ok</button></td>
                  &nbsp;
                  <td align="left" valign="top"><button dojoType="dijit.form.Button" type="button"
                  onclick="document.getElementById('encdec_form').reset();" id="Cancel">Cancel</button></td>
                </tr>
           </table>
           </td>
          </tr>
        </table>
      </form>
    </div>

<?php
tpl_includeFile('footer.html');

 5. Jetzt noch zwei Skripten im cgi-Verzeichnis

Einmal zum verschlüsseln: enc

#!/bin/sh

echo Content-type: text/html
echo ""

if [ -f "<PATH_TO_WIKI>/dokuwiki_data_backup.tar.gz.enc" ] ; then
  echo ""
else
  #get pass
  read passfromform
  pass=`echo "$passfromform" | cut -d"=" -f2 | cut -d"&" -f1`

  # check pass
  if [ "" != "${pass}" ] ; then
    # change dir
    cd <PATH_TO_WIKI>
 
    # create file
    tar cf dokuwiki_data_backup.tar data 2>/dev/null 1>/dev/null
    gzip -9 dokuwiki_data_backup.tar
    openssl des3 -k $pass -in dokuwiki_data_backup.tar.gz -out dokuwiki_data_backup.tar.gz.enc
    rm dokuwiki_data_backup.tar.gz
    rm -rf data

    # add directories
    mkdir data
    mkdir data/attic
    mkdir data/cache
    mkdir data/index
    mkdir data/locks
    mkdir data/media
    mkdir data/media_attic
    mkdir data/media_meta
    mkdir data/meta
    mkdir data/pages
    mkdir data/tmp
    mkdir data/cache/c
    mkdir data/cache/e
    mkdir data/cache/6
    chmod -R 777 data

    cd -
  fi
fi

echo "<html><body><script>history.back();</script></body></html>"

 Und einmal zum entschlüsseln: dec

#!/bin/sh

echo Content-type: text/html
echo ""

if [ -f "<PATH_TO_WIKI>/dokuwiki_data_backup.tar.gz.enc" ] ; then
  #get pass
  read passfromform
  pass=`echo "$passfromform" | cut -d"=" -f2 | cut -d"&" -f1`

  if [ "" != "${pass}" ] ; then
    # change directory
    cd <PATH_TO_WIKI> 

    # decrypt file
    openssl des3 -k $pass -d -in dokuwiki_data_backup.tar.gz.enc -out dokuwiki_data_backup.tar.gz

    # check for errors
    if [ $? -eq 0 ] ; then
      # remove directory
      rm -rf data

      # unzip
      gunzip dokuwiki_data_backup.tar.gz

      # untar
      tar xf dokuwiki_data_backup.tar
      chmod -R a+rw data
      rm dokuwiki_data_backup.tar
      rm dokuwiki_data_backup.tar.gz.enc
    else
      rm dokuwiki_data_backup.tar.gz
    fi
    cd -
    echo ""
  fi
else
  echo ""
fi

echo "<html><body><script>history.back();</script></body></html>"

 

Anmerkungen:

  1. Um die Optik aufzubessern, kann man natürlich den Text Entschlüsseln/Verschlüsseln durch eine schöne Grafik austauschen.
  2. Es macht auch Sinn, die verschlüsselte Datei außerhalb des Webservers, also nicht im httpdocs-Verzeichnis, abzulegen.
  3. Ein Backup der Wiki-Daten ist durch die einzelne, verschlüsselte Datei natürlich auch recht einfach.

 

 

Categories
IT Unix

Debian: IPv4 bei dselect erzwingen

Ich hatte auf unserem neuen Root Server das Problem, dass dselect beim Zugriff auf security.debian.org immer auf die IPv6 Adresse gegangen ist, und es hier zu Timeouts kam.

Eine host-Abfrage lieferte folgendes Ergebnis:

$ host security.debian.org
security.debian.org has address 212.211.132.250
security.debian.org has address 212.211.132.32
security.debian.org has address 195.20.242.89
security.debian.org has IPv6 address 2001:a78:5:1:216:35ff:fe7f:6ceb
security.debian.org has IPv6 address 2001:8d8:580:400:6564:a62:0:2
security.debian.org has IPv6 address 2001:a78:5:0:216:35ff:fe7f:be4f
security.debian.org mail is handled by 10 chopin.debian.org.

Es kommen also beide Adressen IPv4 und IPv6 zurück.

Um die Verwendung der IPv4 Adresse zu erzwingen, muss man in der /etc/gai.conf folgende Zeile einfügen:

precedence ::ffff:0:0/96 100

Anschließend wird immer die IPv4 Adresse verwendet.

Categories
Allgemein

Neuer Doppel DIN Radio für Opel Zafira B

Nachdem ich mit dem Funktionsumfang des Original-Radios für meinen Opel Zafira CD30MP3 nicht mehr zufrieden war, und ich liebend gerne mehr Funktionen hätte, habe ich mich auf die Suche nach einer Alternative gemacht.

Was mich am meisten beim CD30MP3 stört, ist das er weder Bluetooth hat noch einen AUX- oder USB-Eingang besitzt.

Na ja, und da im Zafira Platz für ein 2-DIN Radio ist, sollte das neue Radio natürlich auch mit meinem S4 zurecht kommen bzw. nette Features für moderne Smartphones bieten.

Auf der Suche bin ich dann auf die Geräte von Eonon gestoßen.
Diese Geräte bieten für wenig Geld einen sehr hohen Funktionsumfang.

Ja und da es da spezielle Geräte für den Zafira gibt, habe ich mir dann bei Amazon das Eonon GM5154 bestellt (400 Euro).
Neben den Standardfunktionen wie Radio, DVD, Bluetooth bietet diese Gerät unter anderem auch Navigation und ScreenMirroring, also die Möglichkeit sein Smartphone komplett auf das Radio zu spiegeln.

Keine Woche später war das Gerät auch schon da 🙂
Also habe ich mich gleich an den Einbau gemacht…

Optisch ist das Gerät einwandfrei. Es passt einfach perfekt in den Zafira. Keine Probleme mit dem Rahmen oder der CAN-Bus Steuerung.
Allerdings hatte ich mit dem Gerät diverse Probleme:

  1. Der Radioempfang war katastrophal! Teilweise hat der Empfang funktioniert aber die meiste Zeit war eigentlich nur Rauschen zu hören.
  2. Ich konnte das Gerät nicht zum Navigieren bringen! Mit eingelegter Speicherkarte konnte ich die Navisoftware starten. Nur wurde, egal was ich probiert habe, der externe GPS Empfänger von Eonon nicht erkannt! Auch der automatischen Suchlauf konnte das Problem nicht lösen.
  3. ScreenMirroring funktionierte auch nicht! Das S4 hat kurz versucht via WLAN die Verbindung aufzubauen, war auch kurz da, und zack wieder weg. Das ganze ging dann ununterbrochen so weiter.

Aufgrund dieser ganzen Probleme habe ich das Gerät dann auch gleich wieder zurück geschickt.

So welches Gerät dann??? Das war die große Frage.

Nachdem ich mir mehrere Tage Zeit genommen habe, um einen Ersatz für das CD30MP3 zu finden, habe ich mich letztendlich für ein Gerät
von Pioneer entschieden.

Das AVH-X8500BT.

Vorweg: Ein Markenprodukt 🙂 …kann ja schon einmal nicht soooo schlecht sein 😉

Das Gerät bietet zwar keine Navigation (zumindest nicht ohne teures Zubehör), aber dafür hat es doch einiges an Anschlüssen und sonstigen Features zu bieten:

o Radio
o CD/DVD
o SD-CARD
o 2x USB
o AUX-IN
o Bluetootho 7 Zoll Display
o HDMI Eingang
o Eingang für eine Rückfahrkamera
o Radio AppMode –> mit dem Feature kann man kompatible Smartphone Apps direkt auf dem Radio nutzen 🙂 Also auch eine   Navigationssoftware wie Waze

Im Unterschied zu der Lösung von Eonon benötigt man allerdings noch einen Einbaurahmen und Adapter für den Zafira.
Ich habe mich hier für das Komplettset 1960 von Dietz entschieden.

Um den AppMode mit meinem S4 nutzen zu können habe ich mir auch noch ein HDMI-Kabel, ein weiteres USB-Kabel (eines ist bei dem Pioneer Radio dabei) und den HDMI Adapter ET-H10FAU geholt.

Das verlegen der Kabel war allerdings keine leichte Aufgabe 🙂 Es hat aber dann doch alles so funktioniert wie ich es mir vorgestellt habe.
HDMI- und USB-Kabel habe ich hinter die Handbremse verlegt, und am HDMI-Adapter angeschlossen.

Zafira HDMI/USB - Handbremse
Zafira HDMI/USB – Handbremse

Da bei dem Einbauset und beim Radio selber wahnsinnige viele Kabel zusammenkommen, die hinter dem Radio Platz finden müssen, habe ich die Kabel eine Etage weiter nach unten gelegt 🙂 Also hinter die Steuerung für die Klima/Heizung.

Zafira Front zerlegt
Zafira Front zerlegt

Nachdem die Kabel nicht mehr wirklich im Weg waren ließ sich das Radio ohne Probleme in dem vorgesehenen Schacht verstauen.

That’s it 🙂
Es funktioniert alles wie gewünscht und der neue Klang ist wirklich eine deutliche Verbesserung!

Pioneer AVH-X8500BT - Opel Zafira B
Pioneer AVH-X8500BT – Opel Zafira B

Und: Es kann sich sehen lassen 🙂 🙂 🙂

Mit dieser Lösung gibt es aber leider noch zwei Einschränkungen die man aber umgehen kann 🙂

1. Es funktionieren nicht alle Android Apps mit dem Pioneer Radio, eben nur Freigegebene
Damit sämtliche Apps mit dem Radio verwendet werden können muss man die App ARLiberator installieren. Dazu benötigt man allerdings ein gerootetes Gerät (ist es ja schon seit dem ersten Tag 🙂 ) und na ja, man muss ca. 20 Euro ausgeben.
Es lohnt sich aber auf jeden Fall. Denn durch ARLiberator wird das Handy einfach 1:1 auf das Radio gespiegelt. Das kann man zum Beispiel hier recht schön sehen:

2. Viele der Funktionen, unter anderem der AppMode, laufen nur, wenn man auch das Kabel zur Erkennung der Handbremsenstellung eingebaut hat.
Zum Glück gibt es auch hierfür eine Lösung. Allerdings ist diese wieder mit zusätzlichen Kosten verbunden 🙁
Bis zu den 2013er Modellen von Pioneer war es wohl möglich die Handbremsenprüfung mit Hilfe eines Relais zu umgehen. Das funktioniert bei den aktuellen Modellen nicht mehr. Hier muss man auf eine andere Lösung zurückgreifen:

Pioneer Handbremsenerkennung umgehen
Pioneer Handbremsenerkennung umgehen

Ich habe mir diese “Schaltung” bei eBay bestellt. Wie es angeschlossen wird und wie es dann wirklich funktioniert kann man hier sehen:

 

Categories
Allgemein

Lachsforelle – Augenwischerei – Abzocke

Als leidenschaftliche Angler müssen wir hier einmal etwas ganz wesentliches klarstellen:

Die Fischart “Lachsforelle” ist gänzlich unbekannt!

Eine solche Fischart gibt es nicht. Lachsforelle ist eine Marketing-Erfindung.
Meist handelt es sich hier um Regenbogenforellen, die mit besonderem Farbstoff gefüttert werden, damit ihr Fleisch dem Lachsfleisch ähnelt.

Natürlich kostet so eine “Lachsforelle” deutlich mehr als die ordinäre Regenbogenforelle.

 
Hier noch der Wikipedia-Link zur “Lachsforelle”:
http://de.wikipedia.org/wiki/Lachsforelle