HTTP-Authentifizierung in Ruby-Scripten auf dem Apache

HowTo

Dieser Artikel richtet sich an Programmierer, die mittels eines Ruby, Perl oder Python-Scripts in der CGI-Umgebung des Apache-Webservers eine Basic-Authentication durchführen möchten. Entsprechendes Vorwissen wird vorausgesetzt. Das Codebeispiel ist in Ruby gehalten.

Heute sah ich mich mit dem Problem konfrontiert, dass ich in einem Ruby-Script, welches in der CGI-Umgebung im Apache laufen soll eine Nutzer-Authentifizierung via HTTP (Die bekannten Passwortdialoge des Browsers selber) nutzen wollte. Dazu gab es zwar entsprechende Codeschnipsel, wie das funktionieren sollte, allerdings funktionierte keins davon.

Das Problem lag daran, dass der Apache-Server im Standardfall die Header, die für diese Authentifizierung benötigt werden nicht an die Scripte weiter gibt. Dazu gibt es dann die Möglichkeit Startparameter des Apache zu ändern, das CGI-Modul selber zu patchen oder aber die einfache Möglichkeit mit einer .htaccess-Datei.

Die ersten beiden Methoden fallen auf den meisten Webhostingpaketen flach (Mal abgesehen davon, dass kaum irgendwo Ruby unterstützt werden dürfte, das Problem existiert in Perl / Python als CGI ebenfalls.), da man keine Servereinstellungen ändern kann. Auch bei mir wollte ich an meiner normalen Konfiguration so wenig wie möglich ändern und habe somit zur dritten Möglichkeit gegriffen.

Um die fehlende Variable zur Verfügung zu stellen wird ein kleiner Zweizeiler als ".htaccess" im gleichen Verzeichnis wie das Script gespeichert:

RewriteEngine on
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
Das ist schon alles um die Variable hinzuzufügen.

Im Script selber wird das Auslesen der Variable dann schon ein wenig schwieriger. Das möchte ich hier auch nicht mehr ausführlich erklären sondern nur mit einem (eigentlich recht gut verständlichen Script) demonstrieren:

#!/usr/bin/ruby

require 'cgi' require 'base64'

# Extend the CGI class with extractor for basic-auth credentials class CGI # Passes back the credentials as array or nil when no credentials are provided def basic_auth_data if d = %w{REDIRECT_X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION}.inject([]) \ { |d,h| env_table.has_key?(h) and env_table[h].length > 0 ? env_table[h].to_s.split : d } return Base64.decode64(d[1]).split(':')[0..1] if d[0] == 'Basic' end end end

# Here's an example usage: cgi = CGI.new username, password = cgi.basic_auth_data

# If there was no identification force the browser to ask for puts cgi.header({ "Status" => "401 Authorization Required", "Type" => "text/html", "WWW-Authenticate" => "Basic realm=\"My secret Area\"" }) if cgi.basic_auth_data.nil?

# Output control data puts "Content-Type: text/plain" puts

puts "Usr: '#{username}'" puts "Pwd: '#{password}'"

Das Script findet sich mit Syntaxhighlighting und Downloadmöglichkeit auch noch im Nopaste.