Chci jednoduchou, ale bezpečnou autentizaci v .htaccess!

Zabýval jsem se dnes tím, jak to zařídit, aby zákazníci, kteří přicházejí do speciální sekce mého hostovaného webu (klientské stránky k jednotlivým projektům), byli nějak jednoduše poptáni na heslo. Protože mi opravdu nešlo o nic velkého zvolil jsem obyčejnou Basic autentizaci. Ta má nesporné výhody: protože je opravdu jednoduchá (jde o kontrolu triviálně „zašifrovaného“ a předem sděleného hesla), podporují ji snad všechny prohlížeče, včetně textových, a také je jednoduchá na konfiguraci pomocí .htaccess souboru. Příklad konfigurace následuje:

AuthUserFile /nejaka cesta.../.htaccess.pwd
AuthGroupFile /dev/null
AuthName "Pristup do teto sekce vyzaduje login a heslo"
AuthType Basic
require user zakaznik1

Hesla všech zákazníků jsou uložena v souboru .htaccess.pwd.

Jednoduchost nastavení Basic autentizace je však samozřejmě vykoupena — a teď pevně věřím, že neříkám nic nového — její přílišnou otevřeností. Heslo, které do prohlížeče zadáte, necestuje na server sice v úplně otevřené formě, ale je zakódováno Base64 kódováním, z něhož je lze naprosto triviálním způsobem dostat zase zpět (na unixu k tomu máme dokonce samostatný příkaz base64; další variantou je využít příkaz openssl base64).

V praxi je proto třeba cestu mezi klientem a serverem při zadávání hesla chránit, a to zabezpečením samotného přenosového kanálu, tj. s využitím protokolu HTTPS. Mimochodem, to neplatí jen pro tento extrémní případ Basic autentizace, ale podle mne obecně pro všechny, snad kromě systémů pracujících s jednorázovými hesly (one-time passwords).

Zákazníkům jsem samozřejmě předal odkazy na jejich klientské stránky již zabezpečené, tj. s použitím SSL. Jenže, co když někdo z nich udělá při přepisu adresy chybu, opomene před adresu uvést ono https:// nebo to udělá dokonce schválně? Musíme mít možnost, jak zabezpečenou komunikaci vynutit. Po krátkém googlení narazíte na spoustu zdrojů, které radí použít Apacheovský modul mod_rewrite pro přepsání všech přístupů bez SSL na zabezpečené stránky (např. zde či zde). Příklad konfigurace pro .htaccess následuje:

# /dir/.htaccess
RewriteEngine On
RewriteCond %{SERVER_PORT}!443
RewriteRule ^(.*)$ https://www.nejakaadresa.cz/dir/$1 [R,L]

Kód má přesměrovat veškeré spojení, které přijde na jiný port než port SSL (443), na zabezpečené stránky s HTTPS protokolem. Toto opravdu funguje, ovšem jen do doby, než se pokusíte toto přesměrování zkombinovat s autentizací uvedenou v prvním příkladu.

Apache server je bohužel udělán tak, že autentizace (direktivy modulu mod_auth) má vždy přednost před přesměrováním (mod_rewrite). Výsledkem je to, že pokusíte-li se přijít na nezabezpečenou stránku, jste normálně vyzváni na heslo, to odejde v (téměř) otevřené podobě na server, teprve PAK dojde k přesměrování na zabezpečenou stránku, která po vás ale bude chtít ono heslo podruhé! Jak jistě chápete, tohle není zrovna elegantní chování, o bezpečnosti ani nemluvě.

Automatického přesměrování jsem se musel, po mnoha pokusech, nakonec vzdát. Precedence Apacheovských modulů je jednou daná a nelze ji nijak obelstít. Začal jsem pátrat dál, až jsem narazil (na stránkách jedné kočky, která si říká NerdGirl) na řešení, které na to jde jinak — nesnaží se uživatele automaticky přesměrovat, rovnou ho v případě nezabezpečeného pokusu o přístup nikam nepustí. Mírně upravený kód v .htaccess pak vypadá takto:

<IfModule mod_ssl.c>
  SSLOptions +StrictRequire
  SSLRequireSSL
  SSLRequire %{HTTP_HOST} eq "www.nejakaadresa.cz"
</IfModule>
<IfModule !mod_ssl.c>
  # no non-ssl access
  order allow,deny
</IfModule>

První direktiva testuje, zda máme v Apachi nahrán modul pro SSL komunikaci (mod_ssl). Pokud ano, vynutíme komunikaci pomocí SSL za všech okolností a také zaručíme, abychom se bavili jen s klienty, kteří opravdu chtějí komunikovat s naším serverem (řádek SSLRequire). Všichni ostatní budou zamítnuti chybovým statusem číslo 403. Druhá IfModule direktiva zamítne všechny přístupy v případě, že klient přistupuje pomocí obyčejného protokolu HTTP.

Nyní tedy, v případě, že zákazník opomene použít https v adrese stránek, dostane se mu chybové stránky pro chybu s kódem 403. Je tedy velmi vhodné vytvořit ručně upravenou stránku speciálně pro tyto případy a vysvětlit v ní, co se děje, a jak z problému ven. Pokud by chybová stránka byla dynamická (např. v PHP), šlo by nejspíše uživatele rovnou i automaticky přesměrovat na správnou (zabezpečenou) adresu, s využitím tzv. HTTP Refereru v hlavičkách požadavku. Ale já do toho nešel, mně stačí statická stránka, přenechávám vám to proto za případný domácí úkol… Celý výsledný .htaccess soubor může vypadat např. takto:

<IfModule mod_ssl.c>
  SSLOptions +StrictRequire
  SSLRequireSSL
  SSLRequire %{HTTP_HOST} eq "www.nejakaadresa.cz"
</IfModule>
<IfModule !mod_ssl.c>
  # no non-ssl access
  order allow,deny
</IfModule>
ErrorDocument 403 http://www.nejakaadresa.cz/pristup_odepren.html 
#
AuthUserFile /nejaka cesta.../.htaccess.pwd
AuthGroupFile /dev/null
AuthName "Pristup do teto sekce vyzaduje login a heslo"
AuthType Basic
#
require user zakaznik1

Direktiva ErrorDocument definuje ručně upravený soubor s vysvětlením chyby 403, tj. při odepření přístupu.

Napsal Matouš Borák - 14. listopad 2007, 15:15 do kategorie
   Linkuj.cz - Jagg.cz

Stín v žáru Vašich
informačních potřeb