Kerberos

Mihai Budiu -- mihaib+@cs.cmu.edu
http://www.cs.cmu.edu/~mihaib/

20 iulie 1998

Subiect:
Protocolul de autentificare Kerberos
Cunoștințe necesare:
cunoștințe elementare de matematica, fundamente de Unix
Cuvinte cheie:
autentificare, tichet, parola, securitate, funcție neinversabilă


Contents




Securitate

O definiție largă a noțiunii de ``securitate'' pentru calculatoare ar fi următoarea: interzicerea accesului unor persoane neautorizate la anumite date. Putem distinge două tipuri de restricții: restricții asupra citirii unor date secrete și restricții asupra modificării de către persoane ne-autorizate. Există mecanisme speciale pentru fiecare din aceste scopuri, și există mecanisme care pot fi adaptate amîndurora.

Sisteme sigure

Înainte de a plonja în detalii să observăm că orice schemă de securitate trebuie să înceapă cu securitatea fizică a unor dispozitive de calcul (în sensul că accesul la aparatul însuși este îngrădit). Dacă nu poți avea încredere în nici un calculator, atunci nu poți face nici un fel de comunicație sau stocare de date sigură. Dacă cineva are control asupra sistemul de operare al unui calculator atunci el poate intercepta toate tastele apăsate și toate caracterele scrise pe ecran.

Atunci cînd lucrați pe un calculator trebuie să aveți deplină încredere cel puțin în consola la care lucrați și în celălalt capăt al liniei. Dacă nu puteți garanta aceste lucruri nu trebuie să vă așteptați la nici un fel de garanții de securitate din partea sistemului. Marile bănci au calculatoarele îngropate în niște buncăre subterane extrem de bine păzite; unul dintre motive este, desigur, acela de a nu pierde informații extrem de importante în cazul unei calamități, dar una dintre rațiunile primordiale este garantarea securității fizice a sistemului.

Oricare ar fi scopul pentru care vrem să protejăm date, politica pe care o aplicăm în acest sens va fi o funcție de entitatea care vrea să acceseze datele. De pildă anumite documente militare vor fi secrete pentru gradele mici, dar vor putea fi accesate de un general.

Pentru a putea face astfel de distincții este deci necesar să avem la dispoziție un mecanism de autentificare.

Autentificare

Autentificarea (authentication) este procesul prin care se verifică identitatea unei instanțe (de exemplu un utilizator) care a produs niște date. Cele mai sigure metode de autentificare sunt cele biometrice, care măsoară caracteristici fizice unice ale unui individ (cum ar fi amprentele digitale sau forma irisului). Acestea sunt foarte greu de păcălit. Din (ne)fericire sunt încă foarte costisitoare și puțin răspîndite.

Metoda prin care în mod uzual calculatoarele identifică identitatea unei entități cu care comunică este verificarea că acea entitate știe o informație care foarte probabil nu mai este cunoscută de nimeni altcineva. În sistemele tipice Unix acel secret este parola pe care utilizatorul trebuie să o tasteze înainte de a începe lucrul.

Să mai observăm că pentru a putea vorbi despre autentificare trebuie să avem o noțiune de identitate a utilizatorilor. Calculatorul trebuie să poată distinge cumva între diferiții indivizi care îl folosesc. Trebuie să existe un spațiu de nume pentru utilizatori; autentificarea atunci va pune în corespondență o entitate activă cu un astfel de nume. În Unix numele utilizatorilor autorizați sunt trecute într-o mică bază de date într-un fișier numit /etc/passwd (sistemele mai moderne au scheme mai complicate, dar înrudite); pentru un sistem Unix a autentifica un utilizator constă în a asigna unul dintre aceste nume cunoscute unui set de procese executate de acel utilizator. Dacă un individ nu are un nume în acel fișier atunci el nu există pentru calculator.

Cu alte cuvinte, pentru a putea vorbi despre autentificare trebuie ca părțile implicate în comunicare să aibă un spațiu de nume comun pentru utilizatori.

Criptografie

Dacă întreg sistemul cu care lucrăm este sigur fizic atunci nu avem mare nevoie de autentificare; noțiunea de securitate fizică implică faptul că persoane neautorizate nu pot accesa sistemul. De îndată însă ce datele trebuie să traverseze porțiuni nesigure trebuie să luăm măsuri suplimentare de precauție. Transformarea datelor în scopul de a împiedica accesul persoanelor neautorizate se numește criptare. Criptarea transformă un text inteligibil (plaintext) într-un cifru (ciphertext). Procesul de codificare se numește cifrare sau criptare (encryption) iar procesul invers se numește descifrare sau decriptare (decryption).

Funcții ``ne-inversabile''

Putem vedea criptarea unui mesaj ca o transformare a mesajului. În mod normal această transformare este una funcțională, în sensul că unui mesaj îi corespunde un singur cod. Pentru a putea folosi la ceva mesajele codificate, trebuie să existe și o transformare inversă, prin care dintr-un cifru obținem textul inițial.

Dacă notăm funcția de criptare cu E (de la ``encryption'') și cea de decriptare cu D, pentru un mesaj x trebuie să avem relația D(E(x)) = x. De aici rezultă în primul rînd că funcția E trebuie să fie injectivă (altfel inversa nu este bine definită), cu alte cuvinte oricare două codificări ale unor cuvinte diferite trebuie să fie diferite.

În realitate funcțiile de criptare și decriptare mai au un parametru relativ mic (comparat cu lungimea mesajului x, numit cheia de criptare. Cheia este un obiect relativ ușor de descris și manipulat, spre deosebire de funcțiile D și E. E va fi atunci de forma E(x, k), unde k este cheia. D(x, k1) este funcția de decriptare, k1 fiind cheia pentru decriptare.

Se deosebesc două feluri de sisteme de criptare: simetrice, în care k = k1 și asimetrice în care cheile sunt diferite.

Ceea ce este important este că funcțiile D și E sunt cunoscute de toată lumea; atunci cînd vreau să stabilesc un canal sigur cu un alt individ noi trebuie doar să cădem de acord asupra cheilor pe care le folosim (k și k1).

Nu oricare două funcții care satisfac cerințele anterioare sunt bune pentru criptare. Dacă fixăm cheia k și notăm funcția $E(k, \cdot)$ cu Ek, trebuie ca din cunoașterea valorii lui y = Ek(x) (cifrul) să fie practic imposibil de calculat x. Din cauza asta funcțiile de criptare se numesc (nu foarte precis) ``neinversabile'' sau ``greu inversabile'' (one-way functions).

Ce înseamnă de fapt ``imposibil de calculat''? Pentru a răspunde la această întrebare ne trebuie ceva cunoștințe de teoria complexității, pe care o să le omitem din prezentarea de față. În principiu asta înseamnă că orice algoritm care ar calcula cheia k știind y, D și E trebuie să aibă o complexitate mai mare decît polinomială (de exemplu exponențială).

Să observăm că există întotdeauna algoritmi care pot calcula inversa, pur și simplu iterînd prin toate cheile posibile k și văzînd dacă D(y,k) este un cuvînt din limbajul de intrare. Dar dacă cheia are n biți, trebuie încercate 2n combinații diferite. Pentru un n suficient de mare sunt mult prea multe încercări, chiar și pentru cel mai perfecționat supercalculator.

De altfel unul dintre cele mai populare programe pentru ``spart'' parole pentru Unix, Crack, se bazează pe faptul că cheile de fapt nu sunt egal probabile; utilizatorii vor alege cu mare probabilitate anumite cuvinte (pe care le vor mutila în mici feluri), pentru că sunt mai ușor de memorat. ``Crack'' are la dispoziție un dicționar enorm și o suită de reguli pentru a modifica cuvintele, și pur și simplu încearcă toate variantele; deși asta poate părea mult, o limbă umană are în jur de 200 000 de cuvinte, o bagatelă pentru un calculator (comparați de pildă cu 1288=72 057 594 037 927 936, cît ar fi toate combinațiile de 8 caractere ASCII).

P = NP?

Într-un articol mai vechi din PC Report despre algoritmi am prezentat felurite clase de complexitate, printre care și clasele P și NP, ale problemelor a căror soluție se poate calcula în timp polinomial, cu un calculator determinist, respectiv nedeterminist.

Problema P=NP este probabil cea mai importantă problemă deschisă din informatica; de peste 25 de ani s-au depus eforturi susținute pentru a vedea dacă există probleme care se pot rezolva cu mașini nedeterministe (care ``ghicesc'' o parte din soluție) repede dar nu și cu mașini deterministe (cum sunt toate calculatoarele reale).

Ei bine, pentru a fi rezonabilă, problema aflării cheii cu care este criptat un mesaj nu poate fi mai complicată decît o problemă din clasa NP. Lucrurile se pot justifica perfect riguros; ideea de bază este că cel care cunoaște cheia trebuie să decodifice relativ rapid mesajul primit (dacă decodificarea însăși ar dura foarte mult metoda n-ar mai fi practică). Un străin care posedă însă o mașină (deocamdată fictivă) nedeterministă ar putea folosi mașina pentru a ``ghici'' cheia, după care ar decodifica cu această cheie și ar verifica corectitudinea ghicirii.

De aici rezultă o consecință foarte interesantă: dacă se va demonstra că P=NP nu există criptografie! Atunci orice cifru cunoscut ar putea fi spart în timp polinomial! Dacă nu ați crezut pînă acum că problema P=NP merită atenție, poate acum v-ați schimbat opinia: rezolvarea ei (în sensul confirmării egalității) ar supăra o grămadă de agenții de spionaj și militare.

Desigur, cele spuse anterior consideră că orice problemă care are o complexitate polinomială este ușoară (chiar dacă polinomul este ceva de genul x10000) și că toate problemele exponențiale sunt grele. Este adevărat că 1.0001x depășește cu mult pe x10000, dar asta numai de la o valoare foarte mare a lui x încolo.

Dar să lăsăm pentru moment teoria complexității. Să presupunem că $P \not= NP$, căci altfel nu mai avem ce studia, și să aruncăm o privire la modul în care poate fi folosită criptografia pentru a transmite secrete. Treaba asta este cu mult mai grea decît pare.

Pericole

Cînd se proiectează un protocol sigur (de pildă de autentificare) trebuie să știm exact de ce anume vrem să ne păzim. Un model răspîndit, pe care îl vom folosi și în textul de față face următoarele presupuneri:

Este de asemenea important de știut că ușurința spargerii unui protocol crește cu cantitatea de mesaje avute la dispoziție; cu cît mai multe cifruri sunt observate, cu atît mai mult succes pot fi făcute anumite atacuri statistice.

Protocolul Kerberos

Protocolul Kerberos a fost proiectat la Universitatea MIT (Massachusetts Institute of Techonology) în cadrul proiectului Athena, în jurul anului 1984. Scopul protocolului Kerberos este de a permite unui client să-și demonstreze identitatea unui server aflat la distanță, undeva dincolo de o rețea complet nesigură. Protocolul garantează de asemenea că clientul nu poate conversa cu un calculator care se dă drept server; autentificarea se face în ambele direcții.

Protocolul în sine constă dintr-un schimb de mesaje între un client și o serie de servere, fiecare cu o altă misiune. Idea de bază aparține lui Needham și Schroeder care au publicat ideea inițială în 1978 în Communications of the ACM. Descrierea detaliată a protocolului se găsește în documentul numit RFC 1510, care este disponibil de pe Internet1. Cei care administrează Kerberos pot pune întrebări pe grupul de News comp.protocols.kerberos.

Protocolul Kerberos indică de fapt o serie de mesaje care trebuie schimbate între părțile care doresc să comunice; unele din mesaje sunt criptate. Ce funcție de criptare/decriptare se folosește teoretic nu contează prea tare, atîta vreme cît funcția este greu inversabilă. Implementările curente folosesc un algoritm standard de criptare numit DES (Data Encryption Standard).

Kerberos este un protocol; pentru a fi util anumite aplicații (cele care au nevoie de comunicație client-server) trebuie modificate pentru a folosi autentificarea oferită de Kerberos. (Aplicațiile modificate sunt atunci numite ``kerberized''). În mod normal într-un domeniu administrativ în care se folosește Kerberos programe ca telnet, rlogin, POP (post-office protocol), fișiere la distanță (AFS), etc. trebuie rescrise în așa fel încît clientul să se autentifice serverului folosind noua metodă (toate aceste aplicații sunt de tip client-server).

Principii

Protocolul pare destul de complicat, dar dacă aveți răbdare o să înțelegeți tot ce se întîmplă; nu e mare știință la mijloc.

Premiza inițială este că există un server central, numit serverul de autentificare (AS), care cunoaște identitățile tuturor clienților posibili. De asemenea, fiecare client a stabilit o parolă secretă pe care și acest server o știe. Parola ajunge la server printr-o cale sigură, de exemplu prin poștă sau printr-un mesager uman (mă rog, sigură cel puțin din punct de vedere software). Parola asta nu este cunoscută de nimeni altcineva, nici măcar de celelalte servere cu care clientul va comunica (de la care va obține serviciile care-l interesează de fapt). Toate celelalte servere din domeniul administrativ sunt și ele clienți ai AS: au o parolă unică știută numai de AS și de acel server.

Niciodată parola nu va circula în clar pe rețea; atunci oricine ar putea să o citească și să o folosească în locul clientului de drept.

Pentru a se proteja împotriva dușmanilor care vor înregistra sau șterge mesaje, mesajele vor avea informații ca ora la care au fost trimise și un număr de ordine (pentru a detecta omisiunile).

O altă regulă interesantă este că cheile folosite în comunicarea dintre client și servere se schimbă frecvent. Implementarea standard expiră o cheie după 25 de ore. În felul acesta un atac criptanalitic nu va avea prea mult succes: dacă durează mai mult de 25 de ore atunci cheia descoperită este deja inutilă (desigur, eventualele mesaje deja interceptate și stocate vor putea fi citite, dar stricăciunile sunt limitate). Cheia pe care un client și un server o folosesc în comun se numește cheie de sesiune și este generată aleator.

Mesajele (protocolul Needham-Schroeder)

Să presupunem că clientul nostru vrea să vorbească cu un server de disc. Protocolul esențial este compus din 4 mesaje (protocolul real este o simplă extensie a ideii pe care o prezint aici):

  1. Un mesaj de la client spre AS, prin care se indică intenția de a comunica cu serverul de disc;

  2. Un mesaj de răspuns de la AS pentru client, prin care AS îi trimite clientului noua cheie de sesiune și un pachețel pentru serverul de disc;

  3. Un mesaj de la client spre serverul de disc, în care este inclus pachețelul de mai sus, pentru a garanta faptul că clientul a discutat cu AS (pachețelul poate veni numai de la AS);

  4. Un mesaj de răspuns de la serverul de disc prin care clientul este convins că serverul a putut deschide pachețelul, deci că discută într-adevăr cu serverul de disc.

După acest schimb inițial de mesaje clientul și serverul de disc vor folosi cheia de sesiune generată de AS pentru a cripta cu DES toată comunicația dintre ei.

Vom vedea că mesajele sunt relativ încîlcite pentru a preveni toate atacurile de mai sus. Dacă veți încerca să simplificați protocolul aproape sigur îl veți face vulnerabil la anumite tipuri de atacuri.

Să notăm cele 3 entități care colaborează astfel: C clientul, AS serverul de autentificare și S serverul de disc (vedeți și figura 1).

O tehnică importantă a protocolului, care este folosită pentru a contraataca folosirea unor mesaje vechi înregistrate este de a eticheta mesajele cu ora emiterii (într-un mod care nu poate fi contrafăcut) și de a verifica la recepție dacă ora este rezonabilă. Sincronizarea ceasurilor este o problema extrem de grea în sistemele distribuite, așa că pentru Kerberos ``rezonabil'' înseamnă că ceasul local la recepție arată plus/minus 5 minute de ora din mesaj (ora de transmitere).

Un alt truc interesant este folosirea a ceea ce se numește nonce: un obiect care este folosit o singură dată. Acesta este practic un număr aleator. Vom vedea mai jos cum este acesta folosit.

Figure 1: Protocolul de bază (simplificat).
\begin{figure}\centerline{\epsfxsize=8cm\epsffile{baza.eps}}\end{figure}

Vom mai introduce următoarele notații:

Cu aceste notații putem scrie complet ne-ambiguu toate mesajele schimbate între C, S și AS. Săgeata indică traseul fiecărui mesaj:

C ---> AS:
C, S, ora expirare, N (nonce, aleator).

Clientul afirmă propria identitate, identitatea serverului cu care vrea să discute, trimite un număr aleator și cît timp ar dori să converseze cu S. Pînă aici totul e simplu. AS va răspunde astfel:

AS ---> C$:
{KC,S, S, ora expirare, N}KC, {TC,S}KS .

Acest complicat mesaj are mai două părți mari: prima este destinată clientului, și este criptată cu cheia clientului KC, iar a doua este un tichet pentru S, criptat cu cheia lui S. Să vedem la ce folosesc feluritele părți din mesaj:

  • Prima parte a mesajului este criptată cu cheia KC a lui C. Pentru că această cheie este secretă, nimeni altcineva nu poate decripta acest mesaj decît C; pentru restul lumii mesajul este gunoi. La fel stau lucrurile și pentru partea a doua a mesajului, care fiind criptată cu KS este inteligibilă numai pentru S.

  • KC,S este un număr aleator generat de AS, care va fi folosit ca cheie de sesiune între C și S. Observați că cheia de sesiune apare în ambele părți ale mesajului, în așa fel încît va fi cunoscută atît de către C cît și de S.

  • AS îi confirmă lui C identitatea serverului de disc S și indică durata de validitate a lui KC,S (care poate fi diferită decît a dorit C în primul mesaj).

  • Faptul că N apare în mesajul către C garantează că acest mesaj a fost criptat de AS: nimeni altcineva nu putea să-l bage pe N înauntru. De asemenea, acest mesaj nu putea fi un mesaj mai vechi dintr-o comunicație anterioară, pentru că N diferă.

  • Partea a doua a mesajului este opacă pentru C; tot ce poate face C cu ea este să o înainteze lui S. C (sau oricine altcineva) nu o poate citi. Dacă cineva ar modifica partea a doua, S nu ar mai putea-o decodifica și obține un mesaj corect, deci tichetul nu poate fi contrafăcut sau modificat.

C ---> S:
{C, ora curenta, suma de control}KC,S, {TC,S}KS.

Mesajul are din nou două părți.

S ---> C:
{ora curenta+1}KC,S.

Prin acest mesaj S în convinge pe C că a ajuns la destinația dorită: mesajul are ora curentă trimisă anterior de C plus 1. Dar ora putea fi extrasă numai de cel care avea KC,S, care fiind o cheie de sesiune era știută numai de S. Nu era suficient să returneze aceeași valoare, pentru că atunci acest mesaj putea fi o copie a (unei părți a) mesajului anterior.

La sfîrșitul acestei comunicații atît C cît și S sunt siguri de identitatea celuilalt și în plus au la dispoziție o cheie de sesiune KC,S cu care pot cripta toate mesajele pe care le schimbă. Autentificarea a fost făcută.

Implementarea Kerberos

Între un protocol de autentificare pe hîrtie și o implementare reală pe un calculator e o distanță considerabilă. Secțiunea următoare, consacrată slăbiciunilor lui Kerberos va ilustra și mai pregnant acest lucru.

Implementarea lui Kerberos încearcă să mai țină cont de anumite particularități ale lumii reale care fac realizarea protocolului mai dificilă.

Prima întrebare spinoasă care se ivește este: unde este stocată parola fiecărui client KC, KS, etc.). AS trebuie să fie o mașină foarte sigură, undeva într-un subsol ferit, dar mașinile client vor fi probabil peste tot la-ndemînă. Dacă parola unui client este stocată pe disc atunci este mai la-ndemîna atacurilor asupra clientului.

