Sesije u PHP-u. Izgradnja nevjerojatno jednostavnog sustava registracije u PHP-u i MySQL-u Gdje je sve počelo

💖 Sviđa li vam se? Podijelite vezu sa svojim prijateljima

Reg.ru: domene i hosting

Najveći registrar i pružatelj usluga hostinga u Rusiji.

Više od 2 milijuna naziva domena u službi.

Promocija, domenska pošta, poslovna rješenja.

Više od 700 tisuća kupaca diljem svijeta već je odabralo svoj izbor.

*Pređite mišem da biste pauzirali pomicanje.

Natrag naprijed

Izrada jednostavnog sustava registracije korisnika u PHP-u i MySQL-u

Stvaranje registracijskog sustava zahtijeva puno posla. Morate napisati kod koji potvrđuje adrese e-pošte, šalje e-poruku kojom potvrđuje registraciju, također potvrđuje valjanost ostalih polja obrazaca i još mnogo toga.

I nakon što sve ovo napišete, korisnici će se nerado registrirati, jer... ovo zahtijeva određeni napor s njihove strane.

U ovom vodiču ćemo stvoriti vrlo jednostavan sustav registracije koji uopće ne zahtijeva niti pohranjuje lozinke! Rezultat će biti lako modificirati i dodati postojećem PHP mjestu. Želite li saznati kako funkcionira? Pročitajte u nastavku.



Evo kako će funkcionirati naš super jednostavan sustav:

Kombinirat ćemo obrazac za autorizaciju i registraciju. Ovaj obrazac će imati polje za unos vaše e-mail adrese i gumb za registraciju;
- Prilikom popunjavanja polja s email adresom, klikom na gumb za registraciju kreirat će se zapis o novom korisniku, ali samo ako upisana email adresa nije pronađena u bazi.

Nakon toga se kreira nasumični jedinstveni skup znakova (token), koji se šalje na e-mail koji korisnik navede u obliku poveznice koja će biti relevantna 10 minuta;
- Link vodi korisnika na našu web stranicu. Sustav utvrđuje prisutnost tokena i autorizira korisnika;

Prednosti ovog pristupa:

Nema potrebe za pohranjivanjem lozinki ili potvrđivanjem polja;
- Nema potrebe za vraćanjem lozinke, sigurnosnih pitanja itd.;
- Od trenutka registracije/prijave korisnika, uvijek možete biti sigurni da će taj korisnik biti u vašoj pristupnoj zoni (da je e-mail adresa istinita);
- Nevjerojatno jednostavan proces registracije;

Mane:

Sigurnost korisničkog računa. Ako netko ima pristup korisnikovoj pošti, može se prijaviti.
- E-pošta nije sigurna i može se presresti. Imajte na umu da je ovo pitanje također relevantno u slučaju kada je lozinka zaboravljena i treba je vratiti ili u bilo kojem sustavu autorizacije koji ne koristi HTTPS za prijenos podataka (prijava/lozinka);
- Dok ispravno konfigurirate svoj poslužitelj e-pošte, postoji mogućnost da poruke s vezama za autorizaciju završe u spamu;

Uspoređujući prednosti i nedostatke našeg sustava, možemo reći da sustav ima visoku upotrebljivost (maksimalna pogodnost za krajnjeg korisnika), a istovremeno ima nizak pokazatelj sigurnosti.

Stoga je preporučljivo koristiti ga za registracije na forumima i servisima koji ne rade s važnim informacijama.

Kako koristiti ovaj sustav

U slučaju da samo trebate koristiti sustav za autorizaciju korisnika na vašoj web-lokaciji, a ne želite ovu lekciju uzeti u komadiće, evo što trebate učiniti:

Morate preuzeti izvore priložene lekciji
- Pronađite datoteku tables.sql u arhivi. Uvezite je u svoju bazu pomoću opcije uvoza u phpMyAdmin. Alternativni način: otvorite ovu datoteku kroz uređivač teksta, kopirajte SQL upit i izvršite ga;
- Otvorite include/main.php i ispunite postavke za povezivanje s vašom bazom (navedite korisnika i lozinku za povezivanje s bazom, kao i host i naziv baze). U istoj datoteci također morate navesti e-mail koji će se koristiti kao izvorna adresa za poruke koje šalje sustav. Neki hostovi blokiraju odlaznu e-poštu osim ako obrazac ne sadrži stvarnu adresu e-pošte, koja je stvorena na upravljačkoj ploči hosta, stoga navedite pravu adresu;
- Učitajte sve index.php , protected.php datoteke i sredstva te uključuje mape putem FTP-a na svoj host;
- Dodajte donji kod na svaku PHP stranicu na kojoj želite prikazati obrazac za prijavu;

Require_once "includes/main.php"; $korisnik = novi korisnik(); if(!$user->loggedIn())( preusmjeri("index.php"); )
- Spremni!

Za one koje zanima kako to sve funkcionira, pročitajte u nastavku!

Prvi korak je pisanje HTM koda za autorizacijski obrazac. Ovaj kod se nalazi u datoteci index.php. Ova datoteka također sadrži PHP kod koji obrađuje podatke obrazaca i druge korisne funkcije sustava za prijavu. Više o tome možete saznati u odjeljku u nastavku posvećenom pregledu PHP koda.

indeks.php

Vodič: Super jednostavan sustav registracije s PHP-om i MySQL-om Prijavite se ili registrirajte

Unesite svoju e-mail adresu iznad i poslat ćemo
imate link za prijavu.

Prijava/Registracija

U odjeljku head (između oznaka i) uključio sam glavne stilove (nisu obuhvaćeni ovim vodičem, pa ih možete pogledati sami. Folder assets/css/style.css). Prije završne oznake uključio sam jQuery biblioteku i datoteku script.js koju ćemo napisati i analizirati u nastavku.


JavaScript

jQuery prati stanje gumba "Registracija/Prijava" pomoću funkcije e.preventDefault() i šalje AJAX zahtjeve. Ovisno o odgovoru poslužitelja, prikazuje jednu ili drugu poruku i određuje daljnje radnje/

sredstva/js/script.js

$(function())( var form = $("#login-register"); form.on("submit", function(e)( if(form.is(".loading, .loggedIn"))( return false ; ) var email = form.find("input").val(), messageHolder = form.find("span"); e.preventDefault(); $.post(this.action, (e-mail: email), function (m)( if(m.error)( form.addClass("error"); messageHolder.text(m.message); ) else( form.removeClass("error").addClass("loggedIn"); messageHolder . text(m.message); ) )); )); $(document).ajaxStart(function())( form.addClass("loading"); )); $(document).ajaxComplete(function()) ( form. removeClass("učitavanje"); )); ));

je dodan u obrazac za prikaz trenutnog stanja AJAX zahtjeva (ovo je omogućeno zahvaljujući metodama ajaxStart()) I ajaxComplete(), koju možete pronaći pri kraju datoteke).

Ova klasa prikazuje rotirajuću animiranu GIF datoteku (kao da nam nagovještava da se zahtjev obrađuje), a također djeluje kao oznaka za sprječavanje ponovnog slanja obrasca (kada je gumb za registraciju već jednom kliknut). Klasa .loggedIn još je jedna zastavica - postavlja se kada je e-pošta poslana. Ova zastavica odmah blokira sve daljnje radnje s obrascem.

Shema baze podataka

Naš nevjerojatno jednostavan sustav bilježenja koristi 2 MySQL tablice (SQL kod je u datoteci tables.sql). Prvi pohranjuje podatke o korisničkim računima. Drugi pohranjuje podatke o broju pokušaja prijave.


Shema korisničke tablice.

Sustav ne koristi lozinke, kao što se može vidjeti na dijagramu. Na njemu možete vidjeti stupac tokena s tokenima uz stupac token_validity. Token se instalira čim se korisnik spoji na sustav i postavi svoju e-poštu za slanje poruke (više o tome u sljedećem bloku). Stupac token_validity postavlja vrijeme 10 minuta kasnije, nakon čega token više nije valjan.


Shema tablice koja broji broj pokušaja autorizacije.

U obje tablice, IP adresa je pohranjena u obrađenom obliku, korištenjem funkcije ip2long u polju tipa integer.

Sada možemo napisati neki PHP kod. Glavna funkcionalnost sustava dodijeljena je klasi User.class.php koju možete vidjeti ispod.

Ova klasa aktivno koristi idorm (docs), te su knjižnice minimalni potrebni alati za rad s bazama podataka. Upravlja pristupom bazi podataka, generiranjem tokena i provjerom valjanosti tokena. Omogućuje jednostavno sučelje koje olakšava povezivanje sustava za registraciju na vašu stranicu ako koristi PHP.

Korisnik.klasa.php

Class User( // Private ORM case private $orm; /** * Pronađite korisnika prema tokenu. Samo valjani tokeni se prihvaćaju u obzir. Token se generira samo 10 minuta od trenutka kada je kreiran * @param string $token . Ovo je onaj koji tražimo token * @return User Vrati vrijednost korisničke funkcije */ public static function findByToken($token)( // pronađite token u bazi podataka i provjerite je li postavljen točan vremenski žig $rezultat = ORM::for_table("reg_users") ->where ("token", $token) ->where_raw("token_validity > NOW()") ->find_one(); if(!$result)( return false; ) return new User($result); ) /** * Autorizirajte ili registrirajte korisnika * @param string $email. Adresa e-pošte korisnika * @return User */ public static function loginOrRegister($email)( // Ako je takav korisnik već postoji, vratite vrijednost funkcije User s navedene adrese e-pošte pohranjene u bazi podataka if(User::exists($email))( return new User($email); ) // U suprotnom, stvorite novog korisnika u bazi podataka i vrati vrijednost funkcije User::create iz navedene e-pošte return User::create($email ); ) /** * Stvorite novog korisnika i spremite u bazu podataka * @param string $email. Adresa e-pošte korisnika * @return User */ private static function create($email)( // Napišite novog korisnika i vratite rezultat korisničke funkcije iz ovih vrijednosti $result = ORM::for_table("reg_users")- >create(); $result->email = $email; $result->save(); return new User($result); ) /** * Provjerite postoji li takav korisnik u bazi podataka i vratite Booleovu vrijednost varijabla * @param string $email. Adresa e-pošte korisnika * @return boolean */ public static function exists($email)( // Postoji li korisnik u bazi podataka? $result = ORM::for_table("reg_users") ->where("email", $email ) ->count(); return $result == 1; ) /** * Stvorite novi korisnički objekt * @param instanca $param ORM , id, email ili 0 * @return User */ javna funkcija __construct($param = null) ( if($param instanceof ORM)( // ORM provjera prošla $this->orm = $param; ) else if(is_string($param))( // Provjera e-pošte prošla $this->orm = ORM:: for_table ("reg_users") ->where("email", $param) ->find_one(); ) else( $id = 0; if(is_numeric($param))( // vrijednost varijable $param je proslijeđen korisničkom identifikatoru $id = $param; ) else if(isset($_SESSION["loginid"]))( // Inače, pogledajte sesiju $id = $_SESSION["loginid"]; ) $this->orm = ORM::for_table( "reg_users") ->where("id", $id) ->find_one(); ) ) /** * Generiraj novi SHA1 autorizacijski token, zapisuje ga u bazu podataka i vraća njegovu vrijednost * @return string */ public function generateToken( )( // Generiraj token za ovlaštenog korisnika i spremi ga u bazu podataka $token = sha1($this->email.time().rand(0, 1000000)); // Spremite token u bazu podataka // I označite ga tako da vrijedi samo sljedećih 10 minuta $this->orm->set("token", $token); $this->orm->set_expr("token_validity", "ADDTIME(NOW(),"0:10")"); $this->orm->save(); vratiti $token; ) /** * Ovlasti korisnika * @return void */ public function login())( // Označi korisnika kao prijavljenog $_SESSION["loginid"] = $this->orm->id; // Ažurirajte vrijednost polja baze podataka last_login $this->orm->set_expr("last_login", "NOW()"); $this->orm->save(); ) /** * Uništi sesiju i odjavi korisnika * @return void */ public function logout ()( $_SESSION = array(); unset($_SESSION); ) /** * Provjerite je li korisnik prijavljen * @return boolean */ public function loggedIn())( return isset($this->orm->id) && $_SESSION["loginid"] == $this->orm->id; ) /** * Provjerava je li korisnik administrator * @return boolean */ public funkcija isAdmin())( return $this->rank() = = "administrator"; ) /** * Pronađite tip korisnika, može biti administrator ili regular * @return string */ javna funkcija rank())( ako ($this->orm->rank == 1)( return "administrator" "; ) return "regular"; ) /** * Metoda koja vam omogućuje da dobijete korisnikove privatne podatke kao * svojstva objekta User * @ param string $key Naziv svojstva koje dobiva pristup * @return mixed */ javna funkcija __get($key)( if(isset($this->orm->$key))( return $this->orm-> $ključ; ) vratiti null; ) )

Tokeni se generiraju pomoću SHA1 algoritma i pohranjuju u bazu podataka. Koristim MySQL-ove funkcije za mjerenje vremena za postavljanje vremenskog ograničenja od 10 minuta za valjanost tokena.

Kada je token potvrđen, izravno kažemo rukovatelju da razmatramo samo tokene koji još nisu istekli, pohranjene u stupcu token_validity.

Imajte na umu da koristim čarobnu metodu __dobiti knjižnica dokumenata na kraju datoteke za presretanje pristupa svojstvima objekta User.

Zahvaljujući tome, postaje moguće pristupiti informacijama pohranjenim u bazi podataka zahvaljujući svojstvima $user->email, $user->token, itd. U sljedećem fragmentu koda, pogledat ćemo kako koristiti ove klase kao primjer .


Zaštićena stranica

Druga datoteka koja pohranjuje korisne i potrebne funkcije je datoteka functions.php. Postoji nekoliko takozvanih pomoćnika - pomoćnih funkcija koje vam omogućuju stvaranje čišćeg i čitljivijeg koda u drugim datotekama.

funkcije.php

Funkcija send_email($from, $to, $subject, $message)( // Pomoćnik koji šalje e-poštu $headers = "MIME-Version: 1.0" . "\r\n"; $headers .= "Content-type: text /plain; charset=utf-8" . "\r\n"; $headers .= "From: ".$from . "\r\n"; povratna pošta($to, $subject, $message, $headers ); ) funkcija get_page_url())( // Određivanje URL-a PHP datoteke $url = "http".(empty($_SERVER["HTTPS"])?"":"s")."://" .$_SERVER ["SERVER_NAME"]; if(isset($_SERVER["REQUEST_URI"]) && $_SERVER["REQUEST_URI"] != "")( $url.= $_SERVER["REQUEST_URI"]; ) else( $url. = $_SERVER["PATH_INFO"]; ) return $url; ) function rate_limit($ip, $limit_hour = 20, $limit_10_min = 10)( // Broj pokušaja prijave u posljednjih sat vremena na ovu IP adresu $ count_hour = ORM: :for_table("reg_login_attempt") ->where("ip", sprintf("%u", ip2long($ip))) ->where_raw("ts > SUBTIME(NOW(),"1:00 ")") ->count(); // Broj pokušaja prijave u zadnjih 10 minuta na ovoj IP adresi $count_10_min = ORM::for_table("reg_login_attempt") ->where("ip", sprintf("%u) ", ip2long($ ip))) ->where_raw("ts > SUBTIME(NOW(),"0:10")") ->count(); if($count_hour > $limit_hour || $count_10_min > $limit_10_min)( throw new Exception("Previše pokušaja prijave!"); ) ) function rate_limit_tick($ip, $email)( // Stvori novi zapis u tablici koji broji broj pokušaja prijave $login_attempt = ORM::for_table("reg_login_attempt")->create(); $login_attempt->email = $email; $login_attempt->ip = sprintf("%u", ip2long($ip )); $login_attempt->save(); ) function redirect($url)( header("Location: $url"); exit; )

Funkcije granica_stope I kvačica_ograničenja_stope pratiti broj pokušaja autorizacije u proteklom vremenskom razdoblju od prvog pokušaja. Pokušaj prijave bilježi se u bazi podataka u stupcu reg_login_attempt. Ove se funkcije pozivaju kada se podaci obrasca obrađuju i šalju kao što možete vidjeti iz sljedećeg isječka koda.

Kod u nastavku preuzet je iz datoteke index.php i upravlja slanjem obrasca. Vraća JSON odgovor, koji zauzvrat obrađuje jQuery u datoteci assets/js/script.js koju smo ranije pogledali.

indeks.php

Try( if(!empty($_POST) && isset($_SERVER["HTTP_X_REQUESTED_WITH"]))( // Izlaz zaglavlja JSON zaglavlja("Content-type: application/json"); // Je li ova adresa e-pošte važeća ako (!isset($_POST["email"]) || !filter_var($_POST["email"], FILTER_VALIDATE_EMAIL))( throw new Exception("Molimo unesite valjanu e-poštu."); ) // Provjerite. Je li korisniku dopuštena prijava, je li premašio broj dopuštenih veza? (datoteka functions.php za više informacija) rate_limit($_SERVER["REMOTE_ADDR"]); // Zabilježi ovaj pokušaj prijave rate_limit_tick($_SERVER["REMOTE_ADDR"] , $ _POST["email"]); // Pošalji e-poštu korisniku $message = ""; $email = $_POST["email"]; $subject = "Vaša veza za prijavu"; if(!User:: exists($email) )( $subject = "Hvala na registraciji!"; $message = "Hvala vam na registraciji na našoj stranici!\n\n"; ) // Pokušaj autorizacije ili registracije korisnika $user = Korisnik ::loginOrRegister($_POST[ "email"]);$message.= "Možete se prijaviti s ovog URL-a:\n"; $message.= get_page_url()."?tkn=".$user->generateToken()."\n\n"; $message.= "Veza će automatski isteći nakon 10 minuta."; $result = send_email($fromEmail, $_POST["email"], $subject, $message); if(!$result)( throw new Exception("Došlo je do pogreške prilikom slanja vaše e-pošte. Pokušajte ponovno."); ) die(json_encode(array("message" => "Hvala! Poslali smo vam poveznicu) u svoju pristiglu poštu. Provjerite i mapu neželjene pošte."))); ) ) catch(Iznimka $e)( die(json_encode(array("error"=>1, "message" => $e->getMessage() )));)

Nakon uspješne prijave/registracije, gornji kod će korisniku poslati poveznicu za prijavu. Token postaje dostupan jer prosljeđuje se kao varijabla u generiranoj vezi metodom $_GET s tkn markerom

indeks.php

If(isset($_GET["tkn"]))( // Je li ovaj token valjan za autorizaciju? $user = Korisnik::findByToken($_GET["tkn"]); if($user)( // Da, je. Preusmjeri na zaštićenu stranicu $user->login(); preusmjeri("protected.php"); ) // Ne, token nije valjan. Preusmjeri na stranicu s obrascem za autorizaciju/registraciju preusmjeri("index. php"); )

$user->login()

će stvoriti potrebne varijable za sesiju, tako da će korisnik, koji gleda sljedeće stranice stranice, ostati ovlašten cijelo vrijeme.

Obrada funkcije za izlazak iz sustava uređena je na sličan način.

indeks.php

If(isset($_GET["logout"]))( $user = new User(); if($user->loggedIn())( $user->logout(); ) redirect("index.php") ;)

Na kraju koda ponovno postavljam preusmjeravanje na index.php, pa parametar ?odjava=1 prenositi putem URL-a nije potrebno.

Naša datoteka index.php zahtijeva dodatne. zaštita - ne želimo da ljudi koji su se prijavili u sustav ponovno vide obrazac za registraciju. U te svrhe koristimo metodu $user->loggedIn().

indeks.php

$korisnik = novi korisnik(); if($user->loggedIn())( redirect("protected.php"); )

Konačno, ovdje je dio koda koji vam omogućuje da zaštitite stranice svoje stranice i učinite ih dostupnima samo nakon autorizacije.

zaštićeno.php

// Kako biste zaštitili svaku stranicu na svojoj web stranici, uključite datoteku main.php // i stvorite novi objekt User. Tako je to jednostavno! require_once "uključuje/main.php"; $korisnik = novi korisnik(); if(!$user->loggedIn())( redirect("index.php"); )

Nakon ove provjere možete biti sigurni da je korisnik uspješno autoriziran. Također možete pristupiti pohranjenim informacijama u bazi podataka pomoću svojstava objekta $korisnik. Za prikaz e-pošte i statusa korisnika koristite ovaj kod:

Echo "Vaša e-pošta: ".$user->email; echo "Vaš rang: ".$user->rank();

metoda rang() ovdje se koristi jer baza podataka obično pohranjuje brojeve (0 za običnog korisnika, 1 za administratora) i te podatke trebamo pretvoriti u statuse kojima pripadaju, u čemu nam ova metoda pomaže.

Da biste običnog korisnika postavili za administratora, jednostavno uredite unos korisnika putem phpMyAdmina (ili bilo kojeg drugog programa koji vam omogućuje upravljanje bazama podataka). Status administratora ne daje nikakve privilegije; u ovom primjeru stranica će prikazati da ste administrator - i to je to.

Ali što učiniti s ovim prepušteno je vašem nahođenju; možete sami napisati i sastaviti kod koji postavlja određene privilegije i mogućnosti za administratore.

Gotovi smo!

Završili smo s ovim nevjerojatno super kvazi jednostavnim oblikom! Možete ga koristiti na svojim PHP stranicama, vrlo je jednostavan. Također ga možete modificirati za sebe i napraviti ga onako kako želite.

Materijal je pripremio Denis Malyshok posebno za web stranicu

p.s. Želite li napredovati u savladavanju PHP-a i OOP-a? Obratite pozornost na vrhunske lekcije o raznim aspektima izrade web stranica, uključujući programiranje u PHP-u, kao i besplatni tečaj o stvaranju vlastitog CMS sustava u PHP-u od nule pomoću OOP-a:

Svidio vam se materijal i želite mi se zahvaliti?
Samo podijelite sa svojim prijateljima i kolegama!


Danas ćemo pogledati iskorištavanje kritične jednodnevne ranjivosti u popularnom CMS Joomla, koja je eksplodirala na Internetu krajem listopada. Govorit ćemo o ranjivostima s brojevima CVE-2016-8869, CVE-2016-8870 i CVE-2016-9081. Sva tri dolaze iz jednog dijela koda koji je čamio u dubini okvira dugih pet godina, čekajući da se završi, da bi se onda oslobodio i sa sobom donio kaos, hakirana mjesta i suze nevinih korisnika ove Joomle. Samo najhrabriji i najhrabriji programeri, čije su oči crvene od svjetla monitora, a čije su tipkovnice pune mrvica kruha, uspjeli su izazvati bijesne zle duhove i položiti svoje glave na oltar popravaka.

UPOZORENJE Sve informacije služe samo u informativne svrhe. Ni urednici ni autor nisu odgovorni za bilo kakvu moguću štetu prouzročenu materijalima ovog članka. Gdje je sve počelo

Demis Palma je 6. listopada 2016. napravio temu na Stack Exchangeu u kojoj je pitao: zašto, zapravo, u Joomla verziji 3.6 postoje dvije metode za registraciju korisnika s istim imenom register()? Prvi je u kontroleru UsersControllerRegistration, a drugi je u kontroleru UsersControllerUser. Damis je htio znati je li metoda UsersControllerUser::register() negdje korištena ili je to samo evolucijski anakronizam zaostao iz stare logike. Brinuo ga je da čak i ako ovu metodu ne koristi niti jedan prikaz, ona se može pozvati izrađenim upitom. Na što sam dobio odgovor od programera pod nadimkom itoctopus, koji je potvrdio: problem stvarno postoji. I poslao izvješće programerima Joomle.

Tada su se događaji najbrže razvijali. Dana 18. listopada Joomla programeri prihvatili su izvješće Damisa, koji je do tada izradio PoC koji bi omogućio registraciju korisnika. Objavio je bilješku na svojoj web stranici, gdje je općenito govorio o problemu koji je zatekao i svojim razmišljanjima o tome. Istog dana izlazi nova verzija Joomle 3.6.3, koja još uvijek sadrži ranjivi kod.

Nakon toga, Davide Tampellini vrti bug do te mjere da registrira ne jednostavnog korisnika, već administratora. A 21. listopada novi slučaj stiže Joomla sigurnosnom timu. Tu se već govori o povećanju privilegija. Istog dana na web stranici Joomla pojavljuje se obavijest da će u utorak, 25. listopada, biti objavljena sljedeća verzija sa serijskim brojem 3.6.3, koja ispravlja kritičnu ranjivost u jezgri sustava.

25. listopada Joomla Security Strike Team pronalazi najnoviji problem koji je stvorio dio koda koji je otkrio Damis. Zatim se obveza s datumom 21. listopada s neupadljivim nazivom Prepare 3.6.4 Stable Release gura u glavnu granu službenog repozitorija Joomla, čime se popravlja nesretna pogreška.

Nakon ovog izlaska, brojni zainteresirani pojedinci pridružuju se zajednici programera - počinju promovirati ranjivost i pripremati exploite.

Dana 27. listopada, istraživač Harry Roberts učitava gotovu eksploataciju u repozitorij Xiphos Research koja može učitati PHP datoteku na poslužitelj s ranjivim CMS-om.

pojedinosti

Pa, pozadina je gotova, idemo na najzanimljiviji dio - analizu ranjivosti. Instalirao sam Joomlu 3.6.3 kao testnu verziju, tako da će svi brojevi redaka biti relevantni za ovu verziju. I svi putovi do datoteka koje ćete vidjeti ispod bit će naznačeni u odnosu na korijen instaliranog CMS-a.

Zahvaljujući otkriću Damis Palme, znamo da postoje dvije metode kojima se vrši registracija korisnika u sustavu. Prvi koristi CMS i nalazi se u datoteci /components/com_users/controllers/registration.php:108. Drugi (onaj koji ćemo morati pozvati) živi u /components/com_users/controllers/user.php:293. Pogledajmo ga pobliže.

286: /** 287: * Metoda registracije korisnika. 288: * 289: * @return boolean 290: * 291: * @od 1.6 292: */ 293: public function register() 294: ( 295: JSession::checkToken("post") ili jexit(JText::_ ("JINVALID_TOKEN")); ... 300: // Dobivanje podataka obrasca. 301: $data = $this->input->post->get("user", array(), "array"); . .. 315: $return = $model->validate($form, $data); 316: 317: // Provjerite pogreške. 318: if ($return === false) 319: ( ... 345: / / Završite registraciju 346: $return = $model->register($data);

Ovdje sam ostavio samo zanimljive retke. Puna verzija ranjive metode može se vidjeti u repozitoriju Joomla.

Razmotrimo što se događa tijekom normalne registracije korisnika: koji se podaci šalju i kako se obrađuju. Ako je registracija korisnika omogućena u postavkama, obrazac se može pronaći na http://joomla.local/index.php/component/users/?view=registration.


Zahtjev za legitimnu registraciju korisnika izgleda kao na sljedećoj snimci zaslona.


Za rad s korisnicima odgovorna je komponenta com_users. Obratite pozornost na parametar zadatka u zahtjevu. Ima format $controller.$method. Pogledajmo strukturu datoteke.

Nazivi skripti u mapi kontrolera odgovaraju nazivima pozvanih kontrolera. Budući da naš zahtjev sada ima $controller = "registration" , bit će pozvana datoteka registration.php i njezina metoda register().

Pažnja, pitanje: kako prenijeti obradu registracije na ranjivo mjesto u kodu? Vjerojatno ste već pogodili. Imena ranjive i prave metode su ista (registar), tako da samo trebamo promijeniti ime pozvanog kontrolera. Gdje se nalazi naš ranjivi kontroler? Tako je, u datoteci user.php. Ispada $controller = "user" . Spajajući sve zajedno dobivamo task = user.register . Sada se zahtjev za registraciju obrađuje metodom koja nam je potrebna.


Druga stvar koju trebamo učiniti je poslati podatke u ispravnom formatu. Ovdje je sve jednostavno. Legitimni register() očekuje od nas niz pod nazivom jform, u koji prosljeđujemo registracijske podatke - ime, prijavu, lozinku, e-poštu (pogledajte snimak zaslona sa zahtjevom).

  • /components/com_users/controllers/registration.php: 124: // Dobivanje korisničkih podataka. 125: $requestData = $this->input->post->get("jform", array(), "array");

Naš klijent dobiva ove podatke iz niza koji se zove korisnik.

  • /components/com_users/controllers/user.php: 301: // Dobivanje podataka obrasca. 302: $data = $this->input->post->get("user", array(), "array");

Stoga mijenjamo nazive svih parametara u zahtjevu iz jfrom u user.

Naš treći korak je pronaći važeći CSRF token, jer bez njega neće biti registracije.

  • /components/com_users/controllers/user.php: 296: JSession::checkToken("post") ili jexit(JText::_("JINVALID_TOKEN"));

Izgleda kao MD5 hash, a možete ga uzeti, na primjer, iz obrasca za autorizaciju na stranici /index.php/component/users/?view=login .


Sada možete kreirati korisnike željenom metodom. Ako je sve funkcioniralo, onda čestitamo - upravo ste iskoristili ranjivost CVE-2016-8870 "nedostaje provjera dopuštenja za registraciju novih korisnika".

Ovako to izgleda u "radnoj" metodi register() iz kontrolera UsersControllerRegistration:

  • /components/com_users/controllers/registration.php: 113: // Ako je registracija onemogućena - Preusmjerava na stranicu za prijavu. 114: if (JComponentHelper::getParams("com_users")->get("allowUserRegistration") == 0) 115: ( 116: $this->setRedirect(JRoute::_("index.php?option=com_users&view=) prijava", lažno)); 117: 118: vrati lažno; 119: )

I tako u ranjivim:

  • /components/com_users/controllers/user.php:

Da, nema šanse.

Da bismo razumjeli drugi, puno ozbiljniji problem, pošaljimo zahtjev koji smo kreirali i vidimo kako se izvršava u različitim dijelovima koda. Ovdje je dio koji je odgovoran za provjeru valjanosti podataka koje je korisnik poslao u metodi radnika:

Nastavak je dostupan samo članovima Opcija 1. Pridružite se zajednici “site” kako biste pročitali sve materijale na stranici

Članstvo u zajednici unutar navedenog razdoblja omogućit će vam pristup SVIM hakerskim materijalima, povećati vaš osobni kumulativni popust i omogućiti vam da skupite profesionalnu ocjenu Xakep Score!

Odlučio sam napisati ovu bilješku jer sam umoran od odgovaranja na istu stvar 100 500 puta na Q&A.

Mnogi web programeri početnici se prije ili kasnije suoče sa zadatkom uvođenja čovjeku čitljivih poveznica (HUR) na svoju web stranicu. Prije implementacije CNC-a, svi linkovi su izgledali kao /myscript.php ili čak /myfolder/myfolder2/myscript3.php, što je teško zapamtiti i još je gore za SEO. Nakon implementacije CNC-a, veze imaju oblik /statiya-o-php ili čak na ćirilici /article-o-php.

Govoreći o SEO-u. Čovjeku čitljive veze STVARNO su izmišljene ne za jednostavno pamćenje, već uglavnom za povećanje mogućnosti indeksiranja stranice, jer podudarnost upita za pretraživanje i dijela URL-a daje dobru prednost u poretku pretraživanja.

Evolucija PHP programera početnika može se izraziti sljedećim nizom koraka:

  • Postavljanje običnog PHP koda u zasebne datoteke i pristup tim datotekama putem poveznica poput /myfolder/myscript.php
  • Razumijevanje da sve skripte imaju značajan zajednički dio (na primjer, stvaranje veze s bazom podataka, čitanje konfiguracije, pokretanje sesije itd.) i, kao posljedica toga, stvaranje zajedničke početne "ulazne" točke, neke skripte koja prihvaća SVE zahtjeve, a zatim bira internu skriptu za povezivanje. Obično se ova skripta zove index.php i nalazi se u korijenu, zbog čega svi zahtjevi (aka URL-ovi) izgledaju ovako: /index.php?com=myaction&com2=mysubaction
  • Potreba za implementacijom usmjerivača i prelazak na veze čitljive ljudima.
  • Napominjem da između točke 2 i 3 većina programera čini očitu pogrešku. Neću pogriješiti ako ovo nazovem vrijednošću oko 95% programera. Čak i većina poznatih okvira sadrži ovu grešku. A sastoji se u sljedećem.

    Umjesto implementacije temeljno novog načina obrade poveznica, pogrešno je napravljen koncept "zakrpa i preusmjeravanja" temeljen na .htaccessu, koji se sastoji od stvaranja mnogih pravila preusmjeravanja pomoću mod_rewrite. Ovi redovi uspoređuju URL s nekim regularnim izrazom i, ako postoji podudaranje, guraju vrijednosti ekstrahirane iz URL-a u GET varijable, naknadno pozivajući isti index.php.

    #Incorrect CNC method RewriteEngine On RewriteRule ^\/users\/(.+)$ index.php?module=users&id=$1 #....Puno više sličnih pravila...

    Ovaj koncept ima mnogo nedostataka. Jedan od njih je otežano kreiranje pravila, veliki postotak ljudskih grešaka prilikom dodavanja pravila koje je teško detektirati, ali dovode do greške servera 500.

    Drugi nedostatak je što se često uređuje na temelju konfiguracije poslužitelja, što je samo po sebi besmislica. I ako se u Apacheu konfiguracija može "zakrpati" pomoću .htaccess, onda u popularnom nginxu nema takve opcije, sve se nalazi u zajedničkoj konfiguracijskoj datoteci u zoni sustava.

    I još jedan nedostatak, vjerojatno najvažniji, je da je ovim pristupom nemoguće dinamički konfigurirati usmjerivač, odnosno "u hodu", algoritamski mijenjati i proširivati ​​pravila za odabir željene skripte.

    Dolje predložena metoda uklanja sve ove nedostatke. Već se koristi u velikom broju modernih okvira.

    Zaključak je da je početni zahtjev uvijek pohranjen u varijabli $_SERVER['REQUEST_URI'], to jest, može se pročitati unutar index.php i analizirati kao niz pomoću PHP-a sa svim rukovanjem pogreškama, dinamičkim preusmjeravanjem itd.

    U tom slučaju možete stvoriti samo jedno statičko pravilo u konfiguracijskoj datoteci, koje će sve zahtjeve preusmjeriti na nepostojeće datoteke ili mape na index.php.

    RewriteEngine On RewriteCond %(REQUEST_FILENAME) !-f #Ako datoteka ne postoji RewriteCond %(REQUEST_FILENAME) !-d #A ako mapa ne postoji RewriteRule ^.*$ index.php

    Štoviše, ovo se pravilo može postaviti iu .htaccess iu glavnu Apache konfiguracijsku datoteku.

    Za nginx, odgovarajuće pravilo će izgledati ovako:

    Lokacija / ( if (!-e $request_filename) ( prepišite ^/(.*)$ /index.php zadnji; ) )

    Jednostavno je.

    Sada pogledajmo dio PHP koda u index.php, koji analizira veze i odlučuje koju će skriptu pokrenuti.

    /dio 1/dio 2/dio 3

    Prva stvar koja pada na pamet je rastaviti ga pomoću explode(‘/’, $uri) i napraviti složeni usmjerivač temeljen na switch/case koji analizira svaki dio zahtjeva. Ne radi to! Ovo je komplicirano i na kraju učini da kôd izgleda užasno nerazumljiv i ne može se konfigurirati!

    Predlažem koncizniji način. Bolje je ne opisivati ​​ga riječima, već odmah prikazati kod.


    4. Trebate dodati jednu tablicu u istu bazu podataka. Pohranit će IP adrese koje su napravile pogreške prilikom prijave. Na ovaj način možemo ograničiti pristup onima koji su pogriješili više od tri puta zaredom na 15-ak minuta.Mislim da će programi koji biraju lozinke morati dugo petljati.
    Idemo na phpmyadmin i napravimo novu tablicu s 3 polja:


    ip - IP adresa.
    datum - datum neuspješne prijave u zadnjih 15 minuta za korisnika s ovom ip. col - broj pogrešaka u zadnjih 15 minuta za korisnika s ovom ip.
    Sjajno! Gotovo, sada promijenimo datoteku za potvrdu prijave i lozinke jer je sada naša lozinka šifrirana. Otvorite testreg.php i izbrišite sve osim uklanjanja razmaka iz prijave i lozinke. Zatim dodajemo sljedeći kod:

    //ukloni dodatne razmake
    $login = trim($login);
    $lozinka = trim($lozinka);

    // zamijeniti novim******************************************** *******
    // povezivanje s bazom podataka
    include("bd.php");// datoteka bd.php mora biti u istoj mapi kao i sve ostale, ako nije, samo promijenite putanju
    // mini-provjera za odabir lozinke
    $ip=getenv("HTTP_X_FORWARDED_FOR");
    if (prazno($ip) || $ip=="nepoznato") ( $ip=getenv("REMOTE_ADDR"); )//izdvoj ip
    mysql_query ("DELETE FROM oshibka WHERE UNIX_TIMESTAMP() - UNIX_TIMESTAMP(date) > 900");//izbrisati IP adrese korisnika koji su pogriješili prilikom prijave nakon 15 minuta.
    $result = mysql_query("SELECT col FROM oshibka WHERE ip="$ip"",$db); // dohvaćanje iz baze podataka broja neuspješnih pokušaja prijave u zadnjih 15 za korisnika s danim ip-om
    $myrow = mysql_fetch_array($rezultat);
    if ($myrow["col"] > 2) (
    //ako ima više od dvije greške, tj. tri, tada izdajemo poruku.
    exit("Unijeli ste svoje korisničko ime ili lozinku netočno 3 puta. Pričekajte 15 minuta prije ponovnog pokušaja.");
    }
    $password = md5($password);//kriptiraj lozinku
    $password = strrev($password);// za pouzdanost dodajte obrnuto
    $lozinka = $lozinka."b3p6f";
    //možete dodati nekoliko vlastitih znakova po svom ukusu, na primjer, unosom "b3p6f". Ako se ova lozinka hakira brutalnom silom na istom md5 poslužitelju, onda očito ništa dobro neće proizaći iz toga. Ali savjetujem vam da stavite druge znakove, možda na početak retka ili u sredini.
    //U ovom slučaju potrebno je povećati duljinu polja lozinke u bazi podataka. Šifrirana lozinka može biti mnogo veća.

    $result = mysql_query("SELECT * FROM users WHERE login="$login" AND password="$password"",$db); //dohvati iz baze sve podatke o korisniku s unesenom prijavom i lozinkom
    $myrow = mysql_fetch_array($rezultat);
    if (prazno($myrow["id"]))
    {
    //ako korisnik s unesenom prijavom i lozinkom ne postoji
    //Pravimo zapis da se ovaj ip nije mogao prijaviti.
    $select = mysql_query("SELECT ip FROM oshibka WHERE ip="$ip"");
    $tmp = mysql_fetch_row($select);
    if ($ip == $tmp) (//provjeri je li korisnik u tablici "oshibka"
    $result52 = mysql_query("SELECT col FROM oshibka WHERE ip="$ip"",$db);
    $myrow52 = mysql_fetch_array($result52);
    $col = $myrow52 + 1;//dodaj još jedan neuspješni pokušaj prijave
    mysql_query("UPDATE error SET col=$col,date=NOW() WHERE ip="$ip"");
    }
    drugo(
    mysql_query("INSERT INTO oshibka (ip,date,col) VRIJEDNOSTI ("$ip",NOW(),"1")");
    //ako nije bilo grešaka u zadnjih 15 minuta, tada umetnite novi unos u tablicu "oshibka"
    }

    exit("Nažalost, korisničko ime ili lozinka koje ste unijeli nisu točni.");
    }
    drugo(
    nbsp; //ako se lozinke podudaraju, pokrećemo sesiju za korisnika! Možete mu čestitati, ušao je!
    $_SESSION["password"]=$myrow["password"];
    $_SESSION["login"]=$myrow["login"];
    $_SESSION["id"]=$myrow["id"];//ovi podaci se vrlo često koriste, tako da će ih prijavljeni korisnik “nositi sa sobom”

    //Dalje spremamo podatke u kolačiće za naknadnu prijavu.
    //PAŽNJA!!! UČINITE OVO PO SVOJEM NAHOĐENJU BUDUĆI PODATCI SU POHRANJENI U KOLAČIĆIMA BEZ ENKRIPCIJE
    if ($_POST["save"] == 1) (
    //Ako korisnik želi da se njegovi podaci spremaju za kasniju prijavu, mi ih spremamo u kolačiće njegovog preglednika
    setcookie("prijava", $_POST["prijava"], vrijeme()+9999999);
    setcookie("lozinka", $_POST["lozinka"], vrijeme()+9999999);
    }}
    echo ""; // preusmjeravamo korisnika na glavnu stranicu, gdje ćemo ga obavijestiti o uspješnoj prijavi
    ?>

    5. U potpunosti ćemo promijeniti glavnu stranicu. Na njemu je potrebno prikazati avatar korisnika, prikazati poveznicu za odjavu s računa i dodati okvir za pamćenje lozinke prilikom prijave.
    Indeks.php




    Početna stranica


    Početna stranica



    6. Prijavljenim korisnicima potrebno je omogućiti odjavu. Na glavnoj stranici je već postojala veza za izlaz. Ali ova datoteka još ne postoji. Kreirajmo dakle novu datoteku exit.php s kodom:

    OK, sada je sve gotovo! Uživajte za svoje zdravlje! Sretno!

    U prošloj lekciji smo shvatili od kojih blokova će se sastojati predložak putovanja, tako da možemo krenuti s radom. Prvo, kreirajmo dvije mape:

    slike - ova će mapa sadržavati sve grafičke datoteke korištene za dizajn predloška. Jer Još uvijek nemamo razvoj dizajna, tada ispustite bilo koju grafičku datoteku u ovu mapu, inače Joomla neće instalirati predložak i javit će pogrešku ako je mapa prazna.

    PAŽNJA: Mapa sa slikama predloška ne sadrži grafički sadržaj!

    css - ova će mapa sadržavati kaskadne stilske datoteke. Najprije u nju postavimo praznu datoteku template.css koja će se koristiti za dodjeljivanje različitih stilova dizajna elementima stranice.

    Zatim možete početi stvarati glavnu datoteku index.php, koja će odrediti vizualni raspored elemenata stranice i reći Joomla CMS-u u koji blok treba smjestiti različite komponente i module. Datoteka je kombinacija PHP-a i HTML-a.

    Uvijek koristim samo Macromedia Dreamweaver kada pišem kod. Odličan program, toplo ga preporučam početnicima, jer... Ako ste pogriješili tijekom rada na kodu, program će svakako istaknuti vašu grešku.

    Na stranici ćete pronaći vodič za Macromedia Dreamweaver. Ako namjeravate razvijati web stranice, trebali biste svladati ovaj program, barem na početnoj razini, kako biste uredili kodove predložaka bez pogrešaka.

    Pozicioniranje elemenata stranice (blokova) vrši se pomoću HTML koda, konkretno koristit ćemo DIV oznake. Ali način na koji će naša stranica raditi na Joomla motoru, tj. Bit će dinamično, tada ćete također morati koristiti PHP jezik. Pomoću njega odredit ćemo u kojim blokovima će se nalaziti pozicije za ispisivanje modula, kako će se te pozicije zvati, hoće li se blokovi sažimati ili ne. Povezat ćemo stilske listove iz vanjskih datoteka, jezik sadržaja, postaviti kako će se mijenjati veličina stranice itd.

    indeks.php

    Zaglavlje datoteke

    Zaglavlje datoteke sastoji se od nekoliko dijelova. Prvi dio PHP koda zaglavlja osigurava da se datoteci ne pristupa izravno, iz sigurnosnih razloga.

    < ?php
    definirano ("_JEXEC" ) ili umri;
    JHtml::_("behavior.framework" , true );
    $app = JFactory::getApplication() ;
    ?>
    < ?php echo "< ?" ; ?>xml verzija="1.0" kodiranje="< ?php echo $this - >_charset ?> " ?>

    DOCTYPE je vrlo važan parametar na temelju kojeg preglednik odlučuje kako prikazati ovu stranicu i kako interpretirati CSS.

    < ! DOCTYPE html PUBLIC "- / / W3C/ / DTD XHTML 1.0 Strict/ / EN" "http:/ / www.w3.org/ TR/ xhtml1/ DTD/ xhtml1- strict.dtd" >

    Sljedeći isječak dohvaća instalirani jezik iz globalne konfiguracije.

    < html xmlns= "http:/ / www.w3.org/ 1999/ xhtml" xml:lang= "< ?php echo $this - >Jezik; ?> " lang="< ?php echo $this - >Jezik; ?> " dir = "< ?php echo $this - >smjer; ?> " >

    Slijedi dio koda koji uključuje dodatne informacije zaglavlja postavljene u globalnoj konfiguraciji. Ove informacije možete vidjeti ako pogledate izvorni kod bilo koje web stranice. Konkretno, to su meta oznake, za koje već znate.

    < head>
    < jdoc:include type= "head" / >

    Sljedeći redovi zaglavlja sadrže veze na glavne Joomla CSS stilove.

    < link rel= "stylesheet" href= "< ?php echo $this - >baseurl ?> / predlošci/ sustav / css/ sustav .css" type= "text/ css" / >
    < link rel= "stylesheet" href= "< ?php echo $this - >baseurl ?> / templates/ system / css/ general.css" type="text/ css" / >

    Za korištenje stilova dizajna predložaka, povezujemo se s datotekom koja sadrži kaskadne listove stilova template.css, koja se nalazi u mapi CSS. Nema veze što je ova datoteka za sada prazna, najvažnije je povezati je, dizajnom ćemo se pozabaviti kasnije, kada instaliramo predložak na Joomlu. Tako ćete lakše promatrati rezultat.

    < link rel= "stylesheet" href= "< ?php echo $this - >baseurl ?> /predlošci/< ?php echo $this - >predložak ?> / css/ predložak.css" tip= "tekst/ css" / >

    Sljedeći isječak koda omogućuje nam sažimanje lijevog ili desnog stupca ako nema modula smještenih na lijevoj i desnoj poziciji. Ako su oba stupca sažeta, sadržaj zauzima 100% širine stranice. Ako je uključen samo jedan stupac, tada sadržaj zauzima 80%. S dva omogućena stupca, sadržaj čini 60% širine stranice.

    < ?php
    if ($this - > countModules("left and right" ) = = 0) $contentwidth = "100" ;
    if ($this - > countModules("left or right" ) = = 1) $contentwidth = "80" ;
    if ($this - > countModules("left and right" ) = = 1) $contentwidth = "60" ;
    ?>

    Zaglavlje se zatvara

    < / head>

    < body>

    Blok “stranica” sadrži dizajn samo stranice web mjesta, koja će biti široka 950 px.

    < div id= "page" >

    Blok "top" nalazi se na samom vrhu stranice i sadrži dva bloka "logo" i "user1".

    < div id= "top" >

    U bokeh "logo" postavit ćemo grafičku datoteku logotipa; to će biti navedeno u listovima stilova. Ali automatski prikaz naziva stranice pišemo u datoteku index.php, a naziv postavljamo u oznaku H1, što je vrlo važno za optimizaciju tražilice.

    < div id= "logo" >
    < h1> < ?php echo $app - >getCfg("namename" ); ?>< / h1>
    < / div>

    Definirajmo poziciju “user1” u istoimenom bloku za prikaz modula za pretraživanje stranice.

    < div id= "user1" >
    < jdoc:include type= "modules" name= "user1" style= "xhtml" / >
    < / div>
    < / div> < ! - - конец блока top - - >

    Izlaz horizontalnog modula izbornika u bloku "korisnik2" na poziciji "korisnik2". Blok će se srušiti ako na tom mjestu nema modula.

    < ?php if ($this - >countModules("user2" ) : ?>
    < div id= "user2 " >
    < jdoc:include type= "modules" name= "user2" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Slijedi blok zaglavlja stranice. U njemu ćemo definirati poziciju "zaglavlja" za prikaz modula. Blok će se srušiti ako na tom mjestu nema modula. Namjerno sam proširio mogućnosti ovog bloka kako bih u njega mogao smjestiti ne samo sliku zaglavlja, već i rotatore slika.

    < ?php if ($this - >countModules("header " ) ): ?>
    < div id= "header " >
    < jdoc:include type= "modules" name= "header " style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    U bloku “user3” definiramo poziciju “user3” za izlazne module.

    Blok će se srušiti ako na ovoj poziciji "user3" nema izlaza modula.

    < ?php if ($this - >countModules("user3" ) : ?>
    < div id= "user3" >
    < jdoc:include type= "modules" name= "user3" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Otvara se blok lijevog stupca koji će se srušiti ako nema modula na poziciji "lijevo".

    < ?php if ($this - >countModules("lijevo") ) : ?>
    < div id= "left" >
    < jdoc:include type= "modules" name= "left" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Otvara se najvažniji blok sadržaja koji može zauzimati 100% širine stranice, 80% i 60%, ovisno o broju uključenih stupaca.

    < div id= "content< ?php echo $contentwidth ; ?> " >

    Prikaz poruka u komponentama

    < jdoc:include type= "message" / >

    Sadržaj izlaznog sadržaja.

    < jdoc:include type= "component" style= "xhtml" / >
    < / div> < ! - - конец блока контента- - >

    Otvara se blok desnog stupca koji će se srušiti ako nema modula u "desnom" položaju.

    < ?php if ($this - >countModules("right" ) ) : ?>
    < div id= "rigth" >
    < jdoc:include type= "modules" name= "right" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Izlaz bloka "podnožje", dizajniran za prikaz modula "HTML koda" s informacijama o autorskim pravima. Ovdje također možete postaviti donji horizontalni izbornik ili modul za prezentaciju sadržaja. Blok će se sažeti ako je više od jednog modula prikazano u ovoj poziciji "podnožja".

    < ?php if ($this - >countModules("footer") ) : ?>
    < div id= "footer" >
    < jdoc:include type= "modules" name= "footer" style= "xhtml" / >
    < / div>
    < ?php endif ; ?>

    Blok stranice stranice "stranica", tijelo i sav kod su zatvoreni.

    < / div> < ! - - конец блока page- - >
    < / body> < ! - - конец блока body - - >
    < / html> < ! - - конец кода- - >

    Napravili smo kompletnu datoteku index.php. Sada znate koje se naredbe koriste i kojim redoslijedom se prikazuju blokovi predložaka.

    PAŽNJA: Kako bi se kod predloška mogao pročitati iz joomla admin panela, datoteku index.php potrebno je otvoriti u AkelPad editoru i spremiti u UTF-8 kodiranju, dok je poništen okvir BOM. Ako ste za rad s datotekom koristili program Macromedia Dreamweaver, tada trebate u gornjem izborniku odabrati “Edit”> “Page Properties” i odabrati kodiranje dokumenta Unicode (utf-8), te poništiti “enable Unicode signatures (BOM) )”. Ipak, toplo vam savjetujem da ne uređujete kod iz Joomla admin panela, ako nešto zabrljate - nema povratka, za razliku od programa Macromedia Dreamweaver, gdje uvijek možete poništiti napravljene promjene.

    Sam dizajn blokova bit će opisan u template.css. Ali mi ćemo konfigurirati stilske listove nakon instaliranja predloška na Joomla 3 (joomla 2.5), a za to moramo stvoriti



    reci prijateljima