OAuth in der Praxis

Wie Sie OAuth zur Autorisierung verwenden
Kommentare

Was OAuth leisten kann und wie das Protokoll theoretisch funktioniert, haben Sie vergangene Woche gelesen. In diesem zweiten Teil erfahren Sie, wie Sie OAuth praktisch einsetzen können.

Andrew S. Tanenbaum hat in seinem Buch „Computer Networks“ über Standards eine sehr treffende Aussage gemacht [1]: „The nice thing about standards is that you have so many to choose from.“ Im Fall von OAuth möchte ich diese Aussage etwas abwandeln: „Das Schöne am OAuth-Standard ist, dass es so viele Bibliotheken dafür gibt“. Es ist ziemlich egal, welche aktuelle Programmiersprache Sie für Ihr Programm verwenden, mit sehr großer Wahrscheinlichkeit gibt es mindestens eine Bibliothek, die Sie bei der Nutzung von OAuth unterstützt [2].

Twittern mit OAuth

Zurzeit dürfte Twitter das bekannteste und am häufigsten genutzte Web-API mit OAuth-Unterstützung bereitstellen. Dementsprechend soll Twitter auch hier als Beispiel dienen. Für Twitter gibt es noch mehr Bibliotheken als für OAuth [3]. Das Twitter API selbst interessiert hier aber nur am Rande, die Möglichkeit, nach erfolgreicher Autorisierung zum Beispiel, einen Tweet abzuschicken, ist eine nette Zugabe zur Erfolgskontrolle. Ebenso kann zum Beispiel die Ausgabe des Realnamens oder des Twitter-Benutzernamens als Beweis der Autorisierung dienen, da sie der Anwendung, die OAuth nutzt, ja nicht mitgeteilt werden.

Bevor es los geht

Bevor Sie über OAuth auf Twitter zugreifen können, müssen Sie ihre Anwendung bei Twitter registrieren. Dazu gehen Sie auf den URL https://dev.twitter.com/apps/new und füllen das in Abbildung 1 zu sehende Formular aus. Nach dem Abschicken nennt Twitter Ihnen die notwendigen Einstellungen für OAuth (Abb. 2).

Abb. 1: Registrierung der Anwendung bei Twitter

Abb. 2: Die bei Twitter registrierte Anwendung

OAuth im Einsatz: ein Consumer

Als Beispiel für die OAuth-Autorisierung dient die Twitter OAuth Library von Abraham Williams [4]. Laden Sie am besten das komplette Archiv herunter, dann haben Sie außer den benötigten Dateien twitteroauth.php und OAuth.php auch die Dokumentation und das Originalbeispiel zur Verfügung. Das folgende Beispiel basiert auf dem von Abraham Williams, wurde aber stark vereinfacht. Außer der OAuth Library benötigen Sie die von Twitter erhaltenen Client Credentials:

Den Consumer Key und das Consumer Secret. Das Consumer Secret sollte dabei wirklich ein Secret bleiben. Da Sie es als Klartext benötigen, können Sie nicht wie im Fall von Passwörtern lediglich den Hash-Wert speichern, sondern müssen das Klartext-Secret so gut wie möglich vor unbefugten Zugriffen schützen. Zum Beispiel indem Sie es außerhalb des Webroot-Verzeichnisses speichern, sodass der Webserver keinen Zugriff darauf hat. Zuerst müssen Sie der Anwendung den Consumer Key und das Consumer Secret mitteilen, im Beispiel in einer Konfigurationsdatei mit folgendem Inhalt:

define('CONSUMER_KEY', 'v9LxW2qY2hk6n5XcK2XDw');
define('CONSUMER_SECRET', 'hshM05dLztJRadbbjjwyXfzpIA7exSWigywm1sI');
define('OAUTH_CALLBACK', 'http://www.ceilers-news.de/oauthtest/callback.php');

Die angegebenen Daten sind mit Erscheinen des Magazins unbrauchbar, da die Testanwendung nach dem Schreiben des Texts gelöscht wird. Der Einfachheit halber wird die Konfigurationsdatei im Verzeichnis der Anwendung gespeichert, im produktiven Einsatz könnte so ein Vorgehen aber leicht zur Preisgabe des Secrets führen, zum Beispiel wenn die Konfigurationsdatei über eine Diretory-Traversal-Schwachstelle ausgespäht wird. Die Konfigurationsdatei muss ebenso wie die Datei twitteroauth/twitteroauth.php in den weiteren Skripten eingebunden werden.