Ca atare Kerberos nu memorează parola nicăieri! Utilizatorul este obligat să o tasteze de fiecare dată cînd vrea să fie autentificat. Observați că KC este folosită în mesajele de mai sus numai pentru a decripta mesajul 2; în rest este inutilă. Deci procedura este următoarea: programul kerberizat al clientului va lua legătura cu AS, iar cînd răspunsul sosește utilizatorul trebuie să tasteze parola KC. Parola este imediat folosită pentru a decripta mesajul de la AS după care este complet ștearsă din memorie. În acest fel fereastra de vulnerabilitate este redusă la maximum.

Pe de altă parte asta poate fi foarte neplăcut, pentru că atunci utilizatorul va trebui să tasteze parola pentru fiecare nou server S pe care vrea să-l folosească (adică pentru fiecare nouă sesiune). Și cum știm că utilizatorii sunt leneși, așa ceva este inadmisibil. Kerberos a introdus atunci un al doilea server central numit ``Serverul care dă tichete'': Ticket Granting Server, TGS. Ideea este TGS are de fapt toate parolele KS. Clientul C se autentifică la TGS exact în același fel ca la oricare server S: obținînd un tichet de la AS. Odată autentificat la TGS și folosind cheia de sesiune KC, TGS clientul poate solicita oricîte chei pentru alte servere S, S1, etc.

Figura 2 și tabela 1 arată situația reală: mesajele 1,2 sunt schimbate numai cînd utilizatorul face login. Mesajele 3,4 sunt schimbate de fiecare dată cînd utilizatorul vrea să contacteze un nou server (adică să deschidă o nouă sesiune). Mesajul 5 este folosit de client pentru a se autentifica fiecărui nou server, iar mesajul 6 este opțional, autentificînd serverul pentru client. În mesajul 5 apare un element nou, numit Ksubsesiune: clientul poate alege aici o nouă cheie de sesiune care să fie folosită în locul cheii oferite de TGS, KC,S. Asta nu schimbă prea tare natura protocolului.

Figure 2: Protocolul Kerberos complet.
\begin{figure}\centerline{\epsfxsize=10cm\epsffile{complet.eps}}\end{figure}


Table 1: Schimbul complet de mesaje în protocolul Kerberos.
h
Nr. Între Conținutul mesajului
1 C ---> AS C, TGS, ora de expirare, N
2 AS ---> C KC, TGS, ora de expirare, N}K_C, {TC,TGS}KTGS
3 C ---> TGS {ora locala}KC,TGS, {TC,TGS}KTGS, S, ora de expirare, N1
4 TGS ---> C {KC,S, S, ora de expirare, N1}KC, TGS, {TC,S}KS
5 C ---> S {ora locala, suma de control,Ksubsesiune}KC,S, {TC,S}KS
6 S ---> C {ora locala}KC,S


