Salta al contenuto
Note per Studenti Caching, autenticazione, tipi MIME e URI in HTTP

Caching, autenticazione, tipi MIME e URI in HTTP

In questa pagina 7

Questa nota raccoglie quattro meccanismi di HTTP che un server o un client reale implementano subito dopo il protocollo base (HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encodingHTTP/1.1 (oggi RFC 9110 e 9112) rende la connessione persistente di default (si chiude solo con "Connection: close"), rende obbligatorio l'header Host (virtual hosting) e introduce i nuovi metodi PUT, DELETE, OPTIONS, TRACE, Expect: 100-continue, richieste di intervalli (206) e Transfer-Encoding: chunked; con la connessione persistente il client deve sapere dove finisce ogni risposta: lunghezza del corpo nell'ordine HEAD/1xx/204/304 senza corpo, Transfer-Encoding chunked, Content-Length, altrimenti fino alla chiusura; il chunked divide il corpo in blocchi preceduti dalla lunghezza in esadecimale, termina con un chunk 0 e un trailer facoltativo, e si decodifica contando i byte dichiarati (non cercando CRLF).HTTP 1.1 - connessioni persistenti, Content-Length e chunked transfer encoding →): la cache, l'autenticazione, i tipi MIME e gli URI. I riferimenti normativi sono RFC 9110 (semantica), RFC 9111 (caching), RFC 7617 e 7616 (autenticazione Basic e Digest), RFC 2045-2046 (MIME), RFC 3986 (URI).

La cache HTTP

Una cache conserva una copia delle risposte per riusarla al posto di contattare di nuovo il server. Riduce il numero di richieste, i tempi di caricamento percepiti e il traffico. Ce ne sono di due tipi:

Una cache memorizza di norma le risposte a GET (e HEAD) con codici come 200, 301, 404. La chiave è metodo più URI, a cui si aggiungono gli header elencati in Vary. Le risposte alle POST e quelle con Cache-Control: no-store non si riusano.

Freschezza

Una risposta memorizzata è fresca finché la sua età è minore della sua durata di freschezza. Finché è fresca la cache la serve senza contattare il server. La durata si ricava, in quest'ordine:

  1. dalla direttiva Cache-Control: max-age=N (secondi di vita dalla generazione della risposta; s-maxage vale per le sole cache condivise);
  2. dall'header Expires (una data assoluta, formato Sun, 06 Nov 1994 08:49:37 GMT);
  3. altrimenti da un'euristica: se c'è Last-Modified una cache può usare circa il 10% del tempo trascorso fra Last-Modified e Date (RFC 9111 par. 4.2.2).

L'età si calcola dalla data di generazione (Date) e dall'header Age che le cache intermedie aggiungono con i secondi già passati nella loro cache. Esempio. Risposta con Date: 10:00:00 e Cache-Control: max-age=3600: alle 10:30 è fresca (età 1800 s < 3600) e viene riusata senza richieste; alle 11:05 (età 3900 s) è scaduta (stale) e va rivalidata. Esempio con l'euristica: un file modificato 10 giorni prima senza max-age né Expires può essere considerato fresco per circa 1 giorno.

Rivalidazione

Quando la copia è scaduta non si butta via: si chiede al server se è ancora buona, con una richiesta condizionale. Servono dei validatori, che il server manda nella risposta originale:

validatore (risposta) header condizionale (richiesta successiva) significato
Last-Modified: <data> If-Modified-Since: <data> "mandami il corpo solo se è cambiato dopo questa data"
ETag: "abc123" If-None-Match: "abc123" "mandami il corpo solo se la versione non è abc123"

L'ETag (entity tag) è un identificatore opaco della versione della risorsa (di solito un hash del contenuto, oppure data e dimensione del file), scritto fra virgolette; con il prefisso W/ (W/"abc123") è un validatore debole (stesso significato, non necessariamente stessi byte). Rispetto alla data ha due vantaggi: ha una risoluzione migliore del secondo e funziona anche se la data di modifica non è affidabile.

http
GET /logo.png HTTP/1.1
Host: www.example.com
If-None-Match: "abc123"

Se la versione corrente ha ancora quel ETag, il server risponde:

http
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600

Il 304 non ha corpo: la cache riusa la copia memorizzata e riparte con una nuova durata di freschezza (dagli header del 304). Il risparmio è tutto il trasferimento del corpo; resta un viaggio di andata e ritorno. Se la risorsa è cambiata il server risponde 200 con il nuovo corpo, il nuovo ETag e il nuovo Last-Modified.

 t = 0       GET /logo.png            ->  200, ETag "abc123", max-age=3600, corpo (si memorizza)
 t = 1800 s  GET /logo.png            ->  servita dalla cache, nessun pacchetto in rete (fresca)
 t = 3700 s  GET /logo.png + If-None-Match: "abc123"   ->  304 Not Modified (si rinnova la freschezza)
 più tardi   (logo cambiato)  If-None-Match: "abc123"  ->  200, ETag "def456", corpo nuovo

La stessa logica serve a evitare i lost update: If-Match: "abc123" in una PUT o DELETE fa eseguire l'operazione solo se la versione corrente è quella letta dal client; altrimenti il server risponde 412 Precondition Failed.

Cache-Control

L'header Cache-Control controlla la cache, in risposta (per il server) e in richiesta (per il client):

direttiva dove effetto
max-age=N risposta fresca per N secondi; max-age=0 = subito scaduta
s-maxage=N risposta come max-age ma solo per le cache condivise
public risposta può essere memorizzata anche da cache condivise
private risposta solo la cache privata dell'utente (non i proxy): per risposte personalizzate
no-cache risposta si può memorizzare ma va rivalidata prima di ogni uso
no-store risposta non memorizzare affatto (dati sensibili)
must-revalidate risposta dopo la scadenza, vietato usare la copia senza rivalidare (neanche se il server è irraggiungibile)
no-cache, max-age=0 richiesta il client vuole una risposta rivalidata (ricarica)

Attenzione al nome: no-cache non vieta la cache, impone la rivalidazione; il divieto è no-store. Pragma: no-cache è l'equivalente di HTTP/1.0, obsoleto. Un'altra intestazione è Vary: Accept-Encoding: dice che la risposta dipende da quell'header di richiesta, quindi la cache deve tenere copie diverse per valori diversi (versione compressa e non). Un browser, alla pressione di F5, manda richieste condizionali; a Ctrl+F5 bypassa la cache.

Un server che implementa la cache deve quindi (1) generare ETag e/o Last-Modified, (2) rispondere 304 quando il validatore combacia, (3) scegliere una politica con Cache-Control (Esercizio - Server con autenticazione Basic, ETag e GET condizionale (sul modello della prova pratica)); un client che implementa la cache (come quello di Esercizio - GET condizionale con If-Modified-Since e cache su file (slide HTTP 0.9 e 1.0)) memorizza corpo e validatore e li usa nella richiesta successiva.

Autenticazione

HTTP ha un meccanismo generale di sfida e risposta (RFC 9110 par. 11):

  1. Il client chiede una risorsa protetta senza credenziali.
  2. Il server risponde 401 Unauthorized con l'header WWW-Authenticate, che indica lo schema e un realm (l'"area protetta").
  3. Il client ripete la richiesta con l'header Authorization, che contiene le credenziali nel formato dello schema.
  4. Se vanno bene: 200; altrimenti di nuovo 401. Un 403 Forbidden significa invece "ho capito chi sei, ma non hai diritto".

Per i proxy lo schema è lo stesso con altri nomi: 407 Proxy Authentication Required, Proxy-Authenticate, Proxy-Authorization.

Basic

http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Area riservata", charset="UTF-8"
http
GET /private/riservato.txt HTTP/1.1
Host: www.example.com
Authorization: Basic bWFyaW86c2VncmV0bw==

La credenziale è base64("utente:password"). Per mario:segreto la codifica Base64 (WebSocket, QUIC e HTTP-3HTTP e' richiesta e risposta, quindi poco adatto a notifiche e dati in tempo reale (polling, long polling, SSE); WebSocket (RFC 6455) apre con un handshake HTTP/1.1 Upgrade (Sec-WebSocket-Key a 16 byte casuali in Base64, risposta 101 con Sec-WebSocket-Accept = Base64 di SHA-1 di chiave piu' GUID fisso) un canale bidirezionale persistente sulla stessa connessione TCP, con frame di 2-14 byte di intestazione (FIN, opcode, MASK, lunghezza su 7, 16 o 64 bit, chiave di mascheramento obbligatoria dal client al server) e messaggi di testo, binari, close, ping e pong. QUIC (RFC 9000) e' un protocollo di trasporto sopra UDP con TLS 1.3 integrato, flussi indipendenti (niente head-of-line blocking fra flussi), connection ID che permettono di cambiare rete, handshake in 1 RTT e ripresa in 0 RTT; HTTP/3 (RFC 9114) mappa HTTP su QUIC con un flusso per richiesta e QPACK per gli header, ed e' annunciato con Alt-Svc.WebSocket, QUIC e HTTP-3 → riprende l'algoritmo) è bWFyaW86c2VncmV0bw==; l'esempio della RFC 7617 è Aladdin:open sesame, cioè QWxhZGRpbjpvcGVuIHNlc2FtZQ==. Il nome utente non può contenere : (il primo : separa i due campi).

Basic non cifra niente: Base64 è una codifica, non una protezione, e si inverte in un attimo. Va quindi usato solo sopra TLS (HTTPS). Il browser rimanda le credenziali a ogni richiesta successiva dello stesso realm (HTTP è senza stato): per questo conviene non usarlo per sessioni lunghe e comparare le password con funzioni a tempo costante.

Digest

Digest (RFC 7616) evita di trasmettere la password: il client trasmette un hash. Nella sfida il server manda un nonce (numero casuale usato una volta), un realm, qop="auth" e l'algoritmo; il client calcola (con MD5 o, meglio, SHA-256):

HA1      = H( utente : realm : password )
HA2      = H( metodo : uri )
response = H( HA1 : nonce : nc : cnonce : qop : HA2 )

dove nc è un contatore delle richieste con quel nonce e cnonce un numero scelto dal client (contro gli attacchi con testi in chiaro scelti). Esempio verificato (RFC 2617, MD5): utente Mufasa, realm testrealm@host.com, password Circle Of Life, nonce dcd98b7102dd2f0e8b11d0f600bfb0c093, GET /dir/index.html, nc=00000001, cnonce=0a4f113b, qop=auth: HA1 = 939e7578ed9e3c518a452acee763bce9, HA2 = 39aff3a2bab6126f332b942af96d3366 e response = 6629fae49393a05397450978507c4ef1. La password non viaggia, ma il server deve poter ricostruire HA1 (non può conservare solo un hash lento della password) e MD5 oggi è debole: in pratica Digest è poco usato.

Altri schemi: Bearer (RFC 6750), con un token ottenuto da un servizio di autorizzazione (OAuth 2.0), e le sessioni con cookie (Livello applicazione - HTTPIl livello applicazione è il più alto della pila: offre servizi all'utente con una connessione logica tra le due applicazioni e riceve servizi solo dal trasporto (DNS, HTTP, e-mail, FTP). Il Web (WWW, nato al CERN nel 1989) è un servizio client-server distribuito di pagine collegate da ipertesti; ogni pagina ha un URL protocollo://host:porta/percorso. HTTP: il client manda una richiesta, il server una risposta, su TCP (server sulla porta 80, client su una porta temporanea); senza stato. Messaggi di testo (riga di richiesta o di stato, intestazioni, riga vuota, corpo), metodi GET, POST, HEAD, PUT, DELETE, codici di stato 2xx-5xx. Una pagina con N oggetti incorporati richiede 2(N+1) RTT con connessioni non persistenti e (N+2) RTT con connessione persistente (trascurando la trasmissione). I cookie danno memoria al protocollo: Set-Cookie nella risposta, Cookie nelle richieste, file nel browser e base di dati nel sito. Un proxy (web cache) tiene le copie delle risposte recenti: meno carico sul server, meno traffico, meno ritardo.Livello applicazione - HTTP →); entrambi, come Basic, vanno protetti con TLS (Crittografia asimmetrica, RSA e TLSCrittografia asimmetrica (a chiave pubblica): chiave di cifratura pubblica v_B, chiave di decifratura privata s_B, matematicamente legate; C = E_vB(P), P = D_sB(C); dà riservatezza. I certificati digitali, emessi da una Certificate Authority e firmati con la sua chiave privata, legano un'identità a una chiave pubblica. Firma digitale: hash del messaggio cifrato con la chiave privata; garantisce autenticità, integrità e non ripudio. RSA: si scelgono due primi grandi p e q, N = pq, phi(N) = (p-1)(q-1), e coprimo con phi(N), d = e^(-1) mod phi(N); chiave pubblica (N, e), segreta (N, d); cifratura c = m^e mod N, decifratura m = c^d mod N con m < N (teorema di Eulero); sicura finché la fattorizzazione è difficile (N di almeno 2048 bit); il padding casuale (PKCS#1 v1.5: 00 02 [casuale] 00 [m]) difende da malleabilità e determinismo. La firma RSA è s = m^d mod N, verificata con s^e mod N. TLS (su TCP) autentica gli estremi con il certificato, cifra i dati con una chiave di sessione simmetrica e garantisce l'integrità con i MAC; da TLS 1.0 a 1.3 la chiave si ricava con Diffie-Hellman e l'handshake si accorcia. DTLS è la versione per UDP.Crittografia asimmetrica, RSA e TLS →).

Una nota per le cache: una risposta a una richiesta con Authorization non può essere servita da una cache condivisa ad altri utenti, salvo che sia dichiarata public.

Tipi MIME

Ogni risposta che ha un corpo dice che tipo di dati contiene, con Content-Type. Il formato è lo standard MIME (nato per la posta, RFC 2045-2046; Posta elettronica - SMTP, POP3 e IMAPLa posta elettronica è una transazione a senso unico: non ha senso che il destinatario tenga un server sempre acceso, quindi si usano server intermedi. Servono due User Agent (UA, il programma dell'utente), due programmi client/server di spinta (MTA, protocollo SMTP: mittente-suo server e server-server) e un programma client/server di tiro (MAA, POP3 o IMAP4: server del destinatario-destinatario). SMTP usa TCP porta 25, comandi HELO, MAIL FROM, RCPT TO, DATA, QUIT e risposte a tre cifre (220 pronto, 250 ok, 354 inizia il testo, 221 chiusura, 421 non disponibile) in tre fasi: apertura, trasferimento, chiusura. POP3 (porta 110): utente e password, poi elenca e scarica i messaggi uno a uno; non organizza la posta sul server. IMAP4 aggiunge intestazioni prima del download, ricerca, download parziale, cartelle sul server. MIME permette di spedire dati non ASCII (immagini, video, lettere accentate) traducendoli in ASCII a 7 bit, per esempio in base64 (3 byte diventano 4 caratteri).Posta elettronica - SMTP, POP3 e IMAP →):

Definizione (media type). tipo/sottotipo[; parametro=valore]. Il tipo indica la famiglia (text, image, audio, video, application, multipart), il sottotipo il formato preciso. I valori sono registrati presso IANA e non distinguono maiuscole e minuscole.

Il parametro charset dice la codifica dei caratteri dei testi (utf-8). Il server ricava il tipo dall'estensione del file con una tabella: è la funzione mime_type degli esercizi (Esercizio - Server HTTP iterativo con GET, HEAD e codici di errore (sul modello della prova pratica)). Un tipo sbagliato fa mostrare male la risorsa (un CSS servito come text/plain non viene applicato) o permette attacchi di content sniffing, mitigati da X-Content-Type-Options: nosniff.

Moduli e multipart

Un modulo HTML invia i campi in due formati:

  • application/x-www-form-urlencoded: coppie nome=valore separate da &, con percent-encoding (lo spazio diventa + oppure %20);
  • multipart/form-data: il corpo è diviso in parti separate da una stringa boundary dichiarata nel Content-Type; ogni parte ha i propri header e può contenere un file. Le righe di separazione sono --boundary e la chiusura è --boundary--.
http
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----XYZ
Content-Length: 186

------XYZ
Content-Disposition: form-data; name="nome"

Mario
------XYZ
Content-Disposition: form-data; name="file"; filename="a.txt"
Content-Type: text/plain

ciao
------XYZ--

Con la boundary ----XYZ le righe di separazione sono ------XYZ (due - più la stringa), e tutte le righe terminano con CRLF: il corpo è di 186 byte. La stringa di boundary non deve comparire nei dati.

Negoziazione del contenuto

Il client dichiara le proprie preferenze con header Accept-*, e il server sceglie la rappresentazione (negoziazione proattiva):

header esempio significato
Accept text/html,application/xhtml+xml;q=0.9,*/*;q=0.8 tipi accettati, con peso q da 0 a 1 (default 1)
Accept-Language it-IT,it;q=0.9,en;q=0.8 lingue preferite
Accept-Encoding gzip, br codifiche del contenuto accettate (compressione)
Accept-Charset utf-8 codifiche dei caratteri

La risposta indica la scelta (Content-Type, Content-Language, Content-Encoding) e, con Vary, da quali header dipende. Un client di API usa Accept: application/json.

URI

Un URI (Uniform Resource Identifier, RFC 3986) è una stringa che identifica una risorsa. Un URL è un URI che dice anche come raggiungerla (http://...); un URN la dà per nome (urn:isbn:...). La sintassi generica è:

   http://mario:pwd@www.example.com:8080/a/b/../c.html?x=1&y=2#sezione
   \__/   \_______/ \_____________/ \__/ \___________/ \_____/ \_____/
  scheme   userinfo      host        porta    percorso   query  frammento
          \____________________________/
                    authority
componente descrizione
scheme protocollo o tipo: http, https, ftp, mailto, ws; non distingue maiuscole
authority [userinfo@]host[:porta]; l'host è un nome di dominio, un IPv4 o un IPv6 fra parentesi quadre (http://[::1]:8080/); porta predefinita: 80 per http, 443 per https
percorso gerarchico con /; distingue maiuscole e minuscole
query dopo ?: coppie chiave=valore separate da &
frammento dopo #: punto interno al documento; non viene mai inviato al server, lo usa il client

Caratteri riservati e percent-encoding

Nell'URI alcuni caratteri hanno funzione sintattica (riservati: : / ? # [ ] @ ! $ & ' ( ) * + , ; =); gli altri ammessi sono gli unreserved: lettere, cifre e - . _ ~. Tutto il resto, e i riservati usati come dati, si scrive in percent-encoding: % seguito dai due caratteri esadecimali del byte. I caratteri non ASCII si codificano nei byte della loro forma UTF-8.

Esempio. caffè & latte diventa caff%C3%A8%20%26%20latte: è in UTF-8 sono i due byte C3 A8, lo spazio è %20, & è %26. La decodifica è l'inverso; un % non seguito da due cifre esadecimali è un errore (400), e %00 (byte nullo) va rifiutato perché può ingannare i controlli sulle stringhe C. Nei dati dei moduli (x-www-form-urlencoded) anche + vale spazio; nel percorso no.

Un server che serve file decodifica prima di controllare il percorso: /%2e%2e/etc/passwd è /../etc/passwd e va rifiutato (path traversal, Esercizio - Parsing di request line, header e URI (sul modello della prova pratica)).

Risoluzione dei riferimenti relativi

Un documento può contenere URI relativi (../img/a.png) risolti rispetto a una base. L'algoritmo (RFC 3986 par. 5.2) fonde il percorso della base con quello relativo e poi rimuove i segmenti . e ... Con base http://a/b/c/d;p?q:

riferimento risultato
g http://a/b/c/g
./g http://a/b/c/g
/g http://a/g
//g http://g
?y http://a/b/c/d;p?y
#s http://a/b/c/d;p?q#s
../g http://a/b/g
../../g http://a/g
../../../g http://a/g (.. non supera la radice)

Rimozione dei segmenti punto: /a/b/c/./../../g diventa /a/g. Un programma C che implementa scomposizione, percent-encoding e decodifica, rimozione dei segmenti punto e risoluzione dei riferimenti, e verifica tutti gli esempi della RFC, è in Esercizio - URI, scomposizione, percent-encoding e riferimenti relativi (sul modello della prova pratica).

Gli URI nelle richieste

La Request-Line contiene un request-target, che ha quattro forme (RFC 9112 par. 3.2):

forma esempio quando
origin-form GET /a/b.html?x=1 HTTP/1.1 richiesta a un server (la più comune); l'host sta in Host
absolute-form GET http://www.example.com/a/b.html HTTP/1.1 richiesta a un proxy
authority-form CONNECT www.example.com:443 HTTP/1.1 solo per CONNECT
asterisk-form OPTIONS * HTTP/1.1 OPTIONS sull'intero server

Il server ricostruisce l'URI completo da schema (dalla connessione), Host e origin-form. Il frammento non compare mai.

Esercizi collegati

Errori tipici

  • Credere che no-cache impedisca di memorizzare: serve no-store.
  • Ignorare il 304 e riscaricare il corpo, oppure inviare un corpo in un 304.
  • Confondere Last-Modified (data) ed ETag (versione), o dimenticare le virgolette dell'ETag nel confronto.
  • Scrivere Authorization: Basic utente:password senza Base64; credere che Base64 protegga la password.
  • Rispondere 403 quando mancano le credenziali (serve 401 con WWW-Authenticate).
  • Dimenticare Content-Type o dichiarare quello sbagliato; servire tutto come text/html.
  • Decodificare il percent-encoding dopo i controlli sul percorso; decodificare due volte; trattare + come spazio nel percorso.
  • Aspettarsi il frammento (#...) nella richiesta: non viene inviato.
  • Confrontare il percorso senza distinguere maiuscole e minuscole, o lo schema e l'host distinguendole.

Domande d'esame

1. Un client ha in cache una risorsa con ETag: "v7" e Cache-Control: max-age=60. Descrivere cosa succede alla richiesta dopo 30 s e dopo 90 s. Traccia. A 30 s la copia è fresca (30 < 60): la cache la serve senza contattare il server. A 90 s è scaduta: il client invia una richiesta condizionale con If-None-Match: "v7". Se la risorsa non è cambiata il server risponde 304 Not Modified senza corpo e la copia torna fresca per altri 60 s; se è cambiata risponde 200 con il corpo nuovo e un nuovo ETag.

2. Descrivere l'autenticazione Basic: messaggi scambiati e perché si usa solo con HTTPS. Traccia. Il client chiede la risorsa; il server risponde 401 con WWW-Authenticate: Basic realm="..."; il client ripete con Authorization: Basic base64(utente:password) (esempio mario:segreto -> bWFyaW86c2VncmV0bw==). Base64 non cifra: chiunque intercetti la richiesta ottiene la password, e il browser la rimanda a ogni richiesta; quindi serve TLS.

3. Codificare caffè & latte in percent-encoding e dire cosa fa un server con GET /%2e%2e/etc/passwd. Traccia. caff%C3%A8%20%26%20latte (è = C3 A8 in UTF-8). Il server decodifica %2e%2e in .. prima di controllare il percorso, ottiene /../etc/passwd e lo rifiuta (403 o 404): se controllasse prima della decodifica, l'attaccante uscirebbe dalla radice dei documenti.

Versione ripasso

direttiva effetto
max-age=N fresca N secondi
public / private anche condivise / solo cache dell'utente
no-cache memorizza ma rivalida sempre
no-store non memorizzare
must-revalidate scaduta: niente uso senza rivalida

Vary: Accept-Encoding: copie diverse per valori diversi.

http
GET /logo.png HTTP/1.1                      HTTP/1.1 304 Not Modified
Host: www.example.com                       ETag: "abc123"
If-None-Match: "abc123"                     Cache-Control: max-age=3600

HTTP/1.1 401 Unauthorized                   GET /private/riservato.txt HTTP/1.1
WWW-Authenticate: Basic realm="Area"        Authorization: Basic bWFyaW86c2VncmV0bw==

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----XYZ
Content-Length: 186                         (corpo: ------XYZ ... ------XYZ--)
  • Cronologia della cache. t = 0: 200, ETag "abc123", max-age=3600 (si memorizza); t = 1800 s: servita dalla cache, nessun pacchetto; t = 3700 s: If-None-Match -> 304, freschezza rinnovata; file cambiato: 200 con ETag "def456".

  • Errori tipici: no-cache creduto un divieto (è no-store); 304 con corpo o ignorato; Basic senza Base64 o creduto sicuro; 403 al posto di 401; Content-Type mancante; controllo del percorso prima della decodifica; + come spazio nel percorso; frammento atteso nella richiesta.

Esercizi su questo argomento

Teoria collegata