Namen sind Schall und Rauch

Vorab ein Hinweis zur Benennung der Parameter: Sowohl die Twitter OAuth Library von Abraham Williams als auch die weiter unten eingesetzte PECL-Erweiterung OAuth verwenden für die Temporary Credentials die Bezeichnung Request Token und für die Token Credentials die Bezeichnung Access Token. Wenn man die Verwendung der Parameter betrachtet, ist das nicht einmal ungeschickt:

Mit dem Request Token wird das Access Token angefordert (=request), und mit dem Access Token erfolgt der Zugriff (=access) auf das Web-API. Hier werden trotzdem weitgehend die Bezeichnungen Temporary Credentials und Token Credentials verwendet, da diese aus dem Standard stammenden Begriffe im ersten Teil des Artikels eingeführt und durchgehend verwendet wurden. Ansonsten müssten Sie beim Vergleich mit der Beschreibung des Standards immer wieder zwischen beiden Varianten wechseln, was im Allgemeinen nicht gerade zum Verständnis beiträgt.

[ header = Seite 2: Schritte 1 – 4 ]

Schritt 1: Anforderung der Temporary Credentials

Als Erstes müssen die Temporary Credentials bei Twitter angefordert werden. Das passiert im Skript redirect.php, in dem zuerst ein neues TwitterOAuth-Objekt erzeugt wird:

$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);

Mit diesem Objekt werden dann die Temporary Credentials angefordert:

$temporary_credentials = $connection->getRequestToken(OAUTH_CALLBACK);

Da die Temporary Credentials noch benötigt werden, werden sie in der Session gespeichert:

$_SESSION['oauth_token'] = $temporary_credentials['oauth_token'];
$_SESSION['oauth_token_secret'] = $temporary_credentials['oauth_token_secret'];

Schritt 2: Autorisierung durch den Benutzer

Nun kann der Benutzer zur Autorisierung der Temporary Credentials zu Twitter geschickt werden:

$url = $connection->getAuthorizeURL($_SESSION['oauth_token']);
header('Location: '.$url);

Der Autorisierungs-URL entspricht folgendem Muster:

https://twitter.com/oauth/authenticate?oauth_token=xyz123

Bei Bedarf können Sie hier weitere Parameter anhängen, die Twitter dann nach der Autorisierung mit zurückliefert, zum Beispiel:

https://twitter.com/oauth/authenticate?oauth_token=xyz123&etwas=wichtiges

Der Benutzer wird nun zu Twitter weitergeleitet, wo er dem Wunsch nach Autorisierung zustimmt (oder auch nicht). Nach erteilter Autorisierung wird der Benutzer von Twitter zum Callback URL, im Beispiel zum Skript callback.php, weitergeleitet.

Schritt 3: Anforderung der Token Credentials

Jetzt müssen die Temporary Credentials in die dauerhaften Token Credentials umgetauscht werden. Dazu wird in callback.php mit den Temporary Credentials ein neues TwitterOAuth-Objekt erzeugt, mit dem dann die Token Credentials angefordert werden können:

$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
$token_credentials = $connection->getAccessToken($_REQUEST['oauth_verifier']);

In $_REQUEST[‚oauth_verifier‘] steht der von Twitter erzeugte Verification-Code, an dem Twitter die erfolgreiche Autorisierung erkennt. Die Token Credentials müssen nur noch gespeichert werden, im Beispiel in der Session:

$_SESSION['token_credentials'] = $token_credentials;

Schritt 4: Es gibt keinen Schritt 4

Mit den Token Credentials wird dann in index.php wieder ein neues TwitterOAuth-Objekt erzeugt, über das (endlich/schon) auf das Twitter API zugegriffen werden kann:

$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $token_credentials['oauth_token'], $token_credentials['oauth_token_secret']);

Die Autorisierung wurde erfolgreich durchgeführt, und als erster Test können die Informationen über das Benutzerkonto abgefragt werden:

$antwort = $connection->get('account/verify_credentials');
print_r($antwort);

Das Ergebnis könnte wie in Listing 1 aussehen. Sie können nun beispielsweise einen Tweet absenden:

$antwort = $connection->post('statuses/update', array('status' => 'Dies ist nur ein Testtweet'));

