HM-Feedleser (Variante mit file_get_contents - Release-Version 1.04)
Warum und weshalb Newsfeeds nach Möglichkeit niemals unkontrolliert
übernommen und ausgegeben werden sollten, wissen Sie bereits, falls Sie die Seite "Feedreader mit
SimpleXML" nicht übersprungen haben. Nachfolgend noch ein kleines Beispiel.
Ein Image-Tag wie das folgende, eingebunden in einem CDATA-Abschnitt, würde bei fehlender Prüfung von
Images sicherlich problemlos mit durchschlüpfen. Weiterhin würde ein Fehler bei der Ausgabe ausbleiben,
so lange ein referenziertes Image erreichbar wäre. Wird dieses Image nach einer gewissen Zeitspanne
jedoch gelöscht, würde der Event-Handler onError stattdessen den Code laden.
<img src="http://...com/bild.jpg" onerror="Schadcode laden">
Wer seine Seiten so gut es geht absichern möchte, sollte den Content aus CDATA-Abschnitten filtern und Images überprüfen. Das auf dieser Seite vorgestellte Script wandelt, prüft und filtert:
Ergänzender Hinweis zum Thema Sicherheit: Wir empfehlen Ihnen das Formular nur zur Erprobung und für Testzwecke zu benutzen oder in einem geschützten Bereich. Nur so können Sie sich sicher sein, dass Unbefugte keine Feeds aus fragwürdigen oder unsicheren Quellen laden können.
Die jeweiligen Feeds werden mit der PHP-Funktion file_get_contents() eingelesen, wobei der Verbindungsaufbau nur bei Feeds von entfernten Hosts mit Angabe der jeweiligen URL und mit der Angabe des HTTP-Protokolls (http://) erfolgen muss. Für das Öffnen von Feed-Dateien, die unter der eigenen Domain erreichbar sind bzw. innerhalb des eigenen Webspaces liegen, sollte es genügen, den Pfad und den Dateinamen anzugeben.
Der Wert für error_reporting sollte bei der Erprobung auf E_ALL gesetzt werden und ist gegebenenfalls durch eine zweite Zeile mit
ini_set("display_errors", true);
zu ergänzen. Nach der Erprobung sollte diese zweite Zeile wieder entfernt oder auskommentiert werden.
Weiterhin sollte der Wert für error_reporting auf 0 gesetzt werden, um nur noch eigene Fehlermitteilungen
durch das Script ausgeben zu lassen.
Weiterhin empfehlen wir, ohne einschlägige Kenntnisse nur die erforderlichen Werte zu editieren, da
anderenfalls die Funktionalität des Scripts beeinträchtigt sein könnte.
Code der zum HM-Feedleser gehörenden Dateien:
Bitte vor einem Download lesen:
» Script für einen sicheren Feedreader...
Download »
Neuste Version » Zum leichteren Einbinden in bestehende Webseiten wurden bei dieser Version die
Funktionen zu einer Klasse vereint und ausgelagert »
Feedleser 1.06 (PHP Version ab 5.3)
Weitere Versionen »
Code-Listing der Datei feedleser.php
<?php /*------------------------------------------------------------------------------------------------------------------ Software-Name: HM-Feedleser (http://www.coder-welten.de/projekte/feedreader.htm) Beschreibung: PHP-Script für einen sicheren Feedreader mit SimpleXML für RSS- und Atom-Feeds. Einzelheiten: http://www.coder-welten.de/projekte/feedreader-file.htm Autor: Horst Müller Version: Release 1.04 (Variante mit file_get_contents) Datum: 09. Juni 2013 Lizenz: Lizenz für Software - http://www.coder-welten.de/projekte/lizenz-fuer-software.htm Copyright: © 2006/2013 - Verlag Horst Müller - Stendal ------------------------------------------------------------------------------------------------------------------- */ ini_set("user_agent", "Mozilla/5.0 (compatible; HM-Feedleser +".$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"].")"); header("Content-Type: text/html; charset=UTF-8"); /*-- Die folgenden Werte sollten bei Bedarf editiert werden, $seite = "" muss editiert werden. -------------------*/ error_reporting(0); // Der Wert kann nach Erprobung von E_ALL auf 0 gesetzt werden /*-- Die Datei oder die Web-Adresse des zu ladenden Feeds, Datei ohne http:// und URL mit http:// eintragen. -----*/ $seite = "http://www.coder-welten.de/projekte/testfeed.rss"; $linkt = "» Weiterlesen «"; // Linktext für den unteren Link $feedt = "Feed - "; // Für den einleitenden Titel des Feeds, kann auch leer bleiben $maxim = 12; // Maximale Anzahl der Ergebnisse pro Seite $formu = true; // Formular anzeigen gleich "true" oder nicht anzeigen gleich "false" $descr = true; // Description anzeigen gleich "true" oder ausblenden gleich "false" $conte = true; // Content anzeigen gleich "true" oder ausblenden gleich "false" $summa = true; // Summary anzeigen gleich "true" oder ausblenden gleich "false" $maxho = 250; // Maximale Höhe für eingebundene Bilder angeben $maxwe = 250; // Maximale Weite für eingebundene Bilder angeben define("SCHLIESSE", ">"); // Für valides HTML je nach Dokumenttyp-Deklaration bei HTML ">" oder bei XHTML " />" /*------------------------------------------------------------------------------------------------------------------ Nachfolgend der HTML-Header der Seite. Sollte das Script in eine andere HTML-Seite eingebunden werden, so ist zu berücksichtigen, dass vor ini_set und header() keine Ausgabe von HTML erfolgen darf, auch keine Ausgabe von Quelltext oder Leerzeichen. ------------------------------------------------------------------------------------------------------------------- */ ?> <!DOCTYPE html> <html> <head> <title>Feedreader</title> <?php echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"feedstyle.css\"".SCHLIESSE."\n"; ?> </head> <body> <h1>Feedreader für RSS und Atom</h1> <?php /*------------------------------------------------------------------------------------------------------------------ Ab hier sollte ohne einschlägige Kenntnisse nichts mehr editiert werden. Das Formular für den Aufruf von Feeds von unterschiedlichen Web-Adressen kann bei Bedarf über den Wert von $formu ein- oder ausgeblendet werden. ------------------------------------------------------------------------------------------------------------------- */ if ($formu == true) { if (file_exists("feedformular.php")) { include_once "feedformular.php"; } else {echo "Formular konnte nicht geladen werden!\n"; } } /*-- Die Funktion wandelt Tags und behandelt Entities außerhalb von CDATA-Abschnitten, falls erforderlich. ------*/ function wandleTags($daten) { $wandle = array( "content:encoded" => "content", "dc:creator" => "creator", "dc:date" => "published" ); $daten = strtr($daten, $wandle); $daten = preg_replace_callback("/(&[#a-z0-9]+;)/", function($enti) { return htmlspecialchars(mb_convert_encoding($enti[1], "UTF-8", "HTML-ENTITIES")); }, $daten); /*-- Für den Fall, dass der Feed nur als String ohne Zeilenumbrüche ausgeliefert wird. ----------------------*/ if (strpos($daten, "><item>") !== false) { $eing = array( "><item>" => ">\r\n<item>", "><title>" => ">\r\n<title>", "><link>" => ">\r\n<link>", "><description>" => ">\r\n<description>", "><content>" => ">\r\n<content>", "><summary>" => ">\r\n<summary>", "><enclosure>" => ">\r\n<enclosure>", "><pubDate>" => ">\r\n<pubDate>", "><updated>" => ">\r\n<updated>", "><published>" => ">\r\n<published", "><author>" => ">\r\n<author>", "><creator>" => ">\r\n<creator>" ); $daten = strtr($daten, $eing); } return $daten; } /*------------------------------------------------------------------------------------------------------------------ Die Funktion filtert HTML-Tags innerhalb und außerhalb von CDATA-Abschnitten und wandelt erlaubte Tags in BBCode. Weiterhin findet eine erste Überprüfung von Images statt, falls $image nicht gleich false. ------------------------------------------------------------------------------------------------------------------- */ function filtereHTML($daten, $image) { $daten = preg_replace("/<p.*?>(.+?)<\/p>/is", "$1[br]", $daten); $daten = preg_replace("/<div.*?>(.+?)<\/div>/is", "$1[br]", $daten); $daten = preg_replace("/<span.*?>(.*?)<\/span>/is", "$1", $daten); $daten = preg_replace("/<br.*?>/i", "[br]", $daten); $daten = preg_replace("/<a href.+?>(.*?)<\/a>/is", "$1", $daten); if ($image === true) { $daten = preg_replace("/<img.*?src=\"([a-z0-9_\/=.:;&?-]+?)\.(jpg|png).*?>/is", "[img]$1.$2[/img]", $daten); } $eing = array( "<b>" => "[b]", "</b>" => "[/b]", "<i>" => "[i]", "</i>" => "[/i]", "<em>" => "[i]", "</em>" => "[/i]", "<ul>" => "[ul]", "</ul>" => "[/ul]", "<li>" => "[li]", "</li>" => "[/li]", "<strong>" => "[b]", "</strong>" => "[/b]", "{" => "", "}" => "" ); $daten = strtr($daten, $eing); $daten = preg_replace("/<.+?>/is", "", $daten); return $daten; } /*------------------------------------------------------------------------------------------------------------------ Die Funktion wandelt erlaubte BBcode-Tags in HTML-Tags zurück und überprüft Images. Falls Bilder die angegeben Höchstmaße überschreiten, werden diese gleichmäßig skaliert. ------------------------------------------------------------------------------------------------------------------- */ function formeHTML($daten) { $eing = array( "[br]" => "<br".SCHLIESSE, "[b]" => "<b>", "[/b]" => "</b>", "[i]" => "<em>", "[/i]" => "</em>", "[ul]" => "<ul>", "[/ul]" => "</ul>", "[li]" => "<li>", "[/li]" => "</li>" ); $daten = strtr($daten, $eing); $daten = preg_replace("/[\n]/", "\n\t", $daten); $daten = preg_replace_callback("/\[img\](.+?)\[\/img\]/", function ($bilder) { @$format = getimagesize($bilder[1]); if($format != false) { if ($format["mime"] == "image/jpeg" or $format["mime"] == "image/png") { global $maxho, $maxwe; $height = $format[1]; $width = $format[0]; if ($height > $maxho){ $height = $maxho; $prozent = ($format[1] / $height); $width = ($format[0] / $prozent); } if ($width > $maxwe){ $width = $maxwe; $prozent = ($format[0] / $width); $height = ($format[1] / $prozent); } return "<img src=\"".htmlspecialchars($bilder[1], ENT_QUOTES)."\" alt=\"Bild\"". " height=\"".round($height)."\" width=\"".round($width)."\"".SCHLIESSE; } } }, $daten); return $daten; } /*-- Die Funktion wandelt Datum und Uhrzeit in ein lesbares Format um. -------------------------------------------*/ function formeDatumZeit($daten) { $daten = preg_replace("/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/", "$3.$2.$1 um $4:$5 Uhr", $daten); $daten = substr($daten, 0, 23); return $daten; } /*-- Die Funktion prüft enthaltene Links auf unerlaubte Zeichen und entfernt diese erforderlichenfalls. ----------*/ function filtereLinks($daten) { if (($pos = strpos($daten, "#")) !== false) { $daten = substr($daten, 0, $pos); } $daten = preg_replace("/[^a-z0-9_\/=.:;&?-]/is", "", $daten); return $daten; } $link = false; // Bekanntmachung der Variablen $link, $lesen und $enco sowie Startwert von $si $lesen = false; $image = false; // $image false zuweisen $enco = ""; $si = 0; echo "<div class=\"feed\">\n"; /*-- Der Feed wird geladen und im Anschluß erfolgt die Ausgabe des Feeds -----------------------------------------*/ if ((@$data = file_get_contents($seite)) != false) { $pxml = simplexml_load_string(wandleTags($data), "SimpleXMLElement", LIBXML_NOCDATA); /*-------------------------------------------------------------------------------------------------------------- Auswählen, ob Feedtitel und Eintritt für RSS oder für Atom, wobei channel->title und channel->item der RSS 2.0 Specification für Feeds entspricht, title und entry hingegen der Specification für Atom-Feeds und item RSS 1.0 --------------------------------------------------------------------------------------------------------------- */ if ($pxml->channel->title) echo "<h2>".$feedt.formeHTML(htmlspecialchars(filtereHTML($pxml->channel->title, $image), ENT_QUOTES))."</h2>\n"; if ($pxml->title) echo "<h2>".$feedt.formeHTML(htmlspecialchars(filtereHTML($pxml->title, $image), ENT_QUOTES))."</h2>\n"; if ($pxml->channel->item) $lesen = $pxml->channel->item; if ($pxml->entry) $lesen = $pxml->entry; if ($pxml->item) $lesen = $pxml->item; /*-------------------------------------------------------------------------------------------------------------- Kontrolle, ob zumindest das erste oder das zweite Item einen Titel enthält. Falls dies wie erwartet der Fall sein sollte, werden noch einmal alle Titel einzeln auf Vorhandensein überprüft. --------------------------------------------------------------------------------------------------------------- */ if (strlen($lesen[0]->title) > 0 or strlen($lesen[1]->title) > 0) { foreach ($lesen as $nachricht) { if ($nachricht->title != false) { /*-- Auswählen, ob Link für RSS $nachricht->link oder Atom $nachricht->link->attributes() --------*/ if ($nachricht->link) { if ((string)$nachricht->link) { $link = $nachricht->link; } elseif ($nachricht->link->attributes()) { $attr = $nachricht->link->attributes(); $link = $attr["href"]; } } /*-- Titel mit Link ------------------------------------------------------------------------------*/ if ($nachricht->title) { echo "\x20\x20<h3><a href=\"".htmlspecialchars(filtereLinks($link), ENT_QUOTES)."\" target=\"_blank\">". formeHTML(htmlspecialchars(filtereHTML($nachricht->title, $image), ENT_QUOTES))."</a></h3>\n"; } /*-------------------------------------------------------------------------------------------------- Einen Hinweis anzeigen, falls Bilder als Beilage mit enclosure der Nachricht hinzugefügt wurden. --------------------------------------------------------------------------------------------------- */ if ($nachricht->enclosure) { if ($nachricht->enclosure->attributes()) { $url = $nachricht->enclosure->attributes(); if (strpos($url["url"], ".jpg") or strpos($url["url"], ".png")) { $enco = "<br".SCHLIESSE."\n\x20\x20<span class=\"klein\">Beilage Medien: ". htmlspecialchars(filtereLinks($url["url"]), ENT_QUOTES)."</span>"; } } } /*-- true erst einmal nur für description und falls kein Fund, dann auch für content und summary */ $image = true; /*-- description und content für RSS und summary für Atom ----------------------------------------*/ if ($nachricht->description and $descr != false) { echo "\x20\x20<p>".formeHTML(htmlspecialchars(filtereHTML($nachricht->description, $image), ENT_QUOTES))."</p>\n"; if ((strpos((string)$nachricht->description, "<img")) !== false) { $image = false; } } if ($nachricht->content and $conte != false) { echo "\x20\x20<p>".formeHTML(htmlspecialchars(filtereHTML($nachricht->content, $image), ENT_QUOTES))."</p>\n"; } if ($nachricht->summary and $summa != false) { echo "\x20\x20<p>".formeHTML(htmlspecialchars(filtereHTML($nachricht->summary, $image), ENT_QUOTES))."</p>\n"; } /*-- Nur für den Fall, dass false nicht durch description ausgelöst wurde ------------------------*/ $image = false; /*-- Link unten ----------------------------------------------------------------------------------*/ if ($link != false) { echo "\x20\x20<p class=\"unten\"><a href=\"".htmlspecialchars(filtereLinks($link), ENT_QUOTES)."\" target=\"_blank\">". $linkt."</a><br".SCHLIESSE."\n"; } /*-- pubDate für RSS und published und updated für Atom ------------------------------------------*/ if ($nachricht->pubDate) { echo "\x20\x20<br".SCHLIESSE."".htmlspecialchars(substr($nachricht->pubDate, 0, 16), ENT_QUOTES). " um ".htmlspecialchars(substr($nachricht->pubDate, 17, 5), ENT_QUOTES)." Uhr"; } elseif ($nachricht->updated) { echo "\x20\x20<br".SCHLIESSE.formeDatumZeit($nachricht->updated)."\n"; } elseif ($nachricht->published) { echo "\x20\x20<br".SCHLIESSE.formeDatumZeit($nachricht->published)."\n"; } /*-- Autor oder Creator für RSS und Atom ---------------------------------------------------------*/ if ($nachricht->author) { echo " von ".htmlspecialchars(filtereHTML($nachricht->author, $image), ENT_QUOTES).$enco."</p>\n"; } elseif ($nachricht->creator) { echo " von ".htmlspecialchars(filtereHTML($nachricht->creator, $image), ENT_QUOTES).$enco."</p>\n"; } else { echo "\x20\x20".$enco."</p>\n"; } /*-- Trennlinie zwischen den einzelnen Mitteilungen ----------------------------------------------*/ echo "\x20\x20<hr class=\"linie\"".SCHLIESSE."\n"; $si++; if ($si == $maxim) {break; } } } } else { echo "<br".SCHLIESSE."Mehrere Titel oder Items scheinen fehlerhaft zu sein!\n"; } } else { echo "<br".SCHLIESSE."Feed konnte nicht geladen werden!\n"; } ?> </div> </body> </html>