Home / Indice sezione
 www.icosaedro.it 

 Apache: CGI scritti in Bash

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.

Indice

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

Glossarietto

Chi ha già una certa pratica di queste cose, può saltare subito al paragrafo successivo. Chi invece trova qualche difficoltà di orientamento nel mare di sigle dell'informatica, potrebbe trarne qualche chiarimento.

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).

Un primo esempio

Darò per scontato che Apache sia correttamente installato e configurato, e che sia già predisposto per eseguire programmi CGI sfruttando il meccanismo SUEXEC come descritto nelle sezioni precedenti. Facciamo subito la prova con un CGI minimale scritto in Bash che si limita a stampare la data corrente:
        #!/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:

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.

Debugging dei CGI

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.

CGI che restituiscono pagine HTML

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.

CGI di test

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.

Metodo GET

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.

Metodo POST

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:

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ò.

Soluzione generale per GET e POST

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

Riconoscere i parametri della query HTTP con Bash

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.

Gestire un DB

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:

Chiave:

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!

CGI che ritornano immagini

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.).

Sicurezza e diritti di accesso

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

Sessioni con i cookie

L'intestazione MIME della risposta del WEB server può contenere il settaggio di un cookie nel browser. Ecco un esempio di risposta del server con una richiesta di questo tipo:
        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:

Un ultimo fatto da notare riguardo alla funzione 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.

Conclusioni

Ci sarebbero molti aspetti ancora da considerare: l'interfacciamento a un DBMS come potrebbe essere MySQL o PostgreSQL; la manipolazione delle immagini; la rotazione dei banner; l'interfacciamento a un POP server per la posta elettronica; l'uso avanzato dei cookie; ecc. Il mio intento era quello di dimostrare che Bash si dimostra molto utile per risolvere molti piccoli problemi pratici e fare dei test. Linguaggi più specializzati e più sofisticati, magari più ricchi di librerie pronte all'uso, rimangono strumenti indispensabili per i progetti più complessi.

Appendice

urldecode.c - Semplice programma-filtro che converte dalla codifica url-encoded alla forma ASCII normale.

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.

Argomenti correlati


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...]