Die Beispielanwendung enthält ein Formular, mit dem Sie beliebige Tweets abschicken können. Ob Twitter sie akzeptiert, ist ein anderes Problem, da es keine Längenprüfung gibt. Zu lange Tweets werden von Twitter zurückgewiesen. Das war auch schon alles, mehr müssen Sie nicht tun, um Ihre Webanwendung durch einen Benutzer bei Twitter autorisieren zu lassen. Sie haben nun vollständigen Zugriff auf das Twitter API.

Jedenfalls soweit die Anmeldung der Anwendung es zulässt: Sie können dort für den Zugriff zwischen Read only, Read and Write und Read, Write and Access direct messages wählen, je nachdem, welche Funktionen Ihre Anwendung nutzen soll. Beachten Sie auch hierbei die Regel des Least Privilege und verlangen Sie nur die Rechte, die Ihre Anwendung wirklich benötigt. Was nicht vorhanden ist, kann auch nicht missbraucht werden. Und vergessen Sie nicht, die vom Benutzer erhaltenen Token Credentials dauerhaft zu sichern, Sie benötigen sie für alle zukünftigen Zugriffe auf das Twitter API in seinem Namen.

stdClass Object
(
[profile_sidebar_border_color] => C0DEED
[protected] =>
[default_profile_image] => 1
[statuses_count] => 1
[created_at] => Thu Oct 27 19:26:53 +0000 2011
[name] => Carsten Eilers
[default_profile] => 1
[profile_use_background_image] => 1
[notifications] =>
[profile_background_image_url_https] => https://si0.twimg.com/images/themes/theme1/bg.png
[utc_offset] =>
[profile_text_color] => 333333
[description] =>
[show_all_inline_media] =>
[follow_request_sent] =>
[contributors_enabled] =>
[following] =>
[verified] =>
[profile_background_image_url] => http://a0.twimg.com/images/themes/theme1/bg.png
[friends_count] => 0
[id_str] => 399612272
[is_translator] =>
[profile_link_color] => 0084B4
[status] => stdClass Object
(
[place] =>
[retweet_count] => 0
[in_reply_to_screen_name] =>
[created_at] => Thu Oct 27 19:27:52 +0000 2011
[retweeted] =>
[in_reply_to_status_id_str] =>
[in_reply_to_status_id] =>
[in_reply_to_user_id_str] =>
[id_str] => 129640475580837888
[truncated] =>
[contributors] =>
[in_reply_to_user_id] =>
 => web
[geo] =>
[favorited] =>
[id] => 1.2964047558084E+17
[coordinates] =>
 => Testtweet..
)

[time_zone] =>
[profile_image_url_https] => https://si0.twimg.com/sticky/default_profile_images/default_profile_3_no...
[location] =>
[screen_name] => em0112
[favourites_count] => 0
[profile_background_color] => C0DEED
[listed_count] => 0
[geo_enabled] =>
[profile_background_tile] =>
[url] =>
[profile_sidebar_fill_color] => DDEEF6
[id] => 399612272
[lang] => en
[followers_count] => 0
[profile_image_url] => http://a2.twimg.com/sticky/default_profile_images/default_profile_3_norm...

Alles ganz einfach

Das sieht immer noch kompliziert aus? Das ist es aber wirklich nicht, wenn Sie sich die Erklärungen aus dem Entwickler Magazin 6.2011 in Erinnerung rufen. Für die obigen Schritte wurden die gleichen Überschriften wie für das dortige Beispiel verwendet, sodass ein Vergleich leichter fällt. Für die drei Schritte zum Erlangen der Autorisierung benötigen Sie nur wenige Zeilen Code:

  1. Die Webanwendung fordert von Twitter die Temporary Credentials an: $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET); $request_token = $connection->getRequestToken(OAUTH_CALLBACK);
  2. Der Benutzer wird zur Autorisierung zu Twitter geschickt: $url = $connection->getAuthorizeURL($token); header(‚Location: ‚.$url);
  3. Die Webanwendung fordert die Token Credentials an: $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $_SESSION[‚oauth_token‘], $_SESSION[‚oauth_token_secret‘]); $token_credentials = $connection->getAccessToken();

