Verschlüsseln und Verschleiern mit PHP & MySQL

PHP und selbst MySQL bieten von sich aus genügend Möglichkeiten, um Passwörter (oder andere Zugangsdaten) zu speichern. Dabei spielt es keine Rolle, ob eine Verschlüsselung (eine Entschlüsselung ist möglich) oder lediglich eine Verschleierung (Bildung eines Hashs) stattfinden soll.

Auf webmasterpro.de oder php-einfach.de sind Beispiele zur Hash-Bildung unter PHP zu finden. Dies ist ein einfacher und zugleich relativ sicherer Weg, um Zugangsdaten per Hash zu speichern.

Per Hash abgelegte Daten können allerdings nicht wiederhergestellt werden. Um Daten zu kodieren und später wieder zu entschlüsseln, sind Verschlüsselungsalgorithmen wie AES oder (der veraltete) DES nötig. MySQL bietet AES bereits von Haus aus an, was das Verschlüsseln eigentlich recht einfach macht. Mit

SELECT AES_ENCRYPT('secret', 'my_salt');

kann bereits eine verschlüsselte Zeichenkette erzeugt werden. Mit

UPDATE table SET value_crypt = AES_ENCRYPT('secret','my_salt');

wird der verschlüsselte String secret in der Datenbank gespeichert, verstärkt durch den Salz my_salt. Dabei spielt es keine Rolle, ob das Passwort-Salz aus einem Datenbank-Datensatz oder per PHP aus einer externen Datei kommt. Wichtig ist aber, dass das Salz möglichst lang ist (bspw. 1024 Zeichen). Auslesen bzw. Entschlüsseln funktioniert mittels der Funktion AES_DECRYPT():

SELECT CAST(AES_DECRYPT(cryptedValue, 'my_salt') AS CHAR) AS decrypted;

Der CAST ist bei Verwendung im PHP-Kontext dringend nötig, wie die Praxis (nicht nur bei mir) zeigt.

Die Techniken lassen sich natürlich beliebig kombinieren. So verwende ich das Hashing per PHP-crypt() bei reinen Zugangskontrollen (Login in Anwendung XY), da dort keine Dechiffrierung nötig ist. Die Verschlüsselung per AES (MySQL) mit per PHP eingebundener Salz-Datei kommt (zusätzlich) in Systemen zum Einsatz, bei denen die Wiederherstellung der (Zugangs-)Daten auf einfache Weise benötigt wird, z.B. einer Passwortverwaltung.

$PATH-Variable unter Mac OS X verändern

Es ist nötig, die Komponenten eines SDK (Software Development Kit) zum $PATH unter Mac OS X 10.7 hinzuzufügen.

Was ist die $PATH-Variable? $PATH ist eine Umgebungsvariable des Betriebssystems (gibt es unter Linux, OS X und Windows). Dabei enthält $PATH eine Auflistung von Verzeichnissen, die ausführbare Dateien enthalten; unter OS X durch „:“ (Doppelpunkt) getrennt.

Um den aktuellen Wert dieser Systemvariable auszulesen, genügt folgender Einzeiler im Terminal:

$ echo $PATH

Das Ergebnis sieht in etwa so aus:

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

Um nun diese Variable zu bearbeiten, ist es (meines Erachtens) am einfachsten, die Datei ~/.bash_profile zu modifizieren (oder $HOME/.bash_profile). Dazu mit dem bevorzugten Editor diese Datei öffnen, z.B.

$ nano ~/.bash_profile

und die Umgebungsvariable $PATH per export-Kommando ergänzen (in neuer Zeile einfügen):

export PATH=$PATH:/new/dir/location1

Dabei wird der bestehende Pfad um das Verzeichnis „/new/dir/location1“ erweitert. Um nach dem Speichern der Datei den neuen „Pfad“ nutzen zu können, muss entweder ein neues Terminal geöffnet werden oder um das aktuelle Terminal weiter zu benutzen, folgendes Kommando absetzen:

$ . $HOME/.bash_profile

Dadurch wird die geänderte Bash-Konfiguration eingelesen und somit die Einstellungen aktualisiert. Zum prüfen wieder ein

$ echo $PATH

