Učini da pogrešan kôd izgleda pogrešno

From The Joel on Software Translation Project

(Difference between revisions)
Jump to: navigation, search
m
m (slijedeći -> sljedeći (jer se radi o pridjevu a ne o glagolskom prilogu sadašnjem). kodiran, kodiranje itd. nema naglaska ô)
 
Line 26: Line 26:
Ako ste programer početnik ili ako pokušavate pročitati kôd u novom jeziku on uvijek izgleda jednako nerazumljivo. Sve dok ne shvatite sam programski jezik ne možete vidjeti čak niti očite sintaksne pogreške.
Ako ste programer početnik ili ako pokušavate pročitati kôd u novom jeziku on uvijek izgleda jednako nerazumljivo. Sve dok ne shvatite sam programski jezik ne možete vidjeti čak niti očite sintaksne pogreške.
-
Tijekom prve faze učenja, počinjete prepoznavati nešto što obično nazivamo "stil kôdiranja". Tako počinjete primjećivati kôd koji ne zadovoljava standarde poravnavanja i čudno korištenje velikih i malih slova u imenima varijabli.
+
Tijekom prve faze učenja, počinjete prepoznavati nešto što obično nazivamo "stil kodiranja". Tako počinjete primjećivati kôd koji ne zadovoljava standarde poravnavanja i čudno korištenje velikih i malih slova u imenima varijabli.
-
To je trenutak kada kažete: "Hiljadu mu retorti, mi ovdje trebamo uvesti neke ujednačene konvencije kôdiranja." i provedete slijedeći dan pišući konvenciju kôdiranja za vaš tim i slijedećih šest dana u raspravi o Pravom i Jedinom Stilu Uvlačenja Zagrada i slijedećih tri tjedna prepisivajući stari kôd kako bi bio u skladu sa Pravim i Jedinim Stilom Uvlačenja Zagrada sve dok vas šef ne skuži i počne se derati na vas što trošite vrijeme na nešto što ne donosi novac, i  vi odlučite da i nije tako loša stvar ako kôd prepravite samo u slučaju kada ponovo naletite na njega, tako da imate samo djelomično Pravi i Jedini Stil Uvlačenja Zagrada i vrlo brzo zaboravite sve o tome i onda možete započeti opsjednutost s nečim drugim nevažnim za zarađivanje kao što je zamjena jedne vrste string klase sa drugom vrstom string klase.
+
To je trenutak kada kažete: "Hiljadu mu retorti, mi ovdje trebamo uvesti neke ujednačene konvencije kodiranja." i provedete sljedeći dan pišući konvenciju kodiranja za vaš tim i sljedećih šest dana u raspravi o Pravom i Jedinom Stilu Uvlačenja Zagrada i sljedećih tri tjedna prepisivajući stari kôd kako bi bio u skladu sa Pravim i Jedinim Stilom Uvlačenja Zagrada sve dok vas šef ne skuži i počne se derati na vas što trošite vrijeme na nešto što ne donosi novac, i  vi odlučite da i nije tako loša stvar ako kôd prepravite samo u slučaju kada ponovo naletite na njega, tako da imate samo djelomično Pravi i Jedini Stil Uvlačenja Zagrada i vrlo brzo zaboravite sve o tome i onda možete započeti opsjednutost s nečim drugim nevažnim za zarađivanje kao što je zamjena jedne vrste string klase sa drugom vrstom string klase.
-
Kako postajete sve vještiji u pisanju kôda u određenoj okolini, počinjete učiti da primjećujete neke stvari. Stvari koje mogu biti savršeno legalne i savršeno u skladu sa konvencijama kôdiranja, ali koje vas zabrinjavaju.
+
Kako postajete sve vještiji u pisanju kôda u određenoj okolini, počinjete učiti da primjećujete neke stvari. Stvari koje mogu biti savršeno legalne i savršeno u skladu sa konvencijama kodiranja, ali koje vas zabrinjavaju.
Kao na primjer, u C-u:
Kao na primjer, u C-u:
Line 39: Line 39:
</code>
</code>
-
Ovo je legalan kôd; on može biti u skladu sa vašim konvencijama kôdiranja, on čak može raditi ono što je i zamišljeno, ali kada imate dovoljno iskustva u pisanje C kôda, vi primjećujete da je src deklariran kao običan char, i čak ako je to ono što ste htjeli, to ipak nije to. Taj kôd smrdi malo previše.
+
Ovo je legalan kôd; on može biti u skladu sa vašim konvencijama kodiranja, on čak može raditi ono što je i zamišljeno, ali kada imate dovoljno iskustva u pisanje C kôda, vi primjećujete da je src deklariran kao običan char, i čak ako je to ono što ste htjeli, to ipak nije to. Taj kôd smrdi malo previše.
Pogledajte ovaj još suptilniji primjer:
Pogledajte ovaj još suptilniji primjer:
Line 48: Line 48:
</code>
</code>
-
U ovom slučaju, kôd je 100% ispravan; on je u skladu sa većinom konvencija kôdiranja i ne postoji ništa pogrešno u njemu, ali vam činjenica da tijelo if naredbe (koje se sastoji od jedne naredbe) nije omeđeno vitičastim zagradama može smetati, zato što vam pada na pamet misao da bi jednog dana netko mogao tamo dodati još jednu liniju kôda:
+
U ovom slučaju, kôd je 100% ispravan; on je u skladu sa većinom konvencija kodiranja i ne postoji ništa pogrešno u njemu, ali vam činjenica da tijelo if naredbe (koje se sastoji od jedne naredbe) nije omeđeno vitičastim zagradama može smetati, zato što vam pada na pamet misao da bi jednog dana netko mogao tamo dodati još jednu liniju kôda:
<code>
<code>
Line 62: Line 62:
1. Ne razlikuješ čisto od nečistog.
1. Ne razlikuješ čisto od nečistog.
-
2. Imaš površnu ideju o čistoći, većinom na razini zadovoljavanja konvencija kôdiranja.
+
2. Imaš površnu ideju o čistoći, većinom na razini zadovoljavanja konvencija kodiranja.
3. Počinješ njušiti suptilne znakove nečistoće ispod površine i one te uznemiravaju toliko da izroniš i ispraviš kôd.
3. Počinješ njušiti suptilne znakove nečistoće ispod površine i one te uznemiravaju toliko da izroniš i ispraviš kôd.
Line 95: Line 95:
vaš sajt je ranjiv na XSS napade. To je sve što je potrebno.
vaš sajt je ranjiv na XSS napade. To je sve što je potrebno.
-
Umjesto toga vi morate korisnikov ulaz kôdirati prije nego što ga kopirate nazad u HTML. Kôdiranje znači zamjena " sa &amp;quot;, zamjena > sa &amp;gt;, i tako dalje. Tako da je
+
Umjesto toga vi morate korisnikov ulaz kôdirati prije nego što ga kopirate nazad u HTML. kodiranje znači zamjena " sa &amp;quot;, zamjena > sa &amp;gt;, i tako dalje. Tako da je
<code>Write "Hello, " & Encode(Request("name"))</code>
<code>Write "Hello, " & Encode(Request("name"))</code>
Line 103: Line 103:
Sav tekst koji potjeće od korisnika je opasan. Svaki opasan tekst ne smije biti stavljen u HTML bez da se kodira.
Sav tekst koji potjeće od korisnika je opasan. Svaki opasan tekst ne smije biti stavljen u HTML bez da se kodira.
-
Pokušajmo pronaći konvenciju kôdiranja koja će jamčiti da će kôd, ako ikad napravite ovu pogrešku,  izgledati pogrešno. Ako pogrešan kôd barem izgleda pogrešno tada postoji mogućnost da greška bude otkrivena od strane onog koji radi na tom kôdu ili onog koji ga pregledava.
+
Pokušajmo pronaći konvenciju kodiranja koja će jamčiti da će kôd, ako ikad napravite ovu pogrešku,  izgledati pogrešno. Ako pogrešan kôd barem izgleda pogrešno tada postoji mogućnost da greška bude otkrivena od strane onog koji radi na tom kôdu ili onog koji ga pregledava.
<font size="4">Moguće rješenje #1</font>
<font size="4">Moguće rješenje #1</font>
Line 115: Line 115:
Počinjete uvježbavati vaše oči da paze na gole Requestove, zato što oni krše konvenciju.
Počinjete uvježbavati vaše oči da paze na gole Requestove, zato što oni krše konvenciju.
-
To radi, u smislu da ako slijedite ovu konvenciju nikad nećete imati XSS bug, ali to nije nužno najbolja arhitektura. Na primjer možda želite spremiti ove stringove u bazu podataka i tada ih nema smisla spremiti HTML-kôdirane, zato što bi mogli završiti na neko drugo mjesto koje nije HTML stranica, kao što je to aplikacija za obradu kreditnih kartica koja bi se mogla zbuniti ako su stringovi HTML-kôdirani. Većina web aplikacija je razvijeno po načelu da svi interni stringovi nisu kôdirani sve do zadnjeg trenutka prije nego što će biti upisani u HTML stranicu, i to je vjerojatno ispravna arhitektura.
+
To radi, u smislu da ako slijedite ovu konvenciju nikad nećete imati XSS bug, ali to nije nužno najbolja arhitektura. Na primjer možda želite spremiti ove stringove u bazu podataka i tada ih nema smisla spremiti HTML-kodirane, zato što bi mogli završiti na neko drugo mjesto koje nije HTML stranica, kao što je to aplikacija za obradu kreditnih kartica koja bi se mogla zbuniti ako su stringovi HTML-kodirani. Većina web aplikacija je razvijeno po načelu da svi interni stringovi nisu kodirani sve do zadnjeg trenutka prije nego što će biti upisani u HTML stranicu, i to je vjerojatno ispravna arhitektura.
Zaista, potrebno je da držimo stvari u nesigurnom obliku neko vrijeme.
Zaista, potrebno je da držimo stvari u nesigurnom obliku neko vrijeme.
Line 123: Line 123:
<font size="4">Moguće rješenje #2</font>
<font size="4">Moguće rješenje #2</font>
-
Što ako uvedemo konvenciju kôdiranja koja kaže da string morate kôdirati u trenutku kada ga ispisujete?
+
Što ako uvedemo konvenciju kodiranja koja kaže da string morate kôdirati u trenutku kada ga ispisujete?
<code>
<code>
Line 149: Line 149:
</code>
</code>
-
Ali u tom slučaju &lt;br&gt;, koji bi trebao započeti novu liniju, biva kôdiran u &amp;lt;br&amp;gt; i korisniku se pojavljuje bukvalno kao tekst < b r >. To također nije ispravno.
+
Ali u tom slučaju &lt;br&gt;, koji bi trebao započeti novu liniju, biva kodiran u &amp;lt;br&amp;gt; i korisniku se pojavljuje bukvalno kao tekst < b r >. To također nije ispravno.
Dakle, ponekad ne možete kôdirati string kada ga čitate, a ponekad kada ga ispisujete, tako da niti jedan od prijedloga ne radi. A bez konvencije, još uvijek se izlažemo opasnosti da uradimo sljedeće:
Dakle, ponekad ne možete kôdirati string kada ga čitate, a ponekad kada ga ispisujete, tako da niti jedan od prijedloga ne radi. A bez konvencije, još uvijek se izlažemo opasnosti da uradimo sljedeće:
Line 169: Line 169:
</code>
</code>
-
Da li smo se sjetili kôdirati string? Ne postoji mjesto u koje možete pogledati kako bi vidjeli bug. Ne postoji mjesto za njuškanje. Ako imate puno kôda poput ovog, treba vam hrpa detektivskog posla da otkrijete izvor svakog stringa koji će biti ispisan prema vani kako biste bili sigurni da je kôdiran.
+
Da li smo se sjetili kôdirati string? Ne postoji mjesto u koje možete pogledati kako bi vidjeli bug. Ne postoji mjesto za njuškanje. Ako imate puno kôda poput ovog, treba vam hrpa detektivskog posla da otkrijete izvor svakog stringa koji će biti ispisan prema vani kako biste bili sigurni da je kodiran.
<font size="4">Pravo rješenje</font>
<font size="4">Pravo rješenje</font>
...
...

Latest revision as of 14:19, 25 January 2009

Joel Spolsky
Četvrtak, 11. svibnja 2005.


Daleke 1983. u mjesecu rujnu, počeo sam raditi moj prvi pravi posao, u velikoj tvornici kruha Oranim u Izraelu koja radi oko 100000 štruca kruha svake večeri u šest gigantskih peći veličine nosača aviona.

Prvi put kada sam ušao u pekaru nisam mogao vjerovati koliko je neuredno. Stranice peći bješe požutjele, strojevi zahrđali, masnoća je bila posvuda.

"Da li je uvijek tako neuredno?", pitao sam se.

"Što? O čemu to pričaš?”, šef se čudio. “Upravo smo završili čišćenje. Ovako čisto nije bilo tjednima.”

Hm!?

Trebalo mi je nekoliko mjeseci čišćenja pekare svako jutro prije nego što sam shvatio na što su mislili. U pekari, čisto je značilo da nema tijesta na strojevima. Čisto je značilo da nema plijesnivog tijesta u smeću. Čisto je značilo da nema tijesta na podu.

Čisto nije značilo da je boja na pećima bila lijepa i bijela. Bojanje peći je bilo nešto što se radilo jednom u 10 godina, a ne jednom dnevno. Čisto nije značilo da nema masnoće. Zapravo, bilo je puno strojeva koji su trebali biti redovno podmazani ili nauljeni i tanak sloj čistog ulja je obično bio znak da je stroj upravo bio očišćen.

DoughRounder.PNG

Čitav koncept čistog u pekari je nešto što je trebalo naučiti. Za laike je bilo nemoguće da ušetaju i procjene da li je prostor čist ili nije. Laiku nikad ne bi palo na pamet da pogleda u unutrašnje površine valjalice za tijesto (stroj koji valja kockaste blokove tijesta u lopte, prikazan je na slici desno) da vidi da li su savršeno čiste. Laik bi bio opterećen činjenicom da stara peć ima neravnomjerno obojane stranice, jer su te stranice bile ogromne. Ali pekara uopće ne zanima činjenica da je njihova pećnica izvana počela žutjeti. Okus kruha je još uvijek bio jednako dobar.

Nakon dva mjeseca provedenih u pekari naučili biste kako "vidjeti" čisto.

I za kôd vrijedi isto.

Ako ste programer početnik ili ako pokušavate pročitati kôd u novom jeziku on uvijek izgleda jednako nerazumljivo. Sve dok ne shvatite sam programski jezik ne možete vidjeti čak niti očite sintaksne pogreške.

Tijekom prve faze učenja, počinjete prepoznavati nešto što obično nazivamo "stil kodiranja". Tako počinjete primjećivati kôd koji ne zadovoljava standarde poravnavanja i čudno korištenje velikih i malih slova u imenima varijabli.

To je trenutak kada kažete: "Hiljadu mu retorti, mi ovdje trebamo uvesti neke ujednačene konvencije kodiranja." i provedete sljedeći dan pišući konvenciju kodiranja za vaš tim i sljedećih šest dana u raspravi o Pravom i Jedinom Stilu Uvlačenja Zagrada i sljedećih tri tjedna prepisivajući stari kôd kako bi bio u skladu sa Pravim i Jedinim Stilom Uvlačenja Zagrada sve dok vas šef ne skuži i počne se derati na vas što trošite vrijeme na nešto što ne donosi novac, i vi odlučite da i nije tako loša stvar ako kôd prepravite samo u slučaju kada ponovo naletite na njega, tako da imate samo djelomično Pravi i Jedini Stil Uvlačenja Zagrada i vrlo brzo zaboravite sve o tome i onda možete započeti opsjednutost s nečim drugim nevažnim za zarađivanje kao što je zamjena jedne vrste string klase sa drugom vrstom string klase.

Kako postajete sve vještiji u pisanju kôda u određenoj okolini, počinjete učiti da primjećujete neke stvari. Stvari koje mogu biti savršeno legalne i savršeno u skladu sa konvencijama kodiranja, ali koje vas zabrinjavaju.

Kao na primjer, u C-u:


   char* dest, src;

Ovo je legalan kôd; on može biti u skladu sa vašim konvencijama kodiranja, on čak može raditi ono što je i zamišljeno, ali kada imate dovoljno iskustva u pisanje C kôda, vi primjećujete da je src deklariran kao običan char, i čak ako je to ono što ste htjeli, to ipak nije to. Taj kôd smrdi malo previše.

Pogledajte ovaj još suptilniji primjer:

   if (i != 0) 
       foo(i);

U ovom slučaju, kôd je 100% ispravan; on je u skladu sa većinom konvencija kodiranja i ne postoji ništa pogrešno u njemu, ali vam činjenica da tijelo if naredbe (koje se sastoji od jedne naredbe) nije omeđeno vitičastim zagradama može smetati, zato što vam pada na pamet misao da bi jednog dana netko mogao tamo dodati još jednu liniju kôda:

   if (i != 0) 
       bar(i);
       foo(i);

... i zaboraviti da ubaci vitičaste zagrade, te tako slučajno učiniti da naredba foo(i) bude bezuvjetna! Dakle, kada vidite blokove kôda koji nisu omeđeni vitičastim zagradama, mogli biste osjetititi nečistoću koja u vama budi osjećaj nelagode.

OK, do sada sam spomenuo tri razine dostignuća u programera:

1. Ne razlikuješ čisto od nečistog.

2. Imaš površnu ideju o čistoći, većinom na razini zadovoljavanja konvencija kodiranja.

3. Počinješ njušiti suptilne znakove nečistoće ispod površine i one te uznemiravaju toliko da izroniš i ispraviš kôd.

Postoji čak i viša razina, a to je upravo ono o čemu zaista želim govoriti:

4. Namjerno stvaraš kôd na način da tvoj nos za nečistoću čini tvoj kôd vjerojatnijim da bude ispravan.

To je prava umjetnost: stvarati robustan kôd na način da pronalaziš konvencije koje će činiti da se greške istaknu na zaslonu.

Ovako, sad ću vas prošetati kroz mali primjer, nakon toga ću vam pokazati općenito pravilo koje možete upotrijebiti za pronalaženje konvencija koje kôd čine robustnim, na kraju to će nas dovesti do odbrane određene vrste Mađarske Notacije, vjerojatno ne one vrste koja izaziva mučninu, te do kritike izuzetaka u određenim okolnostima, premda vjerojatno ne u okolnostima u kojima se nalazite često.

Ali ako ste toliko uvjereni da je Mađarska Notacija loša stvar i da su izuzeci najbolji izum još od čokoladnog šejka i ako ne želite čuti bilo koje drugo mišljenje o tome, u redu, prošetajte do Roryija i pročitajte ovaj izvrstan kratki strip; ionako vam vjerojatno neće puno toga ovdje iznesenog nedostajati; u stvati uskoro ću prikazati stvarni kôd koji će vas možda uspavati čak i prije nego što vas naljuti. Tako je. Mislim da je plan da vas gotovo potpuno uljuljam u snove pa da vam onda prikradem Mađarska=Dobro, Izuzetci=Loše tezu u trenutku kad se više ne možete odhrvati.


Primjer

U redu. Krenimo sa primjerom. Recimo da pravite neku vrstu web aplikacije, jer su takve danas u modi.

E sad, postoji sigurnosna ranjivost koja se zove Cross Site Scripting Vulnerability, tj. XSS. Ne želim ići u detalje: sve što trebate znati je to da kada pravite web aplikaciju morate biti oprezni da nikad ne šaljete nazad tekst koji korisnik unosi u obrazcima.

Tako npr. ako imate web stranicu koja pita "Koje je vaše ime?" s okvirom za unos teksta i ako vas dostavljanje te stranice odvede na drugu stranicu koja ispiše "Zdravo, Elmer!" (pretpostavimo da je korisnikovo ime Elmer), dakle, to je sigurnosna ranjivost, zato što korisnik može unjeti bilo kakav podmukao HTML ili JavaScript umjesto Elmer i njihov podmukao JavaScript može učiniti razne gadosti, i te gadosti, tako se doima, dolaze od vas, tako na primjer on može pročitati cookije koje ste vi postavili tamo i proslijediti ih na zločesti sajt Dr. Zloćka.

Prikažimo to u pseudokôdu. Zasmislite da

s = Request("name")

čita ulaz (kao POST argument) od HTML obrasca. Ako ste ikad napisali ovakav kôd:

Write "Hello, " & Request("name")

vaš sajt je ranjiv na XSS napade. To je sve što je potrebno.

Umjesto toga vi morate korisnikov ulaz kôdirati prije nego što ga kopirate nazad u HTML. kodiranje znači zamjena " sa &quot;, zamjena > sa &gt;, i tako dalje. Tako da je

Write "Hello, " & Encode(Request("name"))

savršeno sigurno.

Sav tekst koji potjeće od korisnika je opasan. Svaki opasan tekst ne smije biti stavljen u HTML bez da se kodira.

Pokušajmo pronaći konvenciju kodiranja koja će jamčiti da će kôd, ako ikad napravite ovu pogrešku, izgledati pogrešno. Ako pogrešan kôd barem izgleda pogrešno tada postoji mogućnost da greška bude otkrivena od strane onog koji radi na tom kôdu ili onog koji ga pregledava.

Moguće rješenje #1

Jedno rješenje je da kôdiramo sve stringove istog trentutka kada dođu od korisnika:

s = Encode(Request("name"))

Tako da naša konvencija glasi ovako: ako vidite Request koji nije omeđen sa Encode, kôd mora da je pogrešan.

Počinjete uvježbavati vaše oči da paze na gole Requestove, zato što oni krše konvenciju.

To radi, u smislu da ako slijedite ovu konvenciju nikad nećete imati XSS bug, ali to nije nužno najbolja arhitektura. Na primjer možda želite spremiti ove stringove u bazu podataka i tada ih nema smisla spremiti HTML-kodirane, zato što bi mogli završiti na neko drugo mjesto koje nije HTML stranica, kao što je to aplikacija za obradu kreditnih kartica koja bi se mogla zbuniti ako su stringovi HTML-kodirani. Većina web aplikacija je razvijeno po načelu da svi interni stringovi nisu kodirani sve do zadnjeg trenutka prije nego što će biti upisani u HTML stranicu, i to je vjerojatno ispravna arhitektura.

Zaista, potrebno je da držimo stvari u nesigurnom obliku neko vrijeme.

OK. Pokušati ću još jednom.

Moguće rješenje #2

Što ako uvedemo konvenciju kodiranja koja kaže da string morate kôdirati u trenutku kada ga ispisujete?

   s = Request("name")
   
   // much later:
   Write Encode(s)

Sada kadgod vidite "goli" Write bez Encode vi znate da nešto nedostaje.

Ali to baš i ne radi kako treba...jer ponekad imate male dijelove HTML-a na nekim mjestima u vašem kôdu koje ne želite kôdirati:

   If mode = "linebreak" Then prefix = "<br>"
   
   // much later:
   Write prefix

Ovo izgleda pogrešno prema našoj konvenciji, koja od nas zahtjeva da kôdiramo stringove prije ispisa:

   Write Encode(prefix)

Ali u tom slučaju <br>, koji bi trebao započeti novu liniju, biva kodiran u &lt;br&gt; i korisniku se pojavljuje bukvalno kao tekst < b r >. To također nije ispravno.

Dakle, ponekad ne možete kôdirati string kada ga čitate, a ponekad kada ga ispisujete, tako da niti jedan od prijedloga ne radi. A bez konvencije, još uvijek se izlažemo opasnosti da uradimo sljedeće:

   s = Request("name")
   
   ...pages later...
   name = s
   
   ...pages later...
   recordset("name") = name // store name in db in a column "name"
   
   ...days later...
   theName = recordset("name")
   
   ...pages or even months later...
   Write theName

Da li smo se sjetili kôdirati string? Ne postoji mjesto u koje možete pogledati kako bi vidjeli bug. Ne postoji mjesto za njuškanje. Ako imate puno kôda poput ovog, treba vam hrpa detektivskog posla da otkrijete izvor svakog stringa koji će biti ispisan prema vani kako biste bili sigurni da je kodiran.

Pravo rješenje

...

Personal tools