Kerberos este implementat sub forma unor procese server (AS, TGS) și a unor biblioteci care se pot lega în programele clienților și serverelor. Funcționează sub o mare varietate de sisteme de operare: Unix și Windows NT fiind cele mai notabile. Mai are tot felul de zorzoane, legate de pildă de autentificarea între domenii administrative diferite, transmiterea tichetelor, etc.

Slăbiciunile lui Kerberos

Chiar dacă în teorie Kerberos este minunat, implementarea lui în practică este cel puțin dificilă. Condițiile ideale existente pe hîrtie sunt greu de obținut într-o rețea de calculatoare reale.

La ora actuală nu există nici un fel de metodă complet riguroasă pentru a arăta ca un protocol criptografic nu scapă informații; există metode pentru a testa dacă un protocol rezistă la atacurile cunoscute, dar foarte adesea se publică algoritmi care mai tîrziu se dovedesc greșiți. În general, raționamentele cu astfel de protocoale sunt foarte complicate. Cercetarea în domeniu este în plină desfășurare și folosește tehnici foarte exotice, ca teoria informației, teoria complexității, logici speciale (ex. knowledge theory), etc.

Voi ilustra aici numai unele dintre deficiențe, pentru a da o idee despre natura lor.

Să observăm că clientul trebuie să păstreze undeva cheile de sesiune pentru a putea conversa cu serverele: fiecare mesaj după cele de autentificare va fi criptat cu aceste chei. Clientul trebuie să posede deci practic permanent KC, TGS și KC,S. E adevărat că aceste chei expiră în 25 de ore, deci sunt mai puțin importante decît o parolă care teoretic este folosită luni întregi. Întrebarea este însă: unde sunt ținute pe calculatorul clientului aceste chei?