Die Ausgabe sollte nun den aktualisierten Wert enthalten:

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/new/dir/location1

Rechteverwaltung im TYPO3-Backend

Die Rechte der Benutzer des TYPO3-BE können über Zugriffslisten (Access Lists) bis ins Kleinste definiert werden.

Wenn z.B. folgende Meldung beim Bearbeiten eines Seiteninhaltselements erscheint, ist die Darstellung der Dateilinks (File Links) untersagt:

Sie haben nicht die nötigen Rechte, um diese Änderung durchzuführen.
Reason: ERROR: authMode „explicitDeny“ failed for field „CType“ with value „uploads“ evaluated

Standardmäßig ist $TYPO3_CONF_VARS[‘BE’][‘explicitADmode’] auf den Wert ‚explicitDeny‘ eingestellt, wodurch alle Zugriffe erlaubt sind und Verbote ausdrücklich (explizit) gesetzt werden müssen. Entfernt man nun die Markierung von „[Deny] File Links“ und speichert die Einstellungen, kann die jeweilige Benutzergruppe auch auf das Inhaltselement vom Typ Dateilinks zugreifen.

TYPO3-Rechteverwaltung
Explicitly allow/deny field values

Apache und PHP unter Lubuntu installieren

Apache2 als Webserver und die Skriptsprache PHP5 bilden wohl bei vielen Webworkern den Kern der Arbeit. Somit steht am Anfang die Installation dieser Software-Komponenten.

Die Installation per Terminal gestaltet sich wie folgt:

$ sudo -s
$ apt-get update
$ apt-get install apache2
$ apt-get install php5 libapache2-mod-php5

Um anschließend keine längeren Pfade anzugeben, bietet sich die Einrichtung lokaler Domains an. Dafür gibt es virtuelle Verzeichnisse im Apache. Um beispielsweise die Domain lokal.dev zu erstellen und zu konfigurieren, sind folgende Schritte nötig:

$ nano /etc/apache2/sites-available/lokal.dev.conf
$ a2ensite lokal.dev.conf
$ /etc/init.d/apache2 reload

Die Datei lokal.dev.conf kann als Kopie einer bestehenden Datei erstellt werden. Wichtig sind die Anpassung der Pfade innerhalb des virtualhost-Blocks, damit auch die richtigen Dateien erreicht werden. Mittels a2ensite wird die eben erstellte Konfiguration aktiviert (in /etc/apache2/sites-enabled wird ein Link auf eben diese Datei gesetzt). Anschließend muss die Server-Konfiguration neu eingelesen werden.

Nun muss diese „Domain“ noch auf den lokalen Lookup umgeleitet werden, damit der Browser nicht im weltweiten Netz danach sucht:

$ nano /etc/hosts
dort "127.0.0.1 localhost lokal.dev" ergänzen

Das war’s schon ..

Interessante Links dazu sind:

Automatische Systemaktualisierungen unter Debian/Ubuntu

Per Cron (crontab) funktioniert das nicht zwingend fehlerfrei. In manchen Fällen werden die zwar Updates heruntergeladen, aber nicht installiert. Es wird in manchen Fällen ein grafisches Interface benötigt:

debconf: kann Oberfläche nicht initialisieren: Dialog
debconf: (TERM ist nicht gesetzt, die Dialog-Oberfläche kann daher nicht verwendet werden.)
debconf: greife zurück auf die Oberfläche: Readline
debconf: kann Oberfläche nicht initialisieren: Readline
debconf: (Diese Oberfläche bedarf eines steuernden Terminals.)
debconf: greife zurück auf die Oberfläche: Teletype

Alternativ ist im Anacron bereits ein entsprechender Mechanismus implementiert: /etc/cron.daily/apt .
Nach Erstellen der Datei /etc/apt/apt.conf.d/02periodic sollte eine tägliche Systemaktualisierung starten. Hier der Inhalt der Datei 02periodic:

APT::Periodic::Enable "1";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::Verbose "2";
APT::Periodic::RandomSleep "11";

Den Hinweis auf diese Möglichkeit fand ich im debianforum.

Using Grunt „The JavaScript Task Runner“

Grunt erleichtert definitiv die Entwicklungsarbeit. Aber wie benutze ich es?

Grunt ist abhängig von node.js bzw. dessen Paketmanager npm. Diese müssen folglich vorher installiert sein.
Im Projektverzeichnis sollte eine Datei package.json vorhanden sein:

{
  "name": "myTool",
  "description": "describe the tool"
}

Danach kann Grunt für das Projekt installiert bzw. aktiviert werden:

$ npm install grunt --save-dev

Dadurch wird neues Verzeichnis node_modules im Projektverzeichnis erstellt und Grunt kann benutzt werden. Die Option –save-dev ergänzt/erweitert die devDependencies in package.json um grunt. Analog für verwendete Grunt-Plugins gilt:

$ npm install grunt-contrib-concat --save-dev
$ npm install grunt-contrib-cssmin --save-dev
$ npm install grunt-contrib-uglify --save-dev
$ npm install grunt-contrib-watch --save-dev

Anschließend sind auch die installierten Plugins einsatzbereit, bspw.

$ grunt watch

Die Konfigurationsdatei Gruntfile.js sieht in etwa so aus:

  // Creating a wrapper
module.exports = function(grunt){
    // Initializing configuration objects
  grunt.initConfig({
      // Reading 'package.json' so that we can use setting given there
    pkg : grunt.file.readJSON('package.json'),
      // specifying the settings for css file minification
    cssmin : {
      backend : {
        expand : true,
        cwd : 'web/verwaltung/css/',
        src : [ '*.css', '!*.min.css' ],
        dest : 'web/verwaltung/css/',
        ext : '.min.css'
      },
      frontend : {
        expand : true,
        cwd : 'web/styles/',
        src : [ '*.css', '!*.min.css' ],
        dest : 'web/styles/',
        ext : '.min.css'
      },
      options: {
        banner: '/*! <%= pkg.name %> - <%= grunt.template.today("yyyy-mm-dd H:M") %> */'
      }
    },
      // specifying the settings for js file minification
    uglify : {
      backend: {
        files: {
          'web/verwaltung/js/be.min.js': ['web/js/jquery.jnotify.js','web/js/shared/dialog-service.js','web/verwaltung/js/app.js']
        }
      },
      frontendApp: {
        files: {
          'web/js/app.min.js': ['web/js/jquery.jnotify.js','web/js/shared/dialog-service.js','web/js/snv.js']
        }
      },
      frontendNgGrid: {
        files: {
          'web/js/angular-gridster.min.js': ['web/js/angular-gridster.js']
        }
      },
      options: {
        banner: '/*! <%= pkg.name %> - <%= grunt.template.today("yyyy-mm-dd H:M") %> */'
      }
    },
      // @see http://www.thegeekstuff.com/2014/10/grunt-contrib-watch-automate/
    watch: {
      backend: {
        files: ['web/verwaltung/css/*.css','web/verwaltung/js/*.js'],
        tasks: ['minifyBE'],
        options: {
          spawn:false,
          event:['all']
        }
      },
      frontend: {
        files: ['web/styles/*.css','web/js/*.js'],
        tasks: ['minifyFE'],
        options: {
          spawn:false,
          event:['all']
        }
      }
    }
  });
    // Loading grunt-packages
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
    // Registering tasks
  grunt.registerTask( 'minifyBE', [ 'cssmin:backend', 'uglify:backend' ] );
  grunt.registerTask( 'minifyFE', [ 'cssmin:frontend', 'uglify:frontendApp', 'uglify:frontendNgGrid' ] );
}

Die mit registerTask() hinterlegten Aufgaben können mittels Konsole auch separat gestartet werden:

$ grunt minifyBE

Dateien in FAL ersetzen

Wer unter TYPO3 CMS die Extension DAM (Digital Asset Management) einsetzte, weis die Vorteile der Funktion „Datei ersetzen“ zu schätzen. Zum jetzigen Zeitpunkt gibt es eine analoge Funktion unter FAL bzw. TYPO3 CMS 6.2 jedoch nicht.

Ein Trick, um die Funktionalität beizubehalten, ist es, eine Datei mit gleichem Namen in das Verzeichnis hochzuladen und eine bestehende Datei somit zu überschreiben. Die Datei-Referenz bleibt bestehen und somit ist das Ziel erreicht. Nach dem Hochladen kann die (neue) Datei natürlich umbenannt werden.

Gefunden im TYPO-Forum.

ownCloud aktualisieren

Nachdem ownCloud die letzten Jahre stabil lief und nun Version 8 veröffentlicht wurde, sollte das Update nun auf einem Hosting-Paket eingespielt werden. Die grundlegenden Schritte habe ich bereits in diesem Post beschrieben, die offizielle Doku speziell auf das Update von 7.0 auf 8.0 gibt es hier.

Soweit durchgespielt, alles prima .. für den Moment. Nach der ersten Anmeldung die Ernüchterung: keine aktivierte Kalender-App, keine aktivierte Kontakte-App. Einzig Dateien, Bilder und Apps werden im Menü gelistet.

Nach einer kurzen Web-Recherche wird klar, dass ich nicht der Erste mit diesem Problem bin. Die Installation der beiden Apps Calendar und Contacts schlägt fehl. Doch weder die Hinweise aus dem OC-Forum noch auf Nabble.com helfen, da ich weder die nötigen Rechte besitze, die für die OCC nötig sind, noch Änderungen an den PHP-Einstellungen wirken.

Folglich musste das Update zurückgerollt werden und Version 7.0.4 verbleiben.

Call to undefined method getCharset() in LocallangXmlParser.php

Nach einem erfolgreichen TYPO3-Update von 4.5 auf 6.2 lief alles soweit. Bis zu dem Zeitpunkt, als im Backend ein neuer Datensatz per Web | Seite erstellt werden sollte (Wizard zum Erstellen neuer Inhaltselemente). Es erschien die Fehlermeldung

Fatal error: Call to undefined method tx_wtdirectory_pi1_wizicon::getCharset() in xxx/typo3_src-6.2.5/typo3/sysext/core/Classes/Localization/Parser/LocallangXmlParser.php on line 41

Nach einer kurzen Suche fand ich die Ursache: der statische Aufruf der Funktion getParsedData().

$LOCAL_LANG = t3lib_l10n_parser_Llxml::getParsedData($llFile, $GLOBALS['LANG']->lang);

Beheben liess sich der Bug folgendermaßen, wie auf TYPO3 Forge nachgelesen werden kann:

$version = class_exists('t3lib_utility_VersionNumber')
 ? t3lib_utility_VersionNumber::convertVersionNumberToInteger(TYPO3_version)
 : t3lib_div::int_from_ver(TYPO3_version);
if ($version >= 4007000) {
 $object = t3lib_div::makeInstance('t3lib_l10n_parser_Llxml');
 $LOCAL_LANG = $object->getParsedData($llFile, $GLOBALS['LANG']->lang);
} else {
 $LOCAL_LANG = t3lib_div::readLLXMLfile($llFile, $GLOBALS['LANG']->lang);
}

Auch Powermail lieferte unter Version 1.6.11 eine ähnliche Fehlermeldung:

Fatal error: Call to undefined method TYPO3\CMS\Core\Utility\GeneralUtility::readLLXMLfile() in xxx/typo3conf/ext/powermail/pi1/class.tx_powermail_pi1_wizicon.php on line 79

Hier lag es aber nicht an der statischen Einbindung, sondern noch an einer Versions-Unverträglichkeit:

$LOCAL_LANG = t3lib_div::readLLXMLfile($llFile, $GLOBALS['LANG']->lang);

 

Akkordeon mit TYPO3

Akkordeons eignen sich als Design- bzw. UI-Elemente, um den Benutzer auf wichtige Inhalte zu lenken, ohne beschreibende oder weiterführende Inhalte sofort zur Verfügung zu stellen, aber diese dennoch mit wenig (Zeit-)Aufwand bereitzustellen.

Akkordeon

Zur Umsetzung als Inline-Akkordeon (im Fließtext einzubauen) müssen bspw. folgende Stellen erweitert werden:

  • Template
  • Javascript
  • CSS

Generiert wird das Akkordeon im Typoscript des Main-Templates:

lib.stdheader {
    ...
    # Vererbung von H3:
    91 < .3
    91.fontTag = <h3 class="inlineAccordionToggler">|</h3><div class="inlineAccordionElement">
    ...
}
tt_content.stdWrap {
    ...
    outerWrap = |</div>
    outerWrap.stdWrap.if.equals = 91
    outerWrap.stdWrap.if.value.field = header_layout
}

Dadurch werden Inhaltselemente vom Überschriften-Typ 91 mit einer CSS-Klasse und einem umhüllenden DIV ausgestattet. Die CSS-Klasse hinterlegen wir im eingebundenen Stylesheet:

.inlineAccordionToggler {
    color:#ff7713;
    padding:8px 0;
    margin:0;
    cursor:pointer;
    font-weight: normal;
}
.inlineAccordionActiveToggler {
    font-weight: bold !important;
}
.inlineAccordion {
    border:1px solid #e4e4e4;
    border-width:1px 0;
    margin-bottom:-1px;
    font-size:1em;
}

Momentan kann das Akkordeon aber noch nicht verwendet werden. Dafür muss der Überschriften-Typ 91 per TCEFORM definiert werden:

Web | List | Home => bearbeiten
Ressourcen-Tab => TypoScript-Konfiguration:
TCEFORM.tt_content.header_layout.addItems {
    91 = Inline-Akkordeon
}

Nun kann als Überschriften-Typ Inline-Akkordeon gewählt werden.

tceform

Jetzt fehlt lediglich noch die Javascript-Funktionalität, um das Auf- bzw. Zuklappen bereitzustellen. Bei Verwendung von jQuery könnte das dann so aussehen:

jQuery(document).ready(function(){
    ...
    init_inlineAccordion();
    ...
}
function init_inlineAccordion() {
    var togglers = $('.inlineAccordionToggler');
    if (togglers.length > 0) {
        /**
         * neues Div mit class="inlineAccord" erstellen ..
         * .. und um toggler herum in DOM einfuegen
         */
        $('.inlineAccordionToggler, .inlineAccordionElement').wrapAll('<div class="inlineAccordion"></div>');
        var allPanels = $('.inlineAccordionToggler + .inlineAccordionElement').hide();

        $('.inlineAccordion > .inlineAccordionToggler').click(function() {
            var $this = $(this),
                contentItem = $this.next();
            allPanels.slideUp();
            togglers.removeClass('inlineAccordionActiveToggler');
            if (contentItem.css('display')==='none') {
                // Inhalt einblenden:
                contentItem.slideDown();
                $this.addClass('inlineAccordionActiveToggler');
            }
              return false;
        });
    }
}

Unter Prototype hilft folgendes Snippet:

window.IBSEfehse = {
    inlineAccordion_currentItem: false,
    inlineAccordion_init: function() {
        var togglers = $$('.inlineAccordionToggler'),
            myAccordion = this;
        if (togglers.length > 0) {
            togglers.each(function(toggler,idx){
            myAccordion.inlineAccordion_hideContent(toggler);
                toggler.on('click', function(evt){
                    if (myAccordion.inlineAccordion_currentItem!==false) {
                        if (myAccordion.inlineAccordion_currentItem!==toggler) {
                            myAccordion.inlineAccordion_hideContent(myAccordion.inlineAccordion_currentItem);
                            myAccordion.inlineAccordion_showContent(toggler);
                            myAccordion.inlineAccordion_currentItem = toggler;
                        } else {
                            myAccordion.inlineAccordion_hideContent(toggler);
                            myAccordion.inlineAccordion_currentItem = false;
                        }
                    } else {
                        myAccordion.inlineAccordion_showContent(toggler);
                        myAccordion.inlineAccordion_currentItem = toggler;
                    }
                });
            });
        }
    },
    inlineAccordion_hideContent: function(link){
        var container = link.up(0);
        container.childElements().invoke('hide');
        link.show();
    },
    inlineAccordion_showContent: function(link){
        var container = link.up(0);
        container.childElements().invoke('show');
        this.inlineAccordion_currentItem = link;
    }
};
Event.observe(window, 'load', function() { IBSEfehse.inlineAccordion_init(); });

Das kann in einer bestehenden js-Datei eingearbeitet werden oder separat mittels page.includeJS im TypoScript:

page.includeJs {
    file = xyz.js
}

Das Web und ich