Nach diesen drei Schritten können Sie das Twitter API nutzen, ohne jemals die Twitter-Zugangsdaten des Benutzers erfahren zu haben. Um all das, was sich im Hintergrund abspielt, brauchen Sie sich nicht zu kümmern. Ebenso einfach ist die OAuth-Autorisierung mit anderen Programmiersprachen, um die Einzelheiten kümmern sich die Bibliotheken.

[ header = Seite 3: Die andere Seite des Protokolls: ein Provider ]

Die andere Seite des Protokolls: ein Provider

Eine Anwendung bei einem OAuth Provide autorisieren zu lassen, ist recht einfach. Selbst einen Provider bereit zu stellen, über den die Benutzer anderen Anwendungen die Nutzung der Webanwendung in ihrem Namen erlauben können, ist etwas komplizierter. Das folgende rudimentäre Beispiel ist zwar ebenfalls in PHP geschrieben, nutzt aber die PECL-Erweiterung OAuth [5]. Es basiert auf Beschreibungen von Lorna Jane Mitchell [Initial RequirementsPHP OAuth Provider: Request TokensPHP OAuth Provider: Authenticate UserPHP OAuth Provider: Access Tokens [6] und Rasmus Lerdorf [7].

Man nehme…

Der Service-Provider muss fünf Seiten beziehungsweise Funktionen bereitstellen:

  1. Eine Webseite, auf der Clientanwendungen (d. h. die Consumer) angemeldet werden können und auf der sie ihren Consumer Key und das zugehörige Consumer Secret erhalten.
  2. Einen Endpunkt für die Anforderung der Temporary Credentials (Request Token, Schritt 1 im obigen Beispiel).
  3. Eine Webseite, auf der die Benutzer die Consumer autorisieren können (Schritt 2).
  4. Einen Endpunkt für die Anforderung der Token Credentials (Access Token, Schritt 3).
  5. Eine Webseite, auf der die Benutzer die von ihnen autorisierten Anwendungen verwalten können.

1. Registrierung der Consumer

Bei der Registrierung der Consumer, die für jede Anwendung nur einmal notwendig ist, muss jeder angemeldeten Anwendung ein Consumer Key und ein Consumer Secret zugeteilt werden, die aus zufällig erzeugten Strings bestehen. Im OAuth-Standard gibt es keinerlei Vorgaben über Aufbau oder Zufälligkeit von Key und/oder Secret, sodass beide in Abhängigkeit ihrer Notwendigkeiten frei wählen können. Je schützenswerter Ihre Anwendung ist, desto länger und komplexer sollten Key und Secret sein. Ein Angreifer, der diese Werte errät, kann darüber unter Umständen unbefugt auf Ihre Anwendung zugreifen! Eine Möglichkeit zum Bilden der Werte sieht zum Beispiel folgendermaßen aus:

$hash = sha1(mt_rand());
$consumer_key = substr($hash,0,30);
$consumer_secret = substr($hash,30,10);

Statt separater Variablen für Key und Secret können Sie auch ein Array für beide Werte verwenden:

$consumer_key = array(substr($hash,0,30), substr($hash,30,10));

Consumer Key und Consumer Secret müssen sowohl vom Provider (in diesem Fall also Ihrer Webanwendung) als auch vom Consumer (z. B. wie im obigen Beispiel in der Konfigurationsdatei) gespeichert werden.

Je nach Informationsbedarf können Sie weitere Daten abfragen und speichern, zum Beispiel den Namen und eine Beschreibung des Consumers und dessen Callback-Endpunkt sowie Informationen über den Entwickler.

Eine minimale Tabelle für die Speicherung könnte wie in Tabelle 1 aussehen. Sie müssen nun eine Seite erstellen, auf der die Entwickler ihre Anwendungen anmelden können, woraufhin Sie ihnen den von Ihrer Webanwendung erzeugten Consumer Key und das zugehörige Consumer Secret mitteilen.

In Ihrem eigenen Interesse sollten Sie auch Funktionen zum Aktualisieren der Informationen und zum Löschen der Consumer vorsehen, damit Ihre Webanwendung später nicht unnötigerweise Karteileichen mit längst nicht mehr existierenden Consumern mit sich herumschleppt.

Feld

Type

Null

Key

Default

Extra

id

int

NO

PRI

NULL

auto_increment

consumer_key

varchar(30)

NO

 

NULL

 

consumer_secret

varchar(10)

NO

 

NULL

 

created

Timestamp

NO

 

CURRENT_TIMESTAMP

 

Tabelle 1: Die Tabelle für die OAuth Consumer

2. Der Endpunkt für die Anforderung der Temporary Credentials (Schritt 1)

Für die Endpunkte wird immer der gleiche Constructor-Code verwendet (Listing 2). Besonders wichtig ist dabei die Funktion setRequestTokenPath(): Das ist die einzige Funktion beziehungsweise der einzige URL, auf die/den ohne gültige Token Credentials zugegriffen werden kann. Die Callback-Funktionen erfüllen folgende Aufgaben:

$this->provider = new OAuthProvider();

// Namen der Callback-Funktionen:
$this->provider->consumerHandler(array($this,'lookupConsumer'));
$this->provider->timestampNonceHandler(array($this,'timestampNonceChecker'));
$this->provider->tokenHandler(array($this,'tokenHandler'));

// URL, die ohne Token Credentials aufgerufen werden kann,
// um die Token Credentials anzuforndern:
$this->provider->setRequestTokenPath('/pfad/zu/request_token');

// Prüfen, ob der Request gültig ist:
$this->provider->checkOAuthRequest();
  • lookupConsumer() prüft, ob Consumer Key und Consumer Secret gültig sind
  • timestampNonceChecker() prüft ggf., ob sich der Zeitstempel im gültigen Bereich befindet. Außerdem wird zur Abwehr von Replay-Angriffen geprüft, ob der Nonce bereits verwendet wurde.
  • tokenHandler() prüft, ob die Token Credentials korrekt sind.
public function lookupConsumer($provider) {
// 1. Consumer Secret z.B. aus der Datenbank holen
// Vorsicht: SQL-Injection-Gefahr!
$consumer_key = $provider->consumer_key;
$sql = 'SELECT consumer_secret FROM oauth_consumers WHERE consumer_key ='.$consumer_key;
$consumer_secret = [Ergebnis der SQL-Abfrage]
// 2. Consumer Secret prüfen:
if($provider->consumer_secret == $consumer_secret) {
return OAUTH_OK;
} else {
return OAUTH_CONSUMER_KEY_UNKNOWN;
}
}

Die Funktion lookupConsumer() könnte wie in Listing 3 aussehen. Für die Prüfung des Consumer_Secret gibt es als weiteren möglichen Rückgabewert OAUTH_CONSUMER_KEY_REFUSED zur Kennzeichnung unerwünschter Consumer Keys, zum Beispiel jener, die gesperrt oder noch nicht freigegeben wurden. Die Funktion timestampNonceChecker() können Sie frei an Ihre Anforderungen anpassen. Alles von einer aufwendigen Prüfung von Zeitstempel und Nonce bis zur einfachen Rückgabe von OAUTH_OK beim Aufruf ist möglich.

Je aufwändiger die Funktion, desto schwieriger sind Replay-Angriffe. Besteht bei Ihrer Webanwendung keine Gefahr durch Replay-Angriffe, können Sie einfach immer durch Rückgabe vonOAUTH_OK Alles OK melden. Je gefährlicher ein Angriff ist, desto höhere Anforderungen sollten Sie an den Zeitstempel beziehungsweise das gültige Zeitfenster stellen.

Üblich sind Werte von einigen Minuten rund um die aktuelle Serverzeit. Beim Prüfen des Nonce ist die Prüfung dagegen einfach: Entweder er wurde bereits verwendet oder nicht. Hier liegt die Anforderung bei der Erzeugung des Nonces: Je leichter er vorhersagbar ist, desto leichter fallen auch Angriffe. Die FunktiontokenHandler()wird später beschrieben, jetzt folgt erst mal der Code zum Erzeugen der Temporary Credentials (Request Token). Der kann wie in Listing 4 aussehen. Die Tabelle zur Speicherung der Temporary Credentials könnte wie in Tabelle 2 aussehen.

// Da Token und Secret Binärdaten sind, müssen sie z.B. in Hex umgewandelt
// oder URL-kodiert werden:
$request_token = bin2hex($this->provider->generateToken(4));
$request_token_secret = bin2hex($this->provider->generateToken(12));

// Request Token und Secret müssen z.B. in der Datenbank gespeichert werden
...

// Jetzt muss der URL zum Autorisieren erzeugt und ausgegeben werden:
echo 'login_url=http://webanwendung.example/autorisierung.php?request_token='.$request_token.'&request_token_secret='.$request_token_secret.'&oauth_callback_confirmed=true';

Feld

Type

Null

Key

Default

Extra

id

int

NO

PRI

NULL

auto_increment

consumer_key

varchar(30)

NO

 

NULL

 

request_token

varchar(8)

NO

 

NULL

 

request_token_secret

varchar(32)

NO

 

NULL

 

callback

varchar(500)

NO

 

NULL

 

verifier

varchar(20)

NO

 

NULL

 

authorised_user_id

int

NO

 

NULL

 

created

Timestamp

NO

 

CURRENT_TIMESTAMP

 

Tabelle 2: Die Tabelle für die Temporary Credentials

3. Autorisierung durch den Benutzer (Schritt 2)

Jetzt brauchen Sie eine Webseite, auf der die Benutzer Ihrer Webanwendung der Autorisierung zustimmen können, und zwar am oben schon angegebenen URL /autorisierung.php. Temporary Credentials dürfen nur einmal verwendet werden. Bevor Sie die Autorisierung überhaupt starten, müssen Sie daher prüfen, ob die übergebenen Temporary Credentials überhaupt verwendbar sind.

Am einfachsten geht das, wenn Sie in der Datenbank mit den Request Token in einer Spalte die Benutzer-ID speichern. Dann müssen Sie jetzt nur prüfen, ob im Eintrag des übergebenen Request Token eine Benutzer-ID eingetragen ist oder nicht. Ist eine eingetragen, wurde das Token bereits verwendet und die Autorisierung kann abgebrochen werden. Andernfalls bitten Sie den Benutzer nun um seine Autorisierung.

Machen Sie sich dabei bewusst, dass der Benutzer dabei ist, einer Drittanwendung außerhalb Ihrer Kontrolle Zugriff auf seine Daten zu gewähren beziehungsweise ihr erlaubt, in seinem Namen Aktionen durchzuführen. Bedenken Sie auch, welche Folgen das haben kann. Stellen Sie sicher, dass der Benutzer korrekt authentifiziert ist, führen Sie gegebenenfalls eine erneute Authentifizierung durch.

Wenn der Benutzer seine Autorisierung erteilt hat, müssen Sie das durch Eintragen seiner Benutzer-ID in der Tabelle mit den Temporary Credentials protokollieren. Außerdem müssen Sie nun einen Prüfcode (Verification Code) erzeugen, der ebenfalls zusammen mit den Temporary Credentials gespeichert wird.

Dafür reicht in der Regel etwas Einfaches wie substr(sha1(rand()), 0, 6) aus. Hat der Consumer einen Callback URL, schicken Sie den Benutzer dorthin zurück und übergeben dabei den Prüfcode als Parameter, bei einer Übertragung „out of band, geben Sie den Prüfcode auf der Webseite aus, damit der Benutzer ihn an den Consumer übermitteln kann. Lehnt der Benutzer die Autorisierung ab, können Sie die zu den verwendeten Temporary Credentials gehörende Zeile in der Datenbank löschen, da sie ja nicht erneut verwendet werden dürfen.

[ header = Seite 4: Der Endpunkt für die Anforderung der Token Credentials ]

4. Der Endpunkt für die Anforderung der Token Credentials

Nachdem der Benutzer die Temporary Credentials autorisiert hat, muss der Consumer sie gegen Token Credentials austauschen. Dazu sendet er seinen Consumer Key und sein Consumer Secret, die Temporary Credentials (Request Token und Request Secret) und den Prüfcode an die Webanwendung.

Es wird wieder der gleiche Constructor-Code wie in Schritt 2 verwendet (Listing 2). Der Code zum Prüfen des Prüfcodes und zum Erzeugen der Token Credentials kann wie in Listing 5 aussehen. Die Tabelle zur Speicherung der Token Credentials könnte wie in Tabelle 3 aussehen.

// Da Token und Secret Binärdaten sind, müssen sie z.B. in Hex umgewandelt
// oder URL-kodiert werden:
$access_token = bin2hex($this->provider->generateToken(8));
$access_token_secret = bin2hex($this->provider->generateToken(16));

// Jetzt muss aus der Tabelle mit den Temporary Credentials die Zeile mit dem
// übergebenen Request Token und Verification Code geholt werden.
// Aus dieser Zeile wird die Benutzer-ID benötigt, und durch die AND-Verknüpfung
// wird gleichzeitig geprüft, ob der Prüfcode korrekt ist
// Auch hier gilt wieder: Vorsicht, SQL-Injection-Gefahr!
$sql = 'SELECT * FROM outh_request_tokens WHERE request_token = '.$request_token.' AND verifier = '.$verifier;
...
// Danach kann diese Zeile gelöscht werden, da sie nicht mehr benötigt wird
...
// Jetzt können die neuen Token Credentials (Access Token und Access Secret)
// zusammen mit der zugehörigen Benutzer-ID gespeichert werden
$sql = 'INSERT INTO oauth_access_tokens SET access_token = '.$access_token
. ', access_token_secret = '.$access_token_secret
. ', consumer_key = '.$consumer_key '
. ', user_id = '.$user_id';
...

// Jetzt müssen dem Consumer noch die Token Credentials mitgeteilt werden:
echo "oauth_token=".$access_token.'&oauth_token_secret='.$access_token_secret;

Field

Type

Null

Key

Default

Extra

id

int

NO

PRI

NULL

auto_increment

consumer_key

varchar(30)

NO

 

NULL

 

access_token

varchar(8)

NO

 

NULL

 

access_token_secret

varchar(32)

NO

 

NULL

 

verifi

varchar(20)

NO

 

NULL

 

user_id

int

NO

 

NULL

 

created

Timestamp

NO

 

CURRENT_TIMESTAMP

 

Tabelle 3: Die Tabelle für die Token Credentials

Jetzt hat der Consumer korrekte Token Credentials, mit denen er auf Ihre Webanwendung zugreifen kann. Dafür fehlt jetzt nur noch eins: die Funktion tokenHandler() zur Auswertung der Token. Sie kann wie in Listing 6 aussehen. Je nach Status der Token Credentials liefert tokenHandler() unterschiedliche Werte zurück, sodass die Webanwendung nicht nur zwischen gültigen und ungültigen Token Credentials unterscheiden, sondern gegebenenfalls auch eine ausführlichere Fehlermeldung ausgeben kann. So können Sie den Consumer gegebenenfalls darüber informieren, dass der Benutzer seine Autorisierung zurückgezogen hat und weitere Requests zwecklos sind, wenn nicht zuvor eine erneute Autorisierung erfolgt ist.

public function tokenHandler($provider) {
$this->token = [Abfrage der zu $provider->token gehörenden Zeile aus der DB];
if(!$this->token->loaded) {
return OAUTH_TOKEN_REJECTED;
} else
if($this->token->type==1 && $this->token->state==1) {
return OAUTH_TOKEN_REVOKED;
} else
if($this->token->type==0 && $this->token->state==2) {
return OAUTH_TOKEN_USED;
} else
if($this->token->type==0 && $this->token->verifier != $provider->verifier) {
return OAUTH_VERIFIER_INVALID;
}
if ($provider->token_secret = $this->token->secret) {
return OAUTH_OK;
}
}

5. Verwaltung der Anwendungen

Ihre Webanwendung muss den Benutzern natürlich die Möglichkeit bieten, Anwendungen die erteilte Autorisierung zu entziehen oder sie gegebenenfalls einzuschränken (sofern Ihre Webanwendung verschiedene Stufen der Autorisierung enthält). Mit OAuth hat das aber nur am Rande zu tun, da Sie lediglich die Tabelle mit den Token Credentials entsprechend anpassen müssen.

Die API-Seite

Ein Punkt fehlt noch: die Anpassung des API ihrer Webanwendung an OAuth. Im Allgemeinen reicht es, eine einzige Funktion anzupassen, die bisher die Authentifizierung übernommen hat. Übergeben Sie ihr die vom Consumer gelieferten Token Credentials und werten Sie statt der bisherigen Passwortprüfung das Ergebnis von tokenHandler() aus, um fest zu stellen, ob sie gültig sind. Welcher Benutzer den Zugriff autorisiert hat, verrät Ihnen die entsprechende Spalte in der Tabelle mit den Token Credentials.

Fazit

Der OAuth-Standard sieht schlimmer aus, als es die OAuth-Implementierung zeigt. Die Nutzung von OAuth ist für Consumer fast kinderleicht. Für Provider ist das Ganze etwas schwieriger, aber sie haben ja auch eine deutlich größere Verantwortung zu tragen. Immerhin öffnet ein Fehler im OAuth-Provider ebenso wie ein Fehler in der normalen Authentifizierung unter Umständen einem Angreifer Tür und Tor zur Webanwendung.

Insofern ist es natürlich leichtsinnig, dass sämtliche weitergehenden Sicherheitsmaßnahmen hier außer Acht gelassen wurden. Welche Requests und Responses über HTTPS gesendet werden sollten, steht bereits im Entwickler Magazin 6.2011, weitere Hinweise zu Sicherheitsmaßnahmen finden Sie im Kasten „Weitere Sicherheitsmaßnahmen“.

Weitere Sicherheitsmaßnahmen

In der OAuth-Spezifikation RFC 5849 [8] werden eine Reihe von Sicherheitsüberlegungen aufgeführt, die bei der Implementierung von OAuth berücksichtigt werden sollten:

  • Die Sicherheit der RSA-SHA1-Signatur ist von der Sicherheit des privaten Schlüssels des Clients abhängig. Das ist aber kein Argument gegen RSA, denn die Sicherheit der HMAC-SHA1-Signatur ist von der Sicherheit des Shared Secret abhängig, dass von Client und Server geschützt werden muss.
  • Wird der Authorization HTTP Header nicht verwendet, können vertrauliche authentifizierte Daten in Proxies oder Caches gespeichert und eventuell ausgespäht werden. Als Gegenmaßnahme kann z. B. der Cache Control HTTP Header verwendet werden.
  • Die Shared Secrets werden als Klartext benötigt und könnten auf dem Client oder Server ausgespäht werden, sofern keine zusätzlichen Sicherheitsmaßnahmen ergriffen werden. Das Speichern der HASH-Werte, der übliche Schutz für Passwörter, ist in diesem Fall nicht möglich.
  • Die Client Credentials sollten nicht als einziges Identifizierungsmerkmal verwendet werden, da nicht sicher gestellt ist, dass sie keinem Dritten bekannt sind. Sie können z. B. bei Open-Source-Clients aus dem Sourcecode kopiert oder aus Desktopanwendungen auf mit Schadsoftware infizierten Rechnern ausgespäht werden.
  • OAuth stellt keine Möglichkeit bereit, den Gültigkeitsbereich der Autorisierung einzugrenzen. Sind Einschränkungen erwünscht, müssen sie vom Server implementiert werden.
  • Sofern die Datenübertragung nicht über TLS oder SSL geschützt wird, können die Credentials ausgespäht und analysiert werden. Werden sie nicht zufällig genug erzeugt, lassen sie sich eventuell vom Angreifer voraussagen.
  • Mehrere Features von OAuth können unter Umständen für einen DoS-Angriff durch Belegen vieler Ressourcen ausgenutzt werden. So müssen z. B. die ausgegebenen Nonces protokolliert werden. Ein Angreifer, der sich in kurzer Zeit viele Nonces ausgeben lässt, könnte die Kapazität des Servers überlasten.
  • Der Hash-Algorithmus SHA-1 enthält einige Schwachpunkte und sollte eigentlich nicht mehr eingesetzt werden. Der Einsatz in Verbindung mit HMAC sollte noch sicher sein, beim Einsatz mit RSA bestehen Zweifel. Praktisch sind zwar noch keine Angriffe relevant, ihre Möglichkeit sollte aber berücksichtigt werden.
  • Der Signature Base String wurde für den Einsatz mit den spezifizierten Signaturmethoden entwickelt. Werden andere Signaturmethoden verwendet, ist seine Kompatibilität damit zu prüfen. Da nicht der vollständige HTTP Request geschützt wird, sollte geprüft werden, ob weitergehende Schutzmaßnahmen nötig sind.
  • Die automatische Verarbeitung von wiederholten Autorisierungsanfragen durch bereits zuvor autorisierte Clients birgt Gefahren. Wurden die Client Credentials kompromittiert, kann ein Angreifer den Ressource Owner mit den gefälschten Client Credentials zwecks Autorisierung zum Server weiterleiten, der die Autorisierung automatisch genehmigt. Ohne diesen Automatismus müsste der Angreifer sich die Autorisierung über Social Engineering erschleichen.
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -