Hallitse Python CGI -ohjelmointi alusta alkaen. Tämä syvällinen opas kattaa asennuksen, lomakkeiden käsittelyn, tilanhallinnan, tietoturvan ja sen roolin nykyaikaisessa webissä.
Python CGI-ohjelmointi: Kattava opas verkkorajapintojen rakentamiseen
Nykyaikaisessa verkkokehityksen maailmassa, jota hallitsevat edistyneet kehykset, kuten Django, Flask ja FastAPI, termi CGI (Common Gateway Interface) saattaa kuulostaa kaiulta menneestä aikakaudesta. CGI:n sivuuttaminen tarkoittaa kuitenkin perustavanlaatuisen teknologian ohittamista, joka ei ainoastaan pyörittänyt varhaista dynaamista webiä, vaan tarjoaa edelleen arvokkaita oppitunteja ja käytännön sovelluksia. CGI:n ymmärtäminen on kuin moottorin toiminnan ymmärtäminen ennen auton ajamisen oppimista; se antaa syvän, perustavanlaatuisen tiedon asiakas-palvelin-vuorovaikutuksesta, joka on kaikkien verkkosovellusten pohjana.
Tämä kattava opas selventää Python CGI -ohjelmointia. Tutustumme siihen perusperiaatteista lähtien ja näytämme, kuinka rakennat dynaamisia, interaktiivisia verkkorajapintoja käyttäen vain Pythonin standardikirjastoja. Olitpa sitten opiskelija, joka oppii verkon perusteita, kehittäjä, joka työskentelee vanhojen järjestelmien parissa, tai joku, joka toimii rajoitetussa ympäristössä, tämä opas antaa sinulle taidot hyödyntää tätä tehokasta ja suoraviivaista teknologiaa.
Mitä CGI on ja miksi se on edelleen tärkeää?
Common Gateway Interface (CGI) on standardiprotokolla, joka määrittelee, kuinka verkkopalvelin voi olla vuorovaikutuksessa ulkoisten ohjelmien, usein kutsuttujen CGI-skriptien, kanssa. Kun asiakas (kuten verkkoselain) pyytää tiettyä URL-osoitetta, joka liittyy CGI-skriptiin, verkkopalvelin ei vain tarjoile staattista tiedostoa. Sen sijaan se suorittaa skriptin ja välittää skriptin tulosteen takaisin asiakkaalle. Tämä mahdollistaa dynaamisen sisällön luomisen käyttäjän syötteen, tietokantakyselyiden tai minkä tahansa muun skriptin sisältämän logiikan perusteella.
Ajattele sitä keskusteluna:
- Asiakas palvelimelle: "Haluaisin nähdä resurssin `/cgi-bin/process-form.py` ja tässä on joitakin tietoja täyttämästäni lomakkeesta."
- Palvelin CGI-skriptille: "Sinulle on tullut pyyntö. Tässä ovat asiakkaan tiedot ja tietoa pyynnöstä (kuten heidän IP-osoitteensa, selaimensa jne.). Suorita ja anna minulle vastaus lähetettäväksi takaisin."
- CGI-skripti palvelimelle: "Olen käsitellyt tiedot. Tässä ovat HTTP-otsikot ja HTML-sisältö palautettavaksi."
- Palvelin asiakkaalle: "Tässä on pyytämäsi dynaaminen sivu."
Vaikka modernit kehykset ovat abstrahoineet tämän raa'an vuorovaikutuksen, perusperiaatteet pysyvät samoina. Joten miksi oppia CGI:tä korkean tason kehysten aikakaudella?
- Perustavanlaatuinen ymmärrys: Se pakottaa sinut oppimaan HTTP-pyyntöjen ja -vastausten ydinkoneistot, mukaan lukien otsikot, ympäristömuuttujat ja tietovirrat, ilman "taikaa". Tämä tieto on korvaamatonta minkä tahansa verkkosovelluksen virheenkorjauksessa ja suorituskyvyn optimoinnissa.
- Yksinkertaisuus: Yksittäisen, eristetyn tehtävän osalta pienen CGI-skriptin kirjoittaminen voi olla huomattavasti nopeampaa ja yksinkertaisempaa kuin kokonaisen kehysprojektin pystyttäminen reitityksineen, malleineen ja ohjaimineen.
- Kielineutraali: CGI on protokolla, ei kirjasto. Voit kirjoittaa CGI-skriptejä Pythonilla, Perlillä, C++:lla, Rustilla tai millä tahansa kielellä, joka osaa lukea standardisyötteestä ja kirjoittaa standarditulosteeseen.
- Vanhat järjestelmät ja rajoitetut ympäristöt: Monet vanhemmat verkkosovellukset ja jotkut jaetun hostingin ympäristöt luottavat CGI:hen tai tarjoavat tukea vain sille. Tämän kanssa työskentelyn osaaminen voi olla kriittinen taito. Se on myös yleistä sulautetuissa järjestelmissä, joissa on yksinkertaisia verkkopalvelimia.
CGI-ympäristön määrittäminen
Ennen kuin voit ajaa Python CGI-skriptiä, tarvitset verkkopalvelimen, joka on määritetty suorittamaan sen. Tämä on yleisin kompastuskivi aloittelijoille. Kehitystä ja oppimista varten voit käyttää suosittuja palvelimia, kuten Apachea tai jopa Pythonin sisäänrakennettua palvelinta.
Edellytykset: Verkkopalvelin
Tärkeintä on kertoa verkkopalvelimellesi, että tietyn hakemiston (perinteisesti nimeltään `cgi-bin`) tiedostoja ei pidä tarjoilla tekstinä, vaan ne tulee suorittaa, ja niiden tulosteen tulee lähettää selaimeen. Vaikka tietyt määritysvaiheet vaihtelevat, yleiset periaatteet ovat universaaleja.
- Apache: Sinun on tyypillisesti otettava käyttöön `mod_cgi` ja käytettävä `ScriptAlias`-direktiiviä määritystiedostossasi URL-polun yhdistämiseksi tiedostojärjestelmähakemistoon. Tarvitset myös `Options +ExecCGI`-direktiivin kyseiseen hakemistoon suorituksen sallimiseksi.
- Nginx: Nginxillä ei ole suoraa CGI-moduulia kuten Apachella. Se käyttää tyypillisesti siltaa, kuten FCGIWrapia, CGI-skriptien suorittamiseen.
- Pythonin `http.server`: Yksinkertaiseen paikalliseen testaukseen voit käyttää Pythonin sisäänrakennettua verkkopalvelinta, joka tukee CGI:tä "out of the box". Voit käynnistää sen komentoriviltäsi: `python3 -m http.server --cgi 8000`. Tämä käynnistää palvelimen portissa 8000 ja käsittelee kaikki `cgi-bin/`-alihakemistossa olevat skriptit suoritettavina.
Ensimmäinen "Hei, Maailma!" Python CGI:ssä
CGI-skriptillä on hyvin tietty tulostusmuoto. Sen on ensin tulostettava kaikki tarvittavat HTTP-otsikot, jota seuraa yksi tyhjä rivi, ja sitten sisältöosa (esim. HTML).
Luodaan ensimmäinen skriptimme. Tallenna seuraava koodi nimellä `hello.py` `cgi-bin`-hakemistoosi.
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# 1. HTTP-otsikko
# Tärkein otsikko on Content-Type, joka kertoo selaimelle, minkätyyppistä dataa odottaa.
print("Content-Type: text/html;charset=utf-8")
# 2. Tyhjä rivi
# Yksi tyhjä rivi on ratkaisevan tärkeä. Se erottaa otsikot sisältöosasta.
print()
# 3. Sisältöosa
# Tämä on todellinen HTML-sisältö, joka näytetään selaimessa.
print("<h1>Hei, Maailma!</h1>")
print("<p>Tämä on ensimmäinen Python CGI -skriptini.</p>")
print("<p>Se toimii globaalilla verkkopalvelimella, kaikkien käytettävissä!</p>")
Puristetaan tämä osiin:
#!/usr/bin/env python3
: Tämä on "shebang"-rivi. Unix-tyyppisissä järjestelmissä (Linux, macOS) se kertoo käyttöjärjestelmälle, että tämä tiedosto suoritetaan Python 3 -tulkin avulla.print("Content-Type: text/html;charset=utf-8")
: Tämä on HTTP-otsikko. Se ilmoittaa selaimelle, että seuraava sisältö on HTML:ää ja se on koodattu UTF-8:lla, mikä on välttämätöntä kansainvälisten merkkien tukemiseksi.print()
: Tämä tulostaa pakollisen tyhjän rivin, joka erottaa otsikot leipätekstistä. Tämän unohtaminen on hyvin yleinen virhe.- Viimeiset `print`-lausekkeet tuottavat HTML:n, jonka käyttäjä näkee.
Lopuksi sinun on tehtävä skriptistä suoritettava. Linuxissa tai macOS:ssä suorittaisit tämän komennon päätteessäsi: `chmod +x cgi-bin/hello.py`. Nyt kun navigoit selaimessasi osoitteeseen `http://your-server-address/cgi-bin/hello.py`, sinun pitäisi nähdä "Hei, Maailma!" -viestisi.
CGI:n ydin: Ympäristömuuttujat
Miten verkkopalvelin välittää tietoja pyynnöstä skriptillemme? Se käyttää ympäristömuuttujia. Nämä ovat palvelimen skriptin suoritusympäristöön asettamia muuttujia, jotka tarjoavat runsaasti tietoa saapuvasta pyynnöstä ja itse palvelimesta. Tämä on "Gateway" (yhdyskäytävä) Common Gateway Interfacessa.
Tärkeimmät CGI-ympäristömuuttujat
Pythonin `os`-moduulin avulla voimme käyttää näitä muuttujia. Tässä on joitakin tärkeimpiä:
REQUEST_METHOD
: HTTP-menetelmä, jota käytettiin pyynnössä (esim. 'GET', 'POST').QUERY_STRING
: Sisältää datan, joka lähetettiin URL-osoitteen "?"-merkin jälkeen. Näin dataa välitetään GET-pyynnössä.CONTENT_LENGTH
: Pyynnön rungossa lähetetyn datan pituus, jota käytetään POST-pyynnöissä.CONTENT_TYPE
: Pyynnön rungossa olevan datan MIME-tyyppi (esim. 'application/x-www-form-urlencoded').REMOTE_ADDR
: Pyynnön tekevän asiakkaan IP-osoite.HTTP_USER_AGENT
: Asiakkaan selaimen user-agent -merkkijono (esim. 'Mozilla/5.0...').SERVER_NAME
: Palvelimen isäntänimi tai IP-osoite.SERVER_PROTOCOL
: Käytetty protokolla, kuten 'HTTP/1.1'.SCRIPT_NAME
: Polku parhaillaan suoritettavaan skriptiin.
Käytännön esimerkki: Diagnostiikkaskripti
Luodaan skripti, joka näyttää kaikki saatavilla olevat ympäristömuuttujat. Tämä on uskomattoman hyödyllinen työkalu virheenkorjaukseen. Tallenna tämä nimellä `diagnostics.py` `cgi-bin`-hakemistoosi ja tee siitä suoritettava.
#!/usr/bin/env python3
import os
print("Content-Type: text/html\n")
print("<h1>CGI-ympäristömuuttujat</h1>")
print("<p>Tämä skripti näyttää kaikki verkkopalvelimen välittämät ympäristömuuttujat.</p>")
print("<table border='1' style='border-collapse: collapse; width: 80%;'>")
print("<tr><th>Muuttuja</th><th>Arvo</th></tr>")
# Käy läpi kaikki ympäristömuuttujat ja tulosta ne taulukkoon
for key, value in sorted(os.environ.items()):
print(f"<tr><td>{key}</td><td>{value}</td></tr>")
print("</table>")
Kun suoritat tämän skriptin, näet yksityiskohtaisen taulukon, jossa luetellaan kaikki tiedot, jotka palvelin on välittänyt skriptillesi. Yritä lisätä kyselymerkkijono URL-osoitteeseen (esim. `.../diagnostics.py?name=test&value=123`) ja tarkkaile, kuinka `QUERY_STRING`-muuttuja muuttuu.
Käyttäjän syötteen käsittely: Lomakkeet ja data
CGI:n ensisijainen tarkoitus on käsitellä käyttäjän syötteitä, tyypillisesti HTML-lomakkeista. Pythonin standardikirjasto tarjoaa vankat työkalut tähän. Katsotaan, kuinka käsitellä kahta pääasiallista HTTP-metodia: GET ja POST.
Luodaan ensin yksinkertainen HTML-lomake. Tallenna tämä tiedosto nimellä `feedback_form.html` pääverkkohakemistoosi (ei cgi-bin-hakemistoon).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Globaali palautelomake</title>
</head>
<body>
<h1>Lähetä palautteesi</h1>
<p>Tämä lomake demonstroi sekä GET- että POST-menetelmiä.</p>
<h2>GET-menetelmäesimerkki</h2>
<form action="/cgi-bin/form_handler.py" method="GET">
<label for="get_name">Nimesi:</label>
<input type="text" id="get_name" name="username">
<br/><br/>
<label for="get_topic">Aihe:</label>
<input type="text" id="get_topic" name="topic">
<br/><br/>
<input type="submit" value="Lähetä GET:llä">
</form>
<hr>
<h2>POST-menetelmäesimerkki (lisäominaisuudet)</h2>
<form action="/cgi-bin/form_handler.py" method="POST">
<label for="post_name">Nimesi:</label>
<input type="text" id="post_name" name="username">
<br/><br/>
<label for="email">Sähköpostisi:</label>
<input type="email" id="email" name="email">
<br/><br/>
<p>Oletko tyytyväinen palveluumme?</p>
<input type="radio" id="happy_yes" name="satisfaction" value="yes">
<label for="happy_yes">Kyllä</label><br>
<input type="radio" id="happy_no" name="satisfaction" value="no">
<label for="happy_no">Ei</label><br>
<input type="radio" id="happy_neutral" name="satisfaction" value="neutral">
<label for="happy_neutral">Neutraali</label>
<br/><br/>
<p>Mistä tuotteista olet kiinnostunut?</p>
<input type="checkbox" id="prod_a" name="products" value="Product A">
<label for="prod_a">Tuote A</label><br>
<input type="checkbox" id="prod_b" name="products" value="Product B">
<label for="prod_b">Tuote B</label><br>
<input type="checkbox" id="prod_c" name="products" value="Product C">
<label for="prod_c">Tuote C</label>
<br/><br/>
<label for="comments">Kommentit:</label><br>
<textarea id="comments" name="comments" rows="4" cols="50"></textarea>
<br/><br/>
<input type="submit" value="Lähetä POST:lla">
</form>
</body>
</html>
Tämä lomake lähettää tietonsa skriptiin nimeltä `form_handler.py`. Nyt meidän on kirjoitettava tämä skripti. Vaikka voisitkin manuaalisesti jäsentää `QUERY_STRING`:iä GET-pyyntöihin ja lukea standardisyötteestä POST-pyyntöihin, tämä on virhealtista ja monimutkaista. Sen sijaan meidän tulisi käyttää Pythonin sisäänrakennettua `cgi`-moduulia, joka on suunniteltu juuri tähän tarkoitukseen.
cgi.FieldStorage
-luokka on tässä sankari. Se jäsentää saapuvan pyynnön ja tarjoaa sanakirjamaisen rajapinnan lomaketietoihin, riippumatta siitä, lähetettiinkö ne GET- vai POST-menetelmällä.
Tässä on koodi `form_handler.py`-tiedostolle. Tallenna se `cgi-bin`-hakemistoosi ja tee siitä suoritettava.
#!/usr/bin/env python3
import cgi
import html
# Luo FieldStorage-instanssi
# Tämä yksi objekti käsittelee sekä GET- että POST-pyyntöjä läpinäkyvästi
form = cgi.FieldStorage()
# Aloita vastauksen tulostaminen
print("Content-Type: text/html\n")
print("<h1>Lomakkeen lähetys vastaanotettu</h1>")
print("<p>Kiitos palautteestasi. Tässä vastaanottamamme tiedot:</p>")
# Tarkista, lähetettiinkö lomaketietoja
if not form:
print("<p><em>Lomaketietoja ei lähetetty.</em></p>")
else:
print("<table border='1' style='border-collapse: collapse;'>")
print("<tr><th>Kentän nimi</th><th>Arvo(t)</th></tr>")
# Käy läpi kaikki lomakedatan avaimet
for key in form.keys():
# TÄRKEÄÄ: Desinfioi käyttäjän syöte ennen sen näyttämistä XSS-hyökkäysten estämiseksi.
# html.escape() muuntaa merkit kuten <, >, & niiden HTML-entiteeteiksi.
sanitized_key = html.escape(key)
# .getlist()-metodia käytetään käsittelemään kenttiä, joilla voi olla useita arvoja,
# kuten valintaruutuja. Se palauttaa aina listan.
values = form.getlist(key)
# Desinfioi jokainen arvo listassa
sanitized_values = [html.escape(v) for v in values]
# Yhdistä arvot listasta pilkulla erotetuksi merkkijonoksi näyttöä varten
display_value = ", ".join(sanitized_values)
print(f"<tr><td><strong>{sanitized_key}</strong></td><td>{display_value}</td></tr>")
print("</table>")
# Esimerkki yksittäisen arvon suorasta käyttämisestä
# Käytä form.getvalue('key') kentissä, joissa odotat vain yhtä arvoa.
# Se palauttaa None, jos avainta ei ole olemassa.
username = form.getvalue("username")
if username:
print(f"<h2>Tervetuloa, {html.escape(username)}!</h2>")
Tärkeimmät asiat tästä skriptistä:
- `import cgi` ja `import html`: Tuomme tarvittavat moduulit. `cgi` lomakkeiden jäsentämiseen ja `html` tietoturvaan.
- `form = cgi.FieldStorage()`: Tämä yksi rivi tekee kaiken raskaan työn. Se tarkistaa ympäristömuuttujat (`REQUEST_METHOD`, `CONTENT_LENGTH` jne.), lukee asianmukaisen syötevirran ja jäsentää tiedot helppokäyttöiseksi objektiksi.
- Tietoturva ensin (`html.escape`): Emme koskaan tulosta käyttäjän lähettämää dataa suoraan HTML-sivullemme. Näin tekemällä luodaan Cross-Site Scripting (XSS) -haavoittuvuus. `html.escape()`-funktiota käytetään neutraloimaan kaikki haitallinen HTML tai JavaScript, jonka hyökkääjä saattaa lähettää.
- `form.keys()`: Voimme käydä läpi kaikki lähetetyt kenttien nimet.
- `form.getlist(key)`: Tämä on turvallisin tapa hakea arvoja. Koska lomake voi lähettää useita arvoja samalle nimelle (esim. valintaruudut), `getlist()` palauttaa aina listan. Jos kentällä oli vain yksi arvo, se on lista, jossa on yksi kohde.
- `form.getvalue(key)`: Tämä on kätevä pikakuvake, kun odotat vain yhtä arvoa. Se palauttaa yksittäisen arvon suoraan, tai jos arvoja on useita, se palauttaa listan niistä. Se palauttaa `None`, jos avainta ei löydy.
Avaa nyt `feedback_form.html` selaimessasi, täytä molemmat lomakkeet ja katso, kuinka skripti käsittelee tietoja eri tavoin mutta tehokkaasti joka kerta.
Edistyneet CGI-tekniikat ja parhaat käytännöt
Tilanhallinta: Evästeet
HTTP on tilaton protokolla. Jokainen pyyntö on itsenäinen, eikä palvelimella ole muistia aiemmista saman asiakkaan pyynnöistä. Luodaksemme pysyvän käyttökokemuksen (kuten ostoskorin tai kirjautuneen istunnon), meidän on hallittava tilaa. Yleisin tapa tehdä tämä on evästeillä.
Eväste on pieni tiedonpala, jonka palvelin lähettää asiakkaan selaimelle. Selain lähettää sitten tämän evästeen takaisin jokaisen seuraavan pyynnön mukana samalle palvelimelle. CGI-skripti voi asettaa evästeen tulostamalla `Set-Cookie`-otsikon ja voi lukea saapuvia evästeitä `HTTP_COOKIE`-ympäristömuuttujasta.
Luodaan yksinkertainen kävijälaskuriskripti. Tallenna tämä nimellä `cookie_counter.py`.
#!/usr/bin/env python3
import os
import http.cookies
# Lataa olemassa olevat evästeet ympäristömuuttujasta
cookie = http.cookies.SimpleCookie(os.environ.get("HTTP_COOKIE"))
visit_count = 0
# Yritä hakea 'visit_count'-evästeemme arvo
if 'visit_count' in cookie:
try:
# Evästeen arvo on merkkijono, joten meidän on muutettava se kokonaisluvuksi
visit_count = int(cookie['visit_count'].value)
except ValueError:
# Käsittele tapaukset, joissa evästeen arvo ei ole kelvollinen numero
visit_count = 0
# Kasvata käyntien määrää
visit_count += 1
# Aseta eväste vastaukseen. Tämä lähetetään 'Set-Cookie'-otsikkona.
# Asetamme uuden arvon 'visit_count'-evästeelle.
cookie['visit_count'] = visit_count
# Voit myös asettaa evästeen ominaisuuksia, kuten vanhentumispäivämäärä, polku jne.
# cookie['visit_count']['expires'] = '...'
# cookie['visit_count']['path'] = '/'
# Tulosta Set-Cookie-otsikko ensin
print(cookie.output())
# Tulosta sitten normaali Content-Type-otsikko
print("Content-Type: text/html\n")
# Ja lopuksi HTML-runko
print("<h1>Evästepohjainen kävijälaskuri</h1>")
print(f"<p>Tervetuloa! Tämä on käyntinumerosi: <strong>{visit_count}</strong>.</p>")
print("<p>Päivitä tämä sivu nähdäksesi luvun kasvavan.</p>")
print("<p><em>(Selaimessasi on oltava evästeet käytössä, jotta tämä toimisi.)</em></p>")
Tässä Pythonin `http.cookies`-moduuli yksinkertaistaa `HTTP_COOKIE`-merkkijonon jäsentämistä ja `Set-Cookie`-otsikon luomista. Joka kerta kun vierailet tällä sivulla, skripti lukee vanhan laskurin, kasvattaa sitä ja lähettää uuden arvon takaisin tallennettavaksi selaimeesi.
CGI-skriptien virheenkorjaus: `cgitb`-moduuli
Kun CGI-skripti epäonnistuu, palvelin palauttaa usein yleisen "500 Internal Server Error" -viestin, mikä on hyödytön virheenkorjaukseen. Pythonin `cgitb` (CGI Traceback) -moduuli on hengenpelastaja. Ottamalla sen käyttöön skriptin alussa kaikki käsittelemättömät poikkeukset luovat yksityiskohtaisen, muotoillun raportin suoraan selaimeen.
Käyttääksesi sitä, lisää vain nämä kaksi riviä skriptisi alkuun:
import cgitb
cgitb.enable()
Varoitus: Vaikka `cgitb` on korvaamaton kehityksessä, sinun tulisi poistaa se käytöstä tai määrittää se kirjaamaan tiedostoon tuotantoympäristössä. Yksityiskohtaisten virhetracebackien paljastaminen yleisölle voi paljastaa arkaluonteisia tietoja palvelimesi kokoonpanosta ja koodista.
Tiedostojen lataukset CGI:llä
cgi.FieldStorage
-objekti käsittelee saumattomasti myös tiedostojen latauksia. HTML-lomake on konfiguroitava `method="POST"` ja, mikä tärkeintä, `enctype="multipart/form-data"`.
Luodaan tiedostojen latauslomake, `upload.html`:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tiedoston lataus</title>
</head>
<body>
<h1>Lataa tiedosto</h1>
<form action="/cgi-bin/upload_handler.py" method="POST" enctype="multipart/form-data">
<label for="userfile">Valitse ladattava tiedosto:</label>
<input type="file" id="userfile" name="userfile">
<br/><br/>
<input type="submit" value="Lataa tiedosto">
</form>
</body>
</html>
Ja nyt käsittelijä, `upload_handler.py`. Huom: Tämä skripti vaatii `uploads`-nimisen hakemiston samassa paikassa kuin skripti, ja verkkopalvelimella on oltava kirjoitusoikeudet siihen.
#!/usr/bin/env python3
import cgi
import os
import html
# Ota käyttöön yksityiskohtainen virheraportointi virheenkorjausta varten
import cgitb
cgitb.enable()
print("Content-Type: text/html\n")
print("<h1>Tiedoston latauksen käsittelijä</h1>")
# Hakemisto, johon tiedostot tallennetaan. TURVALLISUUS: Tämän tulee olla suojattu, ei-web-käytettävissä oleva hakemisto.
upload_dir = './uploads/'
# Luo hakemisto, jos sitä ei ole olemassa
if not os.path.exists(upload_dir):
os.makedirs(upload_dir, exist_ok=True)
# TÄRKEÄÄ: Aseta oikeat käyttöoikeudet. Todellisessa skenaariossa tämä olisi rajoittavampi.
# os.chmod(upload_dir, 0o755)
form = cgi.FieldStorage()
# Hae tiedostokohde lomakkeesta. 'userfile' on syöttökentän 'name'.
file_item = form['userfile']
# Tarkista, ladattiinko tiedosto todella
if file_item.filename:
# TURVALLISUUS: Älä koskaan luota käyttäjän antamaan tiedostonimeen.
# Se voi sisältää polkumerkkejä kuten '../' (hakemiston läpikävelyhyökkäys).
# Käytämme os.path.basenamea poistamaan kaikki hakemistotiedot.
fn = os.path.basename(file_item.filename)
# Luo täydellinen polku tiedoston tallentamiseksi
file_path = os.path.join(upload_dir, fn)
try:
# Avaa tiedosto binäärikirjoitustilassa ja kirjoita ladattu data
with open(file_path, 'wb') as f:
f.write(file_item.file.read())
message = f"Tiedosto '{html.escape(fn)}' ladattiin onnistuneesti!"
print(f"<p style='color: green;'>{message}</p>")
except IOError as e:
message = f"Virhe tiedoston tallentamisessa: {e}. Tarkista palvelimen oikeudet hakemistolle '{upload_dir}'."
print(f"<p style='color: red;'>{message}</p>")
else:
message = 'Tiedostoa ei ladattu.'
print(f"<p style='color: orange;'>{message}</p>")
print("<a href='/upload.html'>Lataa toinen tiedosto</a>")
Tietoturva: Ylin huolenaihe
Koska CGI-skriptit ovat suoritettavia ohjelmia, jotka ovat suoraan internetin kautta saatavilla, tietoturva ei ole vaihtoehto – se on vaatimus. Yksikin virhe voi johtaa palvelimen vaarantumiseen.
Syötteen validointi ja desinfiointi (XSS:n estäminen)
Kuten olemme jo nähneet, sinun ei pidä koskaan luottaa käyttäjän syötteeseen. Oleta aina, että se on haitallista. Kun näytät käyttäjän antamia tietoja HTML-sivulla, desinfioi ne aina `html.escape()`-toiminnolla Cross-Site Scripting (XSS) -hyökkäysten estämiseksi. Hyökkääjä voisi muuten syöttää `