Pe o stație obișnuită Unix lucrează în mod normal mai mulți utilizatori. Tichetele unuia ar trebui să fie ferite de ceilalți. Dar pe un sistem Unix practic nimic nu poate fi adăpostit împotriva administratorului (root). Administratorul unui sistem poate citi orice fișier, și poate inspecta memoria fizică a oricărui proces. Acesta este un călcîi al lui Ahile al lui Kerberos; toate metodele cunoscute pentru a penetra un sistem Unix amenință siguranța întregului protocol. Ori securitatea unui sistem Unix, care este foarte complicat, este extrem de greu de controlat; există o sumedenie de breșe de care un atacator ar putea profita.

O altă mare problemă este cu stațiile de lucru fără disc (diskless); aceste stații de obicei importă discuri prin rețea. Deci de îndată ce o astfel de stație stochează un tichet pe disc, tichetul va călători prin rețea, care am stabilit că este expusă la tot felul de atacuri!

Nici păstrarea tichetului în memorie nu este neapărat mai sigură: algoritmii de paginare stochează paginile pe disc (în partiția de swap) atunci cînd calculatorul nu are destulă memorie, deci am revenit la aceeași problemă.

Iată încă un exemplu: am văzut că prospețimea unui tichet este verificată comparînd ora locală a serverului cu ora din tichet. Pentru un interval de 5 minute serverul memorează toate tichetele primite, pentru a depista duplicate, eventual rezultate dintr-un atac care re-transmite pachete vechi capturate. Un tichet mai vechi de 5 minute este considerat expirat și ignorat. În felul acesta un server nu va primi niciodată același pachet de două ori. Asta presupune că serverul și clientul au ceasuri relativ sincronizate.

Într-o rețea mare de calculatoare sincronizarea ceasurilor se face automat, folosind un protocol numit NTP: Network Time Protocol. Un atac foarte spectaculos este următorul: un atacator înregistrează o serie de mesaje de la un client care știe că reprezintă o tranzacție importantă. Peste o săptămînă atacatorul infiltrează în rețea mesaje false NTP prin care setează ceasul unui server cu o săptămînă în urmă. După asta atacatorul retransmite mesajele capturate, care vor fi re-executate, pentru că serverului îi par proaspete.

Asta face securitatea în calculatoare o problemă foarte spinoasă: adesea protocoalele propuse sunt eronate, dar nu există nici o metodă formală pentru a depista și verifica asta. Chiar dacă un protocol este corect formal, se poate baza pe asumpții nerezonabile asupra mediului în care operează, cum ar fi ceasurile sincronizate. Și chiar dacă se bazează pe asumpții rezonabile, implementarea scrisă de un programator uman poate să aibă bug-uri care o fac vulnerabilă.

Kerberos pentru un utilizator

Voi încheia acest articol ilustrînd cum se manifestă Kerberos pentru un utilizator obișnuit. Domeniul administrativ în care lucrez eu de obicei este complet kerberizat. Programul meu login a fost modificat ca imediat ce tastez parola să discute cu AS și să obțină un tichet pentru serverul care dă tichete, TGS. Directorul meu casă este de pe un disc din rețea, aflat undeva departe, pe un server de disc (protocolul folosit este AFS: Andrew File System). Atunci cînd comunic prima oară cu serverul de AFS demonul local de pe mașina mea folosește tichetul obținut de la AS pentru a obține de la TGS un tichet pentru serverul de disc. După aceea se autentifică serverului de disc, exact ca în schema de mai sus.

După autentificarea cu serverul de disc toată comunicația între mașina mea și serverul de disc se va face folosind un alt protocol, numit Secure RPC (Remote Procedure Call): apel sigur de procedură la distanță, care folosește inițial cheia de sesiune oferită de TGS.

Pentru mine ca utilizator final totul este aproape complet transparent. Singura neplăcere este că la fiecare 25 de ore tichetele pentru TGS expiră, și atunci trebuie să-mi tastez din nou parola pentru a obține tichete proaspete. Asta poate fi neplăcut dacă vrei să rulezi o simulare mai îndelungată.

Pot să văd în orice clipă tichetele pe care le posed cu comanda klist:

$ klist
Ticket file:	/tkt/7108-0401-35ae552f
Principal:	me@CS.CMU.EDU

  Issued           Expires          Principal
Jul 20 10:50:34  Jul 21 12:16:55  krbtgt.CS.CMU.EDU@CS.CMU.EDU
Jul 20 10:50:34  Jul 21 12:16:55  afs@CS.CMU.EDU
Jul 20 10:51:30  Jul 21 12:17:51  zephyr.zephyr@CS.CMU.EDU

Tichetele sunt ținute în fișierul /tkt/7108-0401-35ae552f pe discul local. Am în clipa asta 3 tichete: unul pentru TGS, unul pentru serverul de disc AFS și unul pentru sistemul de mesagerie zephyr (care nu ne interesează prea tare acum).

Mai am la dispoziție următoarele comenzi:

kinit
prin care pot să-mi schimb identitatea Kerberos (de pildă dacă un coleg vrea să lucreze pe calculatorul meu o să tasteze kinit numele-lui și apoi parola lui personală;

kdestroy
prin care toate tichetele de pe mașina locală sunt distruse; foarte util dacă plec și nu vreau ca cineva să poată lucra în numele meu;

kpasswd
prin care îmi pot schimba cheia (parola) stocată pe AS. Schimbarea parolei este sigură, pentru că parola va fi trimisă criptat la un server special care face managementul parolelor și modifică baza de date din care citește AS. Autentificarea la managerul de parole se face tot folosind Kerberos. Asta înseamnă că odată ce am o parolă Kerberos (care trebuie introdusă manual în baza de date a lui AS) schimbarea o pot face fără să mai bat vreun administrator la cap;

kdb_init
este o comandă folosită de administrator pentru a crea o nouă bază de date Kerberos atunci cînd pornește serviciul;

kdb_admin
este comanda prin care administratorul adaugă un nou utilizator în baza de date;

kdb_edit
este comanda prin care se pot adăuga noi administratori în baza de date AS: persoane care au dreptul să modifice baza de date.

În plus, în domeniul în care lucrez eu, majoritatea comenzilor care operează în rețea au fost kerberizate. De pildă demonul și clientul de telnet (terminal virtual): cînd eu fac telnet pe o mașină la distanță clientul îmi cere parola după care obține un tichet de la TGS pentru demonul telnetd de pe mașina de la distanță; în acest fel parola mea nu circulă niciodată prin rețea (cum ar fi fost cazul dacă telnet nu era kerberizat).

Concluzie

Securitatea în calculatoare este o problemă de mare importanță economică, mai ales acum cînd tot mai multe tranzacții se fac prin Internet. Proiectarea unui protocol de securitate este o treabă foarte complicată, iar implementarea nu este deloc simplă. Domeniul este în plină cercetare în continuare. Dificultățile sunt amplificate de faptul că lanțul este tot atît de slab cît cea mai slabă verigă, iar verigi sunt destul de multe. Fiți deci cu ochii-n patru.



Footnotes

... Internet1
Într-un articol anterior din PC Report am arătat că standardele privitoare la Internet se publică în astfel de documente numite Request For Comments.