Home / Indice sezione | www.icosaedro.it | ![]() |
Ultimo aggiornamento: 2008-04-27
Si dimostra come sia possibile scrivere programmi CGI che si interfacciano ad Apache usando il Bash come linguaggio di scripting. Si spiega il meccanismo MIME necessario, come decodificare le query HTTP, come salvare e recuperare dati da un semplice DB realizzato con un file di testo, come gestire le sessioni con i cookie, come eseguire il debugging dei CGI. Si presume che sia stato attivato il meccanismo SUEXEC di Apache come descritto nelle sezioni precedenti. Vengono proposti molti esempi, tutti da provare.
Glossarietto
Un primo esempio
Debugging dei CGI
CGI che restituiscono pagine HTML
CGI di test
Metodo GET
Metodo POST
Soluzione generale per GET e POST
Riconoscere i parametri della query HTTP con Bash
Gestire un DB
Programmi CGI che ritornano immagini
Sicurezza e diritti di accesso
Sessioni con i cookie
Conclusioni
Appendice
Argomenti correlati
CGI - Significa "Common Gateway Interface", sigla che di per sè non vuol dire nulla... ma un nome dovevano pur darglielo! Comunque, tecnicamente si tratta del modo in cui si interfacciano il protocollo HTTP del WEB con i programmi sul lato server. L'interfaccia CGI è stata inizialmente definita da NCSA, l'ente governativo che sviluppò Mosaic e il server NCSA. Il nostro WEB server, Apache, adotta questa soluzione.
ASP - Significa Active Server Pages, ed è la soluzione di interfaccia adottata da Microsoft per il suo server IIS.
IIS - Significa Internet Information Server, ed è la soluzione software di Microsoft per Internet che essenzialmente comprende un server WEB e un server FTP.
Apache - Server WEB nato come patch al server di NCSA, da cui il nome: "a patch". Niente a che vedere, dunque, con gli indiani d'America. Oggi Apache è diventato un progetto open-source indipendente che coinvolge decine di programmatori, ed è disponibile per i sistemi operativi UNIX, Linux e MS Windows. Adotta l'interfaccia CGI.
HTML - Significa Hyper-Text Markup Language, è uno standard Internet RFC. Le ultime versioni di questo linguaggio sono state emanate dal consorzio "W3C", e sono universalmente riconosciute come la versione ufficiale più recente. L'HTML permette di descrivere il contenuto di una pagina WEB, e di specificare i link ipertestuali.
HTTP - Significa Hyper-Text Transport Protocol, ed è il protocollo di trasporto adottato per il WEB. Attualmente il protocollo è arrivato alla versione 1.1. Essenzialmente permette il trasferimento di files e varie altre informazioni di interazione con il client. Viene descritto dallo standard RFC 2068. HTTP adotta le convenzioni MIME per la descrizione delle informazioni veicolate tra il server e il client.
MIME - Significa Multipurpose Internet Mail Extensions, è uno standard RFC 1521, e permette di dichiarare la natura delle informazioni e dei file trasferiti, la codifica adottata, e il tipo dei dati. Ecco un esempio minimale di pagina WEB con codifica MIME:
Content-Type: text/html <HTML><BODY>Ciao ciao...</BODY></HTML>Questa è la pagina HTML così come viene ritornata dal server al client. Il client riconosce dalla intestazione il tipo di informazione veicolata (html) e la sua rappresentazione (text). Segue una linea vuota obbligatoria, e poi la pagina HTML vera e propria. La codifica MIME fu definita essenzialmente per la posta elettronica, ma è poi stata adottata nel protocollo HTTP, dove permette di veicolare nella intestazione varie altre informazioni importanti (v. seguito).
#!/bin/bash echo "Content-Type: text/plain" echo echo "La data di oggi:" date
Scrivere questo testo e salvarlo come
/home/pippo/public_html/prova1.cgi
.
Se il vostro nome utente è diverso da "pippo", apportare i dovuti
cambiamenti. Ricordare di rendere eseguibile il programma settando il
flag di esecuzione:
$ chmod +x prova1.cgi
Aprire un browser WEB e puntare all'URL
http://localhost/~pippo/prova1.cgi
per vedere la pagina generata.
Osserviamo subito quanto segue:
UserDir public_html
per dar modo agli utenti di redigere le loro home page.
.cgi
che informa Apache che questo è un programma. Questo meccanismo lo
abbiamo impostato usando la direttiva ScriptAliasMatch
nelle sezioni precedenti.
pippo
dovrebbe apparire con
questi permessi di accesso:
$ ls /home/pippo -d drwx-----x 4 pippo users 4096 Aug 17 22:20 /home/pippoNotare come il gruppo
users
non abbia accesso alla directory
di pippo
, mentre tutti gli altri sì, e cioè Apache che
gira come nobody:nobody
(o altra identità dipendente
dalla vostra configurazione).
Descriviamo brevemente il meccanismo di chiamata: il client richiede la
pagina, Apache individua il file prova1.cgi
, scopre che si
tratta di un programma per via della estensione .cgi
, quindi
lo esegue tramite SUEXEC; il CGI viene quindi eseguito con l'identità
pippo:users
; l'output generato da questo programma viene
restituito da Apache al client che ne ha fatto richiesta; il client
riconosce l'intestazione MIME per un testo puro, e lo visualizza come
tale sullo schermo.
Nel seguito perfezioneremo la nostra conoscenza del CGI attraverso una serie di esempi, sempre utilizzando Bash. Purtroppo Bash non ha un supporto integrato per il CGI, per cui dovremo fare tutto "a mano", ma con grande vantaggio dal punto di vista didattico.
In tutta questa catena di chiamate e passaggi, c'è sempre qualcosa che può andare storto: ecco perchè è importante sapersi orientare nel debugging dei programmi CGI.
Quando un programma CGI non funziona, la prima cosa da provare è di avviare il CGI da riga di comando in una sessione terminale, oppure via telnet:
$ ./prova1.cgi
E' necessario specificare la directory corrente "./" o in alternativa il
path completo del file /home/pippo/public_html/prova1.cgi
. Sullo
schermo dovrebbe apparire l'output corretto del CGI, che nel nostro caso
sarà qualcosa del tipo:
Content-Type: text/plain La data di oggi: Sat Aug 18 11:49:32 CEST 2001
Conviene anche aprire una finestra terminale addizionale dove mostrare con continuità i messaggi di errore di Apache:
$ tail -f /var/log/http/*error.log
Conviene lasciare sempre aperta questa finestra mentre si debugga un CGI.
Abbiamo visto come un CGI può ritornare un testo puro. Per ritornare una pagina HTML si tratta semplicemente di indicare il tipo MIME corrispondente:
#!/bin/bash echo "Content-Type: text/html" echo echo "<HTML><BODY>" echo "<CENTER>La data di oggi:</CENTER>" echo "<CENTER><B>" date echo "</B></CENTER>" echo "</BODY></HTML>"
Salvare questo testo col nome
/home/pippo/public_html/prova2.cgi
, settare il flag x con
$ chmod +x prova2.cgi
e richiamarlo col browser con l'URL
http://localhost/~pippo/prova2.cgi
. Se qualcosa non
funziona, fare riferimento alla sezione di debugging precedente.
Miglioramenti suggeriti: il comando date
è ricco di
funzionalità di formattazione delle date: consiglio di leggere
attentamente il manuale on-line per i dettagli ("man date" e soprattutto
"info date"). Per esempio,
date "+%Y-%m-%d, %H:%M"
formatta la data secondo lo standard ISO-8601.
E' utile tenere sempre a disposizione un CGI di test come il seguente:
#!/bin/bash echo "ContentType: text/plain" echo echo "---------------------------------------------------" echo "Identita' del processo:" id echo "---------------------------------------------------" echo "Ecco lo stdin:" cat echo "---------------------------------------------------" echo "Ecco l'env:" echo env | sort echo "---------------------------------------------------"
Salvare questo script col nome test.cgi
, renderlo eseguibile
e provarlo dal browser. Dovrebbe stampare nell'ordine:
1) L'identità del processo, cioè il nome utente e il gruppo
assegnati al processo CGI, che ne determinano anche i diritti di accesso
al sistema. Il nome utente sarà pippo
e il gruppo sarà
users
.
2) Gli eventuali dati provenienti dallo standard input. Vedremo che Apache può fornire dati al CGI attraverso questo meccanismo.
3) Le variabili di ambiente (environment) definite da Apache. Una scorsa veloce a queste variabili mostra che il CGI ha a disposizione un sacco di informazioni interessanti: l'indirizzo IP del client, il tipo di browser, ecc. Vedremo le più importanti e il loro uso.
Il client ha essenzialmente due meccanismi per inviare dati a un programma CGI: il metodo GET e il metodo POST.
Il metodo GET permette di inserire i parametri della interrogazione direttamente nell'URL. Proviamo il nostro script di test invocandolo con questo URL:
http://localhost/~pippo/test.cgi?alfa=123&beta=456
La query HTTP è la stringa che segue il carattere speciale "?". Si tratta di una sequenza di assegnamenti del tipo
nome=valore
separati tra di loro dal carattere speciale "&" (ampersand). All'inizio
della query c'è il carattere ?
che la separa dal resto
dell'URL.
Notiamo che Apache definisce una variabile d'ambiente:
REQUEST_METHOD=GET
che ci dice che il metodo è GET, ed inoltre definisce un'altra variabile d'ambiente:
QUERY_STRING=alfa=123&beta=456
che contiene la query HTTP. Questo, nella sostanza, è il meccanismo di passaggio di dati dal client verso il CGI usando il metodo GET. Sarà compito del programma CGI riconoscere ed estrarre in modo appropriato questi dati. Bash non dispone di un meccanismo automatico per fare questo: vedremo negli esempi seguenti come fare.
Mentre le ancore di una pagina HTML comportano sempre una richiesta di tipo GET, per i FORM HTML ci sono tre alternative: metodo GET, metodo POST con parametri codificati URL-encoded, metodo POST con parametri codificati multipart/form-data. Qui vediamo la variante di POST che invia i parametri URL-encoded che è la più facile da gestire con Bash. L'altra forma, quella che usa il multipart/form-data, è più generale perché permette anche l'upload dei file dal client al server, ma è anche più difficile da decodificare se ci limitiamo a semplici programmi scritti in Bash.
Il metodo POST differisce dal metodo GET come segue:
REQUEST_METHOD=POST
.
Ecco per esempio come il client invia la stessa richiesta di prima ma questa volta con metodo POST:
POST /~pippo/test.cgi HTTP/1.1 Host: localhost Content-Length: 17 alfa=123&beta=456
Nella intestazione compare il campo Content-Length che contiene la lunghezza del corpo in byte. Il corpo della richiesta contiene i parametri codificati nel solito modo. Vediamo nel prossimo paragrafo come utilizzare in pratica tutto ciò.
Creiamo un CGI sotto forma di script Bash in grado di individuare la query HTTP, indipendentemente dal fatto che essa sia stata ritornata col metodo GET o POST:
#!/bin/bash if [ "$REQUEST_METHOD" = POST ]; then query=$( head --bytes="$CONTENT_LENGTH" ) else query="$QUERY_STRING" fi echo "Content-Type: text/plain" echo echo "Query=$query"
Salviamo questo programma col nome prova3.cgi
, rendiamolo
eseguibile e quindi invochiamolo dal client nel solito modo.
Per provare il metodo GET basta invocare il CGI con questo URL:
http://localhost/~pippo/prova3cgi?alfa=123&beta=456
Per provare il metodo POST bisogna prima creare una pagina HTML con un form:
<HTML><BODY> <FORM method=POST action="prova3.cgi"> Alfa=<input type=text name=alfa><br> Beta=<input type=text name=beta><br> <input type=submit name=bottone value="INVIA!"> </FORM> </BODY></HTML>
Salvare questo testo col nome prova3.html nella stessa directory del
CGI prova3.cgi, e quindi dal browser invocare l'URL
http://localhost/~pippo/prova3.html
Bash non fornisce alcuno strumento integrato per interpretare la query HTTP. Esistono librerie di funzioni già fatte per questo, ma noi ci svilupperemo gli strumenti necessari da soli!
Vediamo come si può risolvere il problema perfezionando il
CGI prova3.cgi
di prima:
#!/bin/bash function get_parameter () { echo "$query" | tr '&' '\n' | grep "^$1=" | head -1 \ | sed "s/.*=//" | urldecode } if [ "$REQUEST_METHOD" = POST ]; then query=$( head --bytes="$CONTENT_LENGTH" ) else query="$QUERY_STRING" fi echo "Content-Type: text/plain" echo echo "Query=$query" alfa=$( get_parameter alfa ) beta=$( get_parameter beta ) gamma=$( get_parameter gamma ) echo "Alfa=$alfa" echo "Beta=$beta" echo "Gamma=$gamma"
Per prima cosa, il programma individua la query, indipendentemente
dal fatto che essa sia stata ritornata col metodo GET o col metodo
POST. La query così ottenuta viene salvata nella variabile globale
$query
. La funzione get_parameter
deve essere
chiamata passando per argomento il nome del parametro della query che
ci interessa; la funzione analizza la query e ritorna il valore del
parametro indicato; se il parametro non esiste ritorna la stringa nulla,
come sarà il caso della gamma.
La funzione get_parameter sfrutta alcuni programmi filtro tradizionalmente disponibili sui sistemi UNIX, quali tr, grep e sed: rimando alle relative man pages per i dettagli.
L'ultimo filtro urldecode
è invece un programmino scritto in C che riporto in appendice:
questo programma converte la codifica esadecimale dei caratteri riservati
nella loro rappresentazione ASCII, secondo quanto richiesto dalle
specifiche RFC 3986. In pratica si tratta di questo: visto che alcuni
caratteri sono riservati per la sintassi della query HTTP, come
ad esempio ? = &, questi caratteri non possono comparire direttamente
come valori delle variabili della query. Per superare questo
problema, i caratteri "vietati" vengono invece rappresentati dal loro
valore esadecimale preceduto dal carattere %. Il programma-filtro
urldecode
, e la sua controparte urlencode
,
assolvono proprio a questa funzione. Per il momento urlencode
non ci serve, ma verrà anche il suo momento.
Vediamo come estendere ulteriormente il nostro CGI prova3
perchè
salvi i dati impostati in un DB e li recapiti anche via posta
elettronica ad un indirizzo scelto. Il codice da aggiungere
è il seguente:
db=/home/pippo/db email="pippo@localhost" dati="$alfa $beta" echo "$dati" >> $db echo "$dati" | mail -s "Dati dal form prova3.html" "$email"
Il DB viene implementato come file di testo, dove ogni riga è un record. I nuovi dati vengono accodati a quelli esistenti.
E' facile implementare una interfaccia di consultazione al DB così creato. Per cominciare, il form di interrogazione:
<HTML><BODY> <FORM method=POST action=prova4.cgi> Chiave:<input type=text name=chiave> <input type=submit name=bottone value="CERCA!"> </FORM> </BODY></HTML>
che dovrebbe mostrare la form seguente:
Salvare questa pagina HTML col nome prova4.html. Il CGI per l'interrogazione del DB è il seguente:
#!/bin/bash function get_parameter () { echo "$query" | tr '&' '\n' | grep "^$1=" | head -1 \ | sed "s/.*=//" | urldecode } if [ "$REQUEST_METHOD" = POST ]; then query=$( head --bytes="$CONTENT_LENGTH" ) else query="$QUERY_STRING" fi echo "Content-Type: text/plain" echo chiave=$( get_parameter chiave ) if [ -z "$chiave" ]; then echo "ERRORE: non hai inserito la chiave di ricerca!" exit fi echo "Esito della ricerca della chiave $chiave:" grep -e "$chiave" /home/pippo/db
Salvare questo script col nome prova4.cgi
, impostare il flag che lo rende
eseguibile, quindi dal browser puntare all'URL
http://localhost/~pippo/prova4.html
Buona ricerca!
Ancora una volta, si tratta semplicemente di impostare il tipo MIME
corretto. Supponiamo di avere una immagine in un file di nome
figura.gif
: si tratta di una GIF. Ecco un CGI che ritorna
questa figura:
#!/bin/bash echo "Content-Type: image/gif" echo cat figura.gif
Scriviamo questo programma nel file prova5.cgi
, rendiamolo eseguibile
nel solito modo, ed invochiamolo dal browser con l'URL http://localhost/~pippo/prova5.cgi
: dovrebbe apparire la nostra figura.
Apparentemente, creare un programma CGI che ritorna una figura sembra del tutto inutile. Infatti basterebbe puntare l'URL del browser direttamente al file della figura stessa!
Invece, ecco possibili applicazioni di un programma CGI che ritorna una figura:
Il programma CGI può essere anche invocato nel tag <IMG> completo di una query, come in questo esempio:
<IMG SRC="prova5.cgi?alfa=123&beta=456">
Naturalmente non si è vincolati al formato GIF: se ne possono usare tanti altri, purche' siano riconosciuti dai browser. Ecco alcuni formati MIME per le immagini comunemente riconosciuti dai browser:
image/gif image/png image/jpeg
Gli stessi principi visti per le immagini valgono ovviamente anche per gli altri tipi di dato (filmati, suoni, ecc.).
Abbiamo visto come i programmi CGI siano eseguiti con l'identità del loro autore. Di conseguenza un programma CGI può fare nè più nè meno tutto quello che può fare l'utente stesso che lo ha scritto. Facciamo un esempio pratico:
Si vuole gestire un grosso archivio di file che contengono ciascuno un articolo del codice civile, e dando la possibilità al cliente di specificare il numero dell'articolo al quale è interessato. Il programma CGI recupera l'articolo e lo ritorna al client. Ecco una possibile implementazione del form:
<FORM method=post action="articolo.cgi"> Art. n. <INPUT type=text name=n> <INPUT type=submit name=x value="OK"> </FORM>
Il cliente inserisce il numero dell'articolo che vuole leggere, per
esempio "1234", schiaccia il bottone OK, e quindi il programma CGI di
nome articolo.cgi
ritorna il file di nome 1234. Ecco il CGI
articolo.cgi
:
#!/bin/bash (solito codice omesso) n=$( get_parameter n ) echo "Content-Type: text/plain" echo cat $n
Problema: cosa succede se un cliente malizioso invece di inserire un numero
innocuo inserisce la stringa /etc/passwd
? Risposta: fedelmente,
il comando cat
ritornerà al client l'elenco degli utenti
registrati nel sistema, il chè non è proprio una bella cosa...
Questo è uno di quei casi in cui un programma CGI mal scritto può incidere sulla sicurezza del sistema, o per lo meno mettere a repentaglio la riservatezza dei dati. Sebbene i diritti di accesso di un utente non consentano di violare la riservatezza degli account degli altri utenti, nè consentano di vedere o alterare informazioni vitali del sistema, questa svista può costituire comunque un ottimo appiglio per un cracker smaliziato.
In tutti casi, l'input da un form dovrebbe essere sempre vagliato dal programma CGI. Nel nostro caso basterebbe assicurare che la variabile $n sia costituita da una o più cifre:
if echo "$n" | grep -q '^[0-9]\+$'; then cat $n else echo "ATTENZIONE! non hai inserito un numero valido!" fi
Qui ci siamo avvalsi di una espressione regolare per rappresentare sinteticamente la sintassi ammessa per $n. Tutti i linguaggi di scripting hanno un supporto per le espressioni regolari, che si dimostra versatile e potente anche in casi più articolati, ad es. per il parsing di un indirizzo email, di una data, di un codice fiscale, ecc.
Una descrizione dettagliata e sintetica delle espressioni regolari si trova nella relativa pagina del manuale in linea:
$ man 7 regex
Content-Type: text/plain Set-Cookie: tuocodice=1234567; expires=Sun 15-Aug-01 00:30:18 GMT Salve, il tuo accesso e' stato validato. In questo momento il tuo browser ha ricevuto il cookie di sessione. Il tuo codice di sessione e' 1234567. Buona navigazione!Ho evidenziato la riga dove il server imposta il cookie. Il browser dovrà memorizzare la variabile
tuocodice
e assegnarle
il valore 1234567
; inoltre, il browser dovrà mantenere
questo valore in memoria fino alla data indicata, dopo di chè dovrà scartarlo.
Nel frattempo, il browser dovrà ritornare al server il cookie ogni volta
che invocherà lo stesso URL. Non ci interessa vedere come questo valore viene
ritornato dal browser al server, ci basta sapere che il CGI si ritroverà
l'elenco dei cookie nella variabile d'ambiente HTTP_COOKIE
.
Passiamo subito all'esempio: creeremo uno script CGI in Bash che imposta e
mostra un cookie. Chiameremo questo programma prova6.cgi
:
#!/bin/bash function setcookie() # # $1 = nome variabile # $2 = valore variabile # $3 = durata (secondi) # $4 = path (opzionale) # { value=$( echo -n "$2" | urlencode ) if [ -z "$4" ]; then path="" else path="; Path=$4" fi echo -n "Set-Cookie: $1=$value$path; expires=" date -u --date="$3 seconds" "+%a, %d-%b-%y %H:%M:%S GMT" } # Intestazione MIME: echo "Content-Type: text/plain" setcookie tuocodice 1234567 60 setcookie tuonome "Mario" 60 setcookie tuocogn "Rossi" 60 echo # Corpo del messaggio: echo "Cookie ritornato dal browser: $HTTP_COOKIE"Gran parte del codice è occupato dalla funzione
setcookie()
perchè ha un comportamento un po' articolato.
La parte più difficile è la composizione corretta della data.
I parametri della funzione setcookie()
sono:
urlencode()
; naturalmente i valori
che poi saranno ritornati dal browser si dovranno riconvertire con
urldecode()
.
setcookie()
è
piuttosto ovvio: questa funzione deve essere richiamata mentre si
compone l'intestazione MIME della risposta: una direttiva
Set-Cookie
che apparisse nel corpo della risposta verrebbe
trattata dal browser come testo qualsiasi e quindi ignorato ai fini dei
cookie.
Il codice del nostro CGI d'esempio prosegue generando una risposta in
formato MIME come al solito, salvo che nell'intestazione richiamiamo la
funzione setcookie()
tre volte per settare altrettante
variabili con scadenza di 60 secondi (tempo sufficientemente breve per
vedere cosa succede senza invecchiare troppo).
Finalmente passiamo alla fase della sperimentazione sul campo: sul nostro
browser componiamo l'URL http://localhost/~pippo/prova6.cgi
:
alla prima invocazione il cookie non appare nella pagina scaricata,
anche se è già nella pancia del browser. Facciamo un reload della
pagina e, miracolosamente, ci appare il cookie con tutte le variabili settate.
Aspettiamo più di 60 secondi, quindi reload di nuovo: il cookie è
sparito! reload: il cookie è di nuovo qui!
Esercizi sui cookie:
Esercizio 1. Spiegare il motivo di questi cookie che appaiono e scompaiono, e in particolare spiegare perchè il cookie non viene presentato alla prima invocazione del CGI, o comunque dopo che è trascorso più di un minuto.
Esercizio 2. Il cookie ritornato al CGI nella variabile HTTP_COOKIE
può contenere diversi assegnamenti e diverse variabili: costruire una
funzione che permetta di estrarre questi valori. Come modello si può
seguire la funzione get_parameter()
.
Arrivati a questo punto abbiamo tutti gli strumenti per realizzare quella soluzione nota come gestione della sessione, di cui però non ci occuperemo qui.
urlencode.c - Semplice programma-filtro che converte testo ASCII in una forma url-encoded.
Scaricare i due programmi e compilare così:
# gcc urldecode.c -o urldecode # gcc urlencode.c -o urlencode
Se si ha accesso root al sistema, suggerisco di mettere i due programmi
nella dir. /bin
il cui PATH
è sicuramente
sempre disponibile. Altrimenti si dovrà specificare il pathfile
completo negli esempi riportati qui, cosa un po' scomoda.
Umberto Salsi | Commenti | Contatto | Mappa | Home / Indice sezione |
An abstract of the latest comments from the visitors of this page follows. Please, use the Comments link above to read all the messages or to add your contribute.
2007-01-19
by Vittorio "Molinari Bash" Cagnetta
Re: File Upload
Umberto Salsi wrote: [...] Anch'io, per quanto ne so su sed, non credo che possa lavorare su files binari. A quel programma sto lavorando io personalmente ( ma sempre con bash ) . Questo e' un estratto della funzione che uso: salva_file_di_upload() { # [...] boundary="$(echo ${CONTENT_TYPE} | cut -d '=' -f 2)" read riga_iniziale_dummy for (( i=1 ; i<=3 ; i++ )) do read riga if [ ${i} -eq 2 ] then IFS=$':' && array_2nda_riga=( ${riga} ) && myme_type="$(echo "${array_2nda_riga[1]}" | tr -d [:space:] )" && IFS=$' \t\n' elif [ ${i} -eq 3 ] then if [ "${myme_type}" = "${myme_type_consentito}" ] then [...] # #### ################# ################################ cat - | \ tac --separator="${boundary}" -b | \ tail -n +6 | tac --separator="--" -b | \ tail -c +5 | head -c -2 | \ head -c $(( ${dimensione_in_kb} * 1000)) 1> "${nome_file_uploadato}" ################################ ################# #### # else messaggio_di_avviso__cgi \ "1" "white" "red" \ "ERRORE : La var \"\${myme_type}\" ( =>[...][more...]
2006-12-16
by Umberto Salsi
Re: File Upload
Anonymous wrote: [...] No, non conosco un programma specifico per estrarre il contenuto della richiesta POST nel formato multipart/form-data. Non credo si possa usare il sed perché i dati binari contengono anche lo zero, che sed interpreta probabilmente come fine stringa. Idem per grep e compagnia. Forse, ma è tutto da provare, si può fare qualcosa con metamail per estrarre i vari pezzi MIME in tanti file. Ma è più probabile che si debba creare un programma specifico, magari scritto in C o altro linguaggio adatto che conosci. Non dovrebbe essere tanto difficile. Le specifiche del formato si trovano nel documento W3C che parla dell'HTML. Gli dai in pasto la riga del Content-Type e poi il body, e lui ti estrae tutti i pezzi nei file dal nome che corrisponde al nome del campo. ...facendo attenzione alla sicurezza: non sovrascrivere ciecamente file arbitrari! [more...]
2006-12-15
by Guest
File Upload
Scusa il mio linguaggio, peró non sono Italiano, perció provo esprimermi come meglio possa. Trovo questo sito molto ispirante. Ho gia scritto vari cgi col bash. Adesso ho provato scriverne uno per fare un file upload attraverso di un browser. Communque salvando sollo il POST input mi da un file que e alterato, cioé, contengono righe ascii como questa, prima dei dati del file: -----------------------------23281168279961 Content-Disposition: form-data; name="textline" -----------------------------23281168279961 Content-Disposition: form-data; name="datafile"; filename="base64.zip" Content-Type: text/html <qui sono i dati bianri> -----------------------------23281168279961 Per caso conosci un programmino per estrarre la parte relevante del file. So che si potrebbe fare col "sed", peró non so se coprirebbe tutte le possibilitá. Grazie, Leonardo [more...]
2006-11-08
by Vittorio "Molinari Bash" Cagnetta
Interfacciamento a un DBMS MySQL
Lavoro come amministratore di sistema, e mi sono trovato nella necessita' di dover scrivere, oltre ai classici scripts da mettere in cron, anche un programma web interattivo di utilita' per gli utenti ("Cambia_Password.cgi"); essendo l'unico linguaggio che conosco il bash, mi sono interessato alla tecnologia CGI ( purtroppo non conosco il php ). Considero questo sito, insieme con gli "Appunti di Informatica Libera", la mia prima fonte di ispirazione. Questo, unito al fatto di avere sempre a disposizione tante utili informazioni che Lei ha gentilmente condiviso rendono il suo sito molto prezioso... percio', visto che non l'ho fatto prima, la ringrazio della sua scelta "a' la Daniele Giacomini". Per "dare", in qualche modo, oltre che "ricevere", mi piacerebbe condividere con lei e con tutti gli appassionati di bash alcune funzioni che ho scritto relative alla gestione di un database MySQL, visto che questo aspetto era stato lasciato tra gli "aspetti ancora da considerare"... spero cosi' [...][more...]
2006-09-15
by Vittorio "Molinari Bash" Cagnetta
Re: Riconoscimento dei parametri "blind way" (senza sapere il loro nome)
Intanto grazie dell'ospitalita'. Mi sento onorato di essere ospitato in questo sito. Umberto Salsi wrote: [...] Sono senz'altro d'accordo con questa giusta affermazione, infatti ho rivisto il tutto, rinforzato il command substitution iniziale proteggendo il nome dei parametri, oltre che il loro valore: dati_dal_browser_remoto_lista="$(echo "${dati_dal_browser_remoto}" | tr '&' '\n' | sed -n s/'.*\(\<.*\>\)=\(.*\)'/'\1=\2'/p | /usr/local/bin/urldecode | sed s\-'$('-"x"-g | sed s\-'`'-""-g )". Alla fine, si ottiene semplicemente: eval parametro_arbitrario_1="valore_arbitrario_1" parametro_arbitrario_2="valore_arbitrario_2" ecc.ecc. che a mio avviso non presenta nessun rischio, dal momento che e' stato evitato un eventuale eval di un command substitution rimuovendo i caratteri: " `...` " oppure " $(...) ". Il massimo "rischio" che un eval del genere puo' comportare sarebbe avere un valore -associato al parametro- con caratteri non alfanumerici o binario... se mi sbaglio, avvertitemi. [...[...][more...]
2006-09-05
by Umberto Salsi
Re: Riconoscimento dei parametri "blind way" (senza sapere il loro nome)
Vittorio "Molinari Bash" Cagnetta wrote:
[...]
Quando in uno script compare "eval", si accende l'allarme giallo. Quando in uno script compare "eval" su parametri ricevuti dal client, si accende l'allarme rosso.
E poi mi sfugge una cosa: se il mio programma riceve 100 variabili dai nomi e dai valori arbitrari, che cosa se ne potrebbe fare?
Comunque, volendo proprio istanziare automaticamente delle variabili ricevute dal client, posso suggerire di aggiungere un prefisso convenzionale al nome della variabile, per esempio la variabile X diventa REQUEST_X. Pero' la cosa andrebbe fatta senza eval, che suscita troppi dubbi.
In alternativa, visto che bash supporta gli array, le variabili ricevute si potrebbero mettere in un array.[more...]
2006-09-04
by Vittorio "Molinari Bash" Cagnetta
Re: Riconoscimento dei parametri "blind way" (senza sapere il loro nome)
Anonymous wrote:
[...]
ERRATA CORRIGE
Ok, prometto di finirla qui...
vars_ENV=( $(set | sed -n s/'.*\(\<.*\>=\).*'/'\1'/p | sort -u | grep [[:upper:]] ) )
(Questo perche':
1) se un utente scrive "PATH" come VALORE del campo di un FORM ( e di
conseguenza di un parametro), il mio programma esce; in quest'altro modo viene controllato esplicitamente: "PATH=" [e non "PATH"] );
2) SE NON SI INDICA [[:upper:]], il comando "set" fornira' anche LE VARIABILI NORMALI del programma, e non solo quelle dell'environment => il risultato sarebbe che OGNI variabile porterebbe ad "exit".
Scusate per l'invadenza. L'ho fatto per dare anch'io un "contributo" utile a tutti...[more...]
2006-09-04
by Guest
Re: Riconoscimento dei parametri "blind way" (senza sapere il loro nome)
Anonymous wrote:
[...]
Come si sa, ci sono tre vulnerabilita' nei CGIs:
1) buffer overflow
2) command substitution ( aka 'system command' )
3) environmental variables ( per attacchi di tipo: DoS ).
Per la vurnerabilita' 1) => ci pensa il comando "read" [ l'ho testato con un input di 50,000,000 di caratteri ( si, proprio 50 milioni )... e non ha fatto una piega ];
per la vulnerabilita' 2) => tutto ok ( "[...] | sed s\-'$('-"x"-g |
[...] Il mio metodo ( a proposito, il comment era il mio ) rimane vulnerabile agli attacchi DoS basati sulle env variables [ infatti, un attacker potrebbe modificare il form cosi': "[...] IFS='$\t' " => disastro. ]
Questa e' la counter-measure:
vars_ENV=( $(set | sed -n s/'.*\(\<.*\>\)=.*'/'\1'/p | sort -u) )
attacked_env_or_not="$(for i in $(seq 0 $((${#vars_ENV[@]} - 1)) )
do
echo "${dati_dal_browser_remoto_lista}" | grep "${vars_ENV[${i}]}"
done )"
if [ $(echo "${attacked_env_or_not}" | tr -dc [:alnum:] | wc -c) -gt 0 ]
then
exit
[...][more...]
2006-08-24
by Guest
Riconoscimento dei parametri "blind way" (senza sapere il loro nome)
Innanzitutto grazie per il Sito, che e' molto utile.
Facciamo il caso che devo scrivere un programma cgi in Bash che ad ogni chiamata debba riconoscere i parametri dei vari forms HTML, SENZA SAPERE pero' non solo il valore da assegnare -ovviamente- ma anche COME SI CHIAMANO i parametri stessi (p.es. ciccio=1, franco=23 ; OPPURE a=giallo, b=rosso, c=verde, ecc.).
Questo metodo che ho chiamato "modo cieco" mi e' stato utile per creare UN UNICO file cgi che lancia differenti forms/pagine HTML a seconda del valore "hidden" della pagina di ritorno (diciamo che e' un metodo che punta al massimo della versatilita').
Ecco come ho fatto (SE pensi che ne possa minimamente valere la pena, ti do' con piacere il permesso di inserirlo nel tuo how-to "Apache: CGI scritti in Bash"):
"[...]
read dati_dal_browser_remoto
dati_dal_browser_remoto_lista="$(echo "${dati_dal_browser_remoto}" | tr '&' '\n' | grep '=' | grep -v "submit" | urldecode | sed s\-'$('-"x"-g | sed s\-'`'-""-g )"
#i due "sed" [...][more...]
2006-08-08
by Guest
cgi-bash
Hello, I'm writing in English as I don't write well in Italian. Hey this is a wonderful documentation on how to use bash for cgi scripts!
Thanks
George[more...]