class: center, middle ![img33r](ki/elePHPant1.webp) # PHP-Grundlagen #### +Apache, MariaDB und etwas Sicherheit ### Hauke Goos-Habermann #### https://weisheit.goos-habermann.de --- ### Über mich - Entwickler des
Softwareverteilungssystems m23
- Organisator der
Kieler Open Source und Linux Tage
-
Dienstleister zu m23, Linux und Freier Software
- Softwareentwicklung (PHP, BASH, C/C++, JS, Python und was sonst so gebraucht wird...) - Administration - Schulungen - Support - Beratung - quasi **alles**, *was mit Linux zu tun hat* - Wöchentlicher Livestream *"Jean und Hauke Show"* auf https://www.youtube.com/c/LinuxGuides - *"Nicht der Weisheit letzter Schluß"* mit **beruflichen** oder **privaten Projekten** auf
tube.tchncs.de/c/ndwls
und
youtube.com/@nichtderweisheit
- Verwendet Linux, Apache, PHP und MySQL/MariaDB seit über 20 Jahren ;-) --- ### Grundlegender Aufbau ![img20r](gfx/lamp.png) - **L**inux * Beliebige Distribution, die die benötigten Pakete mitbringt - **A**pache * Webserver, der PHP als Modul einbindet - **M**ariaDB * SQL-Datenbank - **P**HP - Steht für "**P**HP: **H**ypertext **P**reprocessor" - Handbuch mit **Beispielen**: https://www.php.net/manual/de/index.php - Bindet MySQL/MariaDB-Client als Modul ein - Kann auch auf der Kommandozeile verwendet werden --- ### Maskottchen: ElePHPant ![img20r](ki/elePHPant2.webp) Frei nach https://www.php.net/elephpant.php: Der ***
ElePHPant
*** ist das liebenswerte, elefantöse Maskottchen des PHP-Projekts. Gelegentlich werden offizielle ElePHPants als Stofftiere angeboten, die von
Vincent Pontier
entworfen wurden. Paßt auf, es sind ***Produktfälschungen*** im Umlauf! ;-) --- ### Apache + PHP installieren ![img20r](ki/elePHPant3.webp) Auf Debian installiert Ihr als **root** folgendermaßen: ```bash # Apache und PHP installieren apt -y install apache2 libapache2-mod-php\ php php-bz2 php-opcache php-xml php-mysql\ php-curl php-mbstring php-sqlite3 php-zip\ php-gd # Debians Standardseite deaktivieren a2dissite 000-default # Apache-Konfiguration neu laden systemctl reload apache2 ``` **Achtung:** Das Standardverzeichnis */var/www/html/* ist direkt im Apache-Quelltext festgelegt und somit auch nach Deaktivierung der Startseite gültig. Im Browser öffnen: http://localhost --- ### PHP-Infoseite Datei für die PHP-Infoseite anlegen: ```bash echo '' > /var/www/html/info.php ``` Im Browser abrufen: http://localhost/info.php ![img80](gfx/phpinfo2.png) --- ### MariaDB installieren ![img20r](ki/elePHPant4.webp) Auf Debian installiert Ihr als **root** folgendermaßen: ```bash apt -y install mariadb-client\ mariadb-common mariadb-server ``` --- ### phpMyAdmin ![img20r](ki/elePHPant5.webp) Mit der **Webanwendung** phpMyAdmin könnt Ihr **MariaDB-Datenbanken** und **Tabellen verwalten**, **bearbeiten**, etc. ```bash # phpMyAdmin herunterladen und entpacken: cd /var/www wget https://files.phpmyadmin.net/\ phpMyAdmin/5.2.1/\ phpMyAdmin-5.2.1-all-languages.zip\ -O /tmp/pma.zip unzip /tmp/pma.zip # Aufräumen rm -r /tmp/pma.zip phpMyAdmin # Versionsnummer aus Verzeichnis entfernen mv phpMyAdmin-*-all-languages phpMyAdmin # Zugriffsrechte für phpMyAdmin auf # Apache-Benutzer und -Gruppe setzen chown www-data:www-data -R phpMyAdmin ``` --- ### Apache-Standardseitenkonfiguration ![img20r](ki/elePHPant6.webp) Jetzt überschreiben wir die Standardseite mit einer neuen Konfiguration: ```bash # Konfigurationsdatei schreiben: echo "
DocumentRoot "/var/www" ServerSignature Off SetEnv PDOPASS test
AllowOverride All
" > /etc/apache2/sites-available/alles.conf # phpMyAdmin-Seite aktivieren: a2ensite alles # Apache-Konfiguration neu laden systemctl reload apache2 ``` Im Browser ausprobieren: http://localhost --- ### Apache: Kein Verzeichnislauflisten mehr ![img20r](ki/elePHPant7.webp) ```bash # Konfigurationsdatei schreiben: echo "
DocumentRoot "/var/www" ServerSignature Off
Options -Indexes AllowOverride All
" > /etc/apache2/sites-available/alles.conf # Apache-Konfiguration neu laden systemctl reload apache2 ``` Fehler: http://localhost Infoseite: http://localhost/html/info.php phpMyAdmin: http://localhost/phpMyAdmin --- ### PHP-Infoseite löschen ![img20r](gfx/achtung.png) Aus Sicherheitsgründen sollte die Seite wieder entfernt werden: ```bash rm /var/www/html/info.php ``` --- ### Zugriffsschutz für WWW-Verzeichnisse Neuen Benutzer *db* in **neuer** Nutzerdatenbank anlegen: ```bash htpasswd -c /etc/phpmyadmin.htpasswd db ``` Zusätzlichen Benutzer *db2* in **vorhandener** Nutzerdatenbank hinzufügen: ![img20r](gfx/info.png) ```bash htpasswd /etc/phpmyadmin.htpasswd db2 ``` Benutzer *db* aus Nutzerdatenbank entfernen: ```bash htpasswd -D /etc/phpmyadmin.htpasswd db ``` Zugriff auf */var/www/phpMyAdmin* schützen: ```bash echo 'AuthUserFile /etc/phpmyadmin.htpasswd AuthName GeschlosseneGesellschaft AuthType Basic
require valid-user
' > /var/www/phpMyAdmin/.htaccess ``` --- ### Zugriffsschutz testen ![img20r](ki/elePHPant8.webp) Im Browser abrufen: http://localhost/phpMyAdmin ![img50](gfx/htaccess.png) --- ### PHP-Debug-Modus ![img20r](ki/elePHPant16.webp) Während der **Entwicklungsphase** kann es sinnvoll sein, etwaige **PHP-Fehler**, **-Warnungen** und **-Anmerkungen** direkt in der Ausgabe zu **sehen**. In einer **Produktivumgebung** sollten diese nur in die **Protokolle geschrieben** werden. ```bash # Konfiguration aus Apache php.ini löschen sed -i -e '/^error_reporting.*/d' -e '/^display_errors.*/d'\ /etc/php/*/apache*/php.ini # PHP-Debug AKTivieren echo 'error_reporting = E_ALL display_errors = On' >> /etc/php/*/apache*/php.ini # PHP-Debug DEaktivieren echo 'error_reporting = E_ALL &\ ~E_DEPRECATED & ~E_STRICT display_errors = Off' >> /etc/php/*/apache*/php.ini # Apache-Konfiguration neu laden systemctl reload apache2 ``` --- ### MariaDB-Administrator anlegen MariaDB-Client als **root** starten: ```bash mariadb ``` Neuen Benutzer *admin* mit allen Rechten und Paßwort *ganzGeheimesPassw0rt* anlegen: ```sql CREATE USER 'admin'@'localhost' IDENTIFIED BY 'ganzGeheimesPassw0rt'; GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' WITH GRANT OPTION; FLUSH PRIVILEGES; QUIT; ``` --- ### phpMyAdmin: Konfiguration MariaDB-Server-Zugriffsinformationen in Konfigurationsdatei hinterlegen: ```bash echo ' /var/www/phpMyAdmin/config.inc.php ``` Neu laden: http://localhost/phpMyAdmin --- ### Spielwiese ![img20r](ki/elePHPant11.webp) Wir legen ein Verzeichnis an, in das der normale Benutzer (üblicherweise UID 1000) schreiben kann: ```bash mkdir /var/www/tests chown 1000 /var/www/tests ``` Statt der 1000 kann auch der Benutzername angegeben werden. Das Spielwiesenverzeichnis ist nicht durch .htaccess gesichert. --- ### PHP-Grundstruktur ![img20r](ki/elePHPant12.webp) Das PHP-Skript unter /var/www/tests/test.php speichern: ```php {$zahl}!'); print('');var_dump($zahl); print('');var_dump($wort); ?> ``` Öffnen: http://localhost/tests/test.php --- ### Variablen ![img20r](ki/elePHPant13.webp) Variablen sind in PHP **dynamisch** bzw. **schwach typisiert**. Das bedeutet, daß der **Typ** einer Variablen erst **beim Setzen festgesetzt** wird und sich zur Laufzeit auch ändern kann. ```php $boolean = true; $integer = 23; $float = 1.23; $string = 'Ahoi'; ``` Üblicherweise funktioniert die Automatik, in Problemfällen oder zur Absicherung kann aber auch **explizit** ein **Typ angegeben** werden: ```php ... return((int)$zahl); ... ``` --- ### Arrays (Felder) ![img20r](ki/elePHPant14.webp) Arrays speichern **Schlüssel-/Wertpaare** in einer Struktur, bei der der **Schlüssel** sowohl eine **Zahl** als auch eine **Zeichenkette (assoziative Arrays)** sein können. Speichern als /var/www/tests/array.php: ```php 'Banane', 1 => 'Apfel'); echo(nl2br(print_r($feld,true))); $tier[0] = 'Pinguin'; $tier[1] = 'Elefant'; echo(nl2br(print_r($tier,true))); $gesicht['Pinguin'] = 'Schnabel'; $gesicht['Elefant'] = 'Rüssel'; echo(nl2br(print_r($gesicht,true))); ?> ``` Öffnen: http://localhost/tests/array.php --- ### if, else: Wenn ⇒ dann Mit "**if**" wird je nachdem, ob eine Bedingung erfüllt ist, an der einen oder anderen Stelle im Programm fortgesetzt. "**||**" ist ein logisches ODER, bei dem nur eine Bedingung zutreffen muß, um zu WAHR ergeben. Beim UND "**&&**" hingegen müssen alle Bedingungen WAHR (true) sein, damit der Gesamtausdruck auch WAHR ist und der entsprechende Abschnitt ausgeführt wird. ![img20r](ki/elePHPant15b.webp) Speichern als /var/www/tests/if.php: ```php
= 100) || ($a != 1000)) { echo("
A1: $a
"); } elseif (($a % 2) == 0) echo("
A2: $a
"); else echo("
A3: $a
"); ?> ``` Öffnen: http://localhost/tests/if.php --- ### switch: 1 aus mehreren Möglichkeiten ![img20r](ki/elePHPant16.webp) Bei "**switch**" wird ein Variablen-Wert mit einer **Liste von Möglichkeiten verglichen** und dann die entsprechende Aktion ausgeführt. Speichern als /var/www/tests/switch.php: ```php ``` Öffnen: http://localhost/tests/switch.php --- ### Datenübertragung an Webserver: GET ![img20r](ki/elePHPant17.webp) Mit der GET-Methode werden **Daten** als Teil der **URL** (z.B. http://localhost/tests/get.php?id=12&name=Tux) über Variablen an den Webserver übertragen. **Variableninhalte** müssen **
URL
**-kodiert sein, wenn sie ***"Sonderzeichen"*** (z.B. Leerzeichen, Umlaute, etc.) enthalten. In PHP können diese Daten über die entsprechenden assoziativen Arrays wieder abgefragt werden. Speichern als /var/www/tests/get.php: ```php ". 'Deine Nummer ist '.$_GET['id'].'!'); ?> ``` Öffnen: http://localhost/tests/get.php --- ### Datenübertragung an Webserver: POST ![img20r](ki/elePHPant19.webp) Bei POST stammen die Daten üblicherweise aus einem HTML-Formular, sind nicht in der URL sichtbar und werden als Teil des **HTTP-Datenstromes** übertragen. Speichern als /var/www/tests/post.php: ```php
Name:
``` Öffnen: http://localhost/tests/post.php --- ### Grundlagen: UTF-8 ![img20r](ki/elePHPant9.webp) UTF-8 kodiert Buchstaben, Ziffern und Sonderzeichen in Bitmustern. **Buchstaben** und **Satzzeichen**, die für **englische Texte** gebraucht werden, passen in ein **1 Byte** großes Bitmuster. Über diesen Basissatz hinausgehende Zeichen wie z.B. Umlaute brauchen ein längeres Bitmuster und dementsprechend mehr Bytes zur Speicherung. **Umlaute** und **"ß"** werden z.B. in **2 Bytes** gespeichert. Mehr dazu: https://de.wikipedia.org/wiki/UTF-8 --- ### DB-Grundlagen: utf8mb4_*_ci ![img20r](ki/elePHPant10.webp) **MariaDB** kennt **verschiedene UTF-8-Kodierungen** für die **Optimierungen** der einen oder anderen Art enthalten: * **utf8mb4_general_ci**Vergleiche und Sortieroperationen sind **schneller**, aber berücksichtigen **nicht** alle ***länderspezifische* Spracheigenschaften**. * **utf8mb4_unicode_ci****Langsamer** beim Vergleichen und Sortieren, da länderspezifischen Spracheigenschaften (z.B. wird "ss" wie "ß" sortiert ) berücksichtigt werden.Für europäische Sprachen sollten allerdings keine großen Leistungseinbußen bemerkbar sein. Es werden **maximal 4 Byte pro Zeichen** benötigt. --- ### Datenbank anlegen ![img20r](ki/elePHPant19.webp) Neue Datenbank **"test"** mit ***utf8mb4_general_ci*** oder ***utf8mb4_unicode_ci*** anlegen. --- ### DB-Tabelle anlegen Die Tabelle **"benutzer"** wird mit **Feldern** von verschiedenen Typen angelegt: Je nach Typ können **weitere Eigenschaften** wie z.B. ***A_I*** (auto increment, automatisch hochzählen), ***UNIQUE*** (einzigartiger Wert) oder ***CURRENT_TIMESTAMP*** (aktuelle Zeit) angegeben werden. ![img80](gfx/tabelle_erstellen.png) * **
INT
**: 4-Byte-**Ganzzahl** mit Wertebereich -2147483648 - 2147483647 (bzw. 0 - 4294967295) * **
VARCHAR
**: **Zeichenkette**, Speicherbedarf abhängig vom Zeichensatz + 1 Byte * **
TIMESTAMP
**: **Zeitstempel**, 4-Byte auf 32-Bit und 8-Byte auf 64-Bit-Systemen Feld-Datentypen von MariaDB: https://mariadb.com/kb/en/data-types --- ### Neuen DB-Benutzer anlegen ![img20r](ki/elePHPant18.webp) DB-Benutzer sollten immer nur mit den **minimal benötigten Rechten** ausgestattet werden, um die **Angriffsfläche** zu **verringern**. * **Benutzername**: verwalter * **Hostname**: localhost * **Globale Rechte**: Nichts auswählen * **Rechte auf DB *"test"*** * **Daten** * SELECT * INSERT * UPDATE * DELETE --- ### Datenbankverbindung aufbauen Speichern als /var/www/tests/db.php: ```php query('SELECT * FROM benutzer'); // Das Ergebnis zeilenweise durchgehen foreach ($erg as $zeile) print_r($zeile); // Objekte zerstören, damit der Garbage collector aufräumt $erg = null; $dbo = null; ?> ``` Öffnen: http://localhost/tests/db.php --- ### SQL-Sicherheit ![img20r](ki/elePHPant19.webp) Daten, die durch einen Benutzer (potentiellen Hacker) eingegeben werden, sollten immer mit Vorsicht genossen werden. So kann ein **Angreifer** versuchen, **SQL-Anweisungen einzuschleusen** (Stichwort **
SQL-Injection
**), um Tabelleninhalte zu manipulieren, **Daten** zu **exfiltrieren** oder schlimmstenfalls sogar das ganze **System zu übernehmen**. Anhilfe schaffen **vorbereitete SQL-Anweisungen** (
Prepared Statements
). --- ### Daten einfügen/löschen Die **
PDO
**-Klasse enthält verschiedene Datenbank-Methoden. ```php prepare("INSERT INTO benutzer (name) VALUES (:name)"); // Variable an Parameter des Prepared Statements binden $stInsert->bindParam(':name', $name); // Zeile einfügen $name = 'Tux'; $stInsert->execute(); // Löschen analog // $stDelete = $dbo->prepare("DELETE FROM user WHERE name = (:name)"); $erg = $dbo->query('SELECT * FROM user'); foreach ($erg as $zeile) print_r($zeile); $erg = null; $dbo = null; ?> ``` --- class: center, middle ### Informationen zu mir und meinen Dienstleistungen, m23, ... ### https://goos-habermann.de #### https://goos-habermann.de/Schulung#LinuxOSSHTML