Utilizarea Tehnici Web Crawling Pentru Depistarea Deficientelor de Securitatea ale Datelor Paginilor Webdoc
=== Utilizarea tehnici web crawling pentru depistarea deficientelor de securitatea ale datelor paginilor web ===
UNIVERSITATEA DIN ORADEA FACULTATEA DE INGINERIE ELECTRICĂ ȘI TEHNOLOGIA INFORMAȚIEI
PROGRAMUL DE STUDIU MANAGEMENT ÎN TEHNOLOGIA INFORMAȚIEI
FORMA DE ÎNVĂȚĂMÂNT ZI
Lucrare de Disertație
COORDONATOR/I ȘTIINȚIFIC/I
prof. dr. ing. Ștefan Vari Kakas
ABSOLVENT
Schmith Daniel
ORADEA
2016
UNIVERSITATEA DIN ORADEA FACULTATEA DE INGINERIE ELECTRICĂ ȘI TEHNOLOGIA INFORMAȚIEI
PROGRAMUL DE STUDIU MANAGEMENT ÎN TEHNOLOGIA INFORMAȚIEI
FORMA DE ÎNVĂȚĂMÂNT ZI
Utilizarea tehnici web crawling pentru depistarea deficiențelor de securitate ale datelor din paginilor web
COORDONATOR/I ȘTIINȚIFIC/I
prof. dr. ing. Ștefan Vari Kakas
ABSOLVENT
Schmith Daniel
ORADEA
2016
Cuprins
Cap I. Introducere. Specificarea temei 4
1.1 Motivația lucrării și prezentarea problemei 4
1.2 Tema propusă 4
Cap II. Fundamente teoretice 6
2.1 Considerării generale despre webcrawler-e 6
2.1.1 Googlebot 7
2.2 Analiza unei formular de căutare pentru a detecta punctele slabe privind securitatea 8
Cap III. Soluția propusă pentru rezolvarea problemei 10
3.1 Fundamente de bază 10
3.1.1 Content-Type 10
3.1.2 User agent 12
3.1.3 HTTP cookie 12
3.2 Prezentarea funcției file_get_contents și libraria cURL 13
3.2.1 Funcția file_get_contents. 13
3.2.2 Libraria cURL 14
3.2 Simularea unui formular de căutare 16
Cap IV. Implementarea aplicației de web crawler 19
4.1 Prezentarea generala a aplicației 19
4.2 Detalii de implementare si utilizare 19
Cap V. Concluzii 30
5.1 Realizarea obiectivului propus 30
5.2 Utilitatea aplicației în managementul paginilor web 32
Bibliografie 35
Cap I. Introducere. Specificarea temei
1.1 Motivația lucrării și prezentarea problemei
PHP este unul dintre cele mai importante limbaje de programare web, in principiu a fost folosit in codul HTML, dar de versiunea 4.3.0. se pot folosi si în mod CLI (“linie de comanda”) cea ce permite crearea aplicațiilor independente.
Datorită faptului că PHP este folosit pentru a crea pagini web si aplicații web, are multe funcționalitați cu ajutorul căreia putem să și simulam un browser: să facem cereri, să navigăm, să luam conținutul.
Cu ajutorul acestei limbaj de programare se poate construi un web crawler. O definiție generală, web crawlerul este un program care navigează in mod sistematic World Wide Web, în scopul de a crea un index de date. Exact aceasta face și crawlerul de la Google. Dar pe de o altă parte cu ajutorul unei crawler se pot copia datele de pe un website sau se pot testa funcționalitatea acestuia.
În ziua de azi în orice domeniu se poate folosi un web crawler. De la e-comerț, retail, cercetări de piața pănă la sport toate organizațiile realizează importanța datelor. Pe web totul e public dar totuși nimic, pentru că dacă avem ceva pe care este bazata firma noastră nu vrem să ajungă în mâinile concurenței, numai la utilizatori/clienți. Un exemplu ar fi, dacă avem o pagina web unde în fiecare zi publicăm câteva poze haioase cea ce aduce multe vizitatori, care petrec mult timp pe site, câștigam mulți bani. Dar dacă vine un web crawler și ia pozele publicate de pe site aceste vor fi găsite în dou sau mai multe locuri și așa pierdem vizitatori și bani.
De aceasta este important să nu fie așa de ușor crawlat un web site, google-ul oricum va crawla de ce are nevoie, dar dacă sunt prezente anumite buguri pe site aceasta niciodată nu va ajunge pe un nivel înalt.
În lucrarea acesta sunt prezentate câteva aspecte destul de simple dar foarte importante din punct de vedere a protejarea datelor publice pe web. Fiecare punct slab prezentat este real și există la multe firme online și sunt bazate pe datele lor publice.
1.2 Tema propusă
În acesta lucrarea mi-aș dori să prezint experiența mea cât de ușor e să facem un web crawler care simulează funcționalitătile unui formular, indiferent de limbajul de programare folosită pe pagina web, care interogează cu baze de date pentru a returna rezultate după o căutare. Vor fi prezentate anumite greșeli generale a programatoriilor pe care le-am observat la multe pagini web și cele care mi-au ușurat munca. Aceste greșeli pentru un utilizator simplu uneori nu sunt vizibile, dar cine se princepe un pic de exemplu la SQL sau înțelege cum funcționează un request poate să vadă aceste bug-uri și poate să beneficieze din ele.
Am ales această temă pentru că greșelile pe care am prezentat în lucrare am observat la multe pagini de web, la firme mai mici dar și la firme mai mari de exemplu la cataloage de firme (business directory), sau chiar și la siteuri cu avocații, doctorii, unde sunt și informații personale.
Întrebarea este că oare nu merită să fie tratate aceste greșeli sau din cauza lipsei de teste nici nu au fost găsite sau cineva a sărit peste ele pentru ca nu sunt importante?
În capitolele următoare va fi prezentat cum și cât de ușor e sa găsim aceste greșeli și să profităm de ele, folosind doar doua metode pentru a simula o căutare. Cu o funcție care există în PHP din start care este numită file_get_contents și cu o extensie a PHP-ului cURL (Client URL Library).
Cu aplicația implementată se pot testa anumite funcționalități a unei website, mai exact cererile făcute. Se pot testa dacă requestul comportă așa cum ne-am fi dorit, utilizând aplicația aceste bug-uri se pot găsi mai ușor și pot fi corectate înainte de a publica pagina.
În unele cazuri chiar nu merită testate și fixate anumite bug-uri, pentru că nu afectează pagina, dar de exemplu dacă pe pagină avem un request care face o cerere către o baza de date și în mod normal ar trebuie să ia primele zece rezultate. Dar din cauza lipsei de verificare a limitei acesta se poate modifica pe o mie, pe zece mii sau pe o suta de mii sau pe un numar și mai mare, acesta ar genera o cerere către baza de date care ar dura foarte mult, rezervând resursele și încetinând websiteul. Prezenta acestei bug este un punct slab al websiteului și cu un crawler se poate genera acest request repetitiv pana cedeaza siteul.
Având în vedere ca în lumea de azi depindem de internet, planificam excursii, plătim taxele, căutam loc de muncă, facem cumpărăturile și altele, este foarte important că un website să fie de încredere să funcționeze bine și să nu aibe puncte slabe sau foarte puține. Pentru asta testele înainte de publicarea siteului sunt foarte importante.
Ținând cont de aceste lucruri un tool bazat pe funcționalitățile unei web crawler ar fi util la testarea website-urilor și cu aceste teste pot preveni problemele posibile făcând website-ul să funcționeze mai bine și să fie mai rezistent.
Cap II. Fundamente teoretice
2.1 Considerării generale despre webcrawler-e
Când oameni vorbesc despre motoarele de căutare pe Internet, aceasta defapt înseamnă motoarele de căutare World Wide Web. Înainte de Web a devenit partea cea mai vizibilă a Internetului, au fost câteva motoarele de căutare pentru a ajuta oamenii să găsească informații pe net. Programe cu nume "gopher" și "Archie", au păstrat indexurile de fișiere stocate pe servere conectate la Internet, și a redus dramatic cantitatea de timp necesară pentru a găsi programe și documente. La sfârșitul anilor 1980, obținerea valoare serioase de pe Internet a însemnat să ști cum să folosească Gopher, Archie, Veronica și restul. Astăzi, majoritatea utilizatorilor de Internet limitează căutările pe Web.
Înainte de un motor de căutare vă poate spune în cazul în care un fișier sau un document există, acesta trebuie să fie găsită. Pentru a găsi informații despre sutele de milioane de pagini web care există, un motor de căutare folosește roboți, software speciale, numiți păianjeni (spider) , pentru a construi liste de cuvinte gasite pe site-uri Web. Atunci când un web-spider își construiește listele sale, procesul este numit web crawling. Pentru a construi și menține o listă utilă de cuvinte, păianjeni a unei motor de căutare trebuie să se uite la o mulțime de pagini.
Punctele de pornire uzuale sunt liste de servere folosite intens si foarte populare pagini. Păianjenul va începe cu un site popular, indexează cuvintele de pe paginile sale și vizitează fiecare link găsit în cadrul site-ului. În acest fel, sistemul spidering începe rapid să se deplaseze.
Google a început ca un motor de căutare academică. În lucrare care descrie modul în care a fost construit sistemul, Sergey Brin si Lawrence Page dă un exemplu de cât de repede păianjeni lor pot lucra. Ei au construit sistemul lor inițial de a folosi mai multe spidere, de obicei trei la un moment dat. Fiecare păianjen ar putea pastra aproximativ 300 de conexiuni la pagini web deschise la un moment dat. La performanțele sale de vârf, folosind patru păianjeni, sistemul lor ar putea accesa cu crawlere peste 100 de pagini pe secundă, generând aproximativ 600 kilobytes de date în fiecare secunda.
„Rulează rapid” a însemnat construirea unui sistem pentru a alimenta informațiile necesare pentru păianjeni. Sistemul Google a avut un server dedicat pentru a oferi URL-uri pentru păianjeni. Mai degrabă în funcție de un furnizor de servicii de Internet pentru serverul de nume de domeniu (DNS), care se traduce numele serverului într-o adresă, Google a avut propriul său DNS, pentru a se menține întârzieri la un nivel minim.
Atunci când păianjenul de la Google se uita la o pagina HTML, acesta verifică două lucruri , cuvintele din pagină și unde au fost găsite cuvintele. Cuvintele care apar în titlu, subtitrări, meta tag-uri și alte poziții de importanță relativă s-au remarcat o atenție specială în timpul unei căutări ulterioare. Google păianjen a fost construit pentru a indexa fiecare cuvânt semnificativ pe o pagină, lăsând articolele "a", "an" și "the" . Alte păianjeni au abordări diferite. Aceste abordări diferite, de obicei înseamnă încercarea de a face păianjenul sa funcționează mai rapid, permite utilizatorilor să caute mai eficient și mai rapid. De exemplu, unii păianjeni va ține evidența cuvintele din titlu, sub-titluri și link-uri, împreună cu cele 100 de cuvinte folosite cel mai frecvent pe pagină și fiecare cuvânt din primele 20 de rânduri de text. Lycos folosească această abordare pentru a spidering Web
([1]) .
Alte sisteme, cum ar fi AltaVista, merg în cealaltă direcție, indexând fiecare cuvânt pe o pagină, inclusiv "a", "an" , "the" și alte cuvinte "nesemnificative".
2.1.1 Googlebot
Cel mai cunoscut crawler se numește "Googlebot", care descoperă pagini web disponibile publicului. Crawlerele accesează paginile web și link-uri de pe aceste pagini. Ei merg de la link la link pentru a aduce date de pe aceste pagini web înapoi la serverele Google. Procesul de accesare cu crawlere începe cu o listă de adrese web din accesarea cu crawlere din trecut și sitemaps oferite de proprietării site-ului. Așa cum crawler vizitează aceste site-uri web, caută linkuri pentru alte pagini pentru a vizita. Software-ul acordă o atenție deosebită la site-uri noi, modificări la site-urile existente și link-uri moarte. Sunt programe care determină care site trebuie crawlat, cât de des și câte linkuri de pe siteul respectiv. Google nu acceptă bani pentru a accesa cu crawlere un site mai frecvent. Pentru ei contează de a avea cele mai bune rezultate posibile, deoarece pe termen lung, asta e ceea ce este cel mai bun pentru utilizatori.
Web-ul este ca o bibliotecă și crește în continuu, cu miliarde de cărți. Google adună, în esență, paginile în timpul procesului de accesare cu crawlere și apoi creează un index, deci știe exact cum să se uite la lucrurile. La fel ca indicele în partea din spate a unei cărți, indexul Google include informații despre cuvintele și locațiile lor. Atunci când căutăm, la nivelul cel mai de bază, algoritmii de la google caută termenii de căutare în indexul pentru a găsi paginile corespunzătoare.
Procesul de căutare devine mult mai complex, atunci când căutam "pisici" nu dorim o pagină cu cuvântul "pisici" pe sute de ori. Probabil vrem poze, clipuri video sau o listă de rase. Sistemele de indexare Google observă multe aspecte diferite ale paginilor, cum ar fi când au fost publicate, indiferent dacă acestea conțin imagini și clipuri video și multe altele. Cu Knowledge Graph, va continua să meargă dincolo de potrivire a cuvintelor cheie pentru a înțelege mai bine oamenii, locurile și lucrurile care ne interesează.
Cele mai multe site-uri nu au nevoie să instituie restricții pentru accesarea cu crawlere, indexare sau de servire, astfel încât paginile lor sunt eligibile să apară în rezultatele căutării, fără a fi nevoie să facă orice muncă suplimentară. Proprietării de site-uri au mai multe opțiuni cu privire la modul în care google accesează și indexează site-urile lor prin intermediul Webmaster Tools și un fișier numit "robots.txt". Cu fișierul robots.txt, proprietării de site-ul pot alege să nu fie accesate cu crawlere de Googlebot, sau pot oferi mai multe instrucțiuni specifice cu privire la modul de procesare a paginilor de pe site-urile lor.
Proprietării site-ului pot alege modul în care conținutul este indexat. De exemplu, ei pot alege ca paginile lor să apară fără un fragment (rezumatul paginii afișat sub titlul în rezultatele de căutare) sau o versiune salvată în memoria cache (o versiune alternativă stocată pe serverele Google în cazul în care pagina live nu este disponibilă). Webmasteri pot alege să se integreze în căutare de jos propriile pagini cu Custom Search.
Googlebot este cel mai popular web crawler și una dintre cele mai importante web crawlere. Datorită lui cautariile noastre nu durează numai câteva milisecunde (Alte informații interesante se pot găsi la bibliografia [2]).
2.2 Analiza unei formular de căutare pentru a detecta punctele slabe privind securitatea
Primul pas e să vedem ce methodă folosește formularul html, aceasta putem să găsim usor, în pagina sursă, sau facând inspect element pe pagină, methoda folosită pentru a transmite datele putem să găsim la atributul method (ex : <form action="action_page.php" method="get">). La atributul action, trebuie să apară un link, unde să faca requestul form-ul respectiv, în exemplul de mai sus aceasta este action_page.php . Aici nu neapărat trebuie să fie un link care pointează altundeva, poate sa fie aici un link câtre pagină curentă.
Pănă acum știm methoda si link-ul, deci cum și unde face request pagina respectivă. Urmatorul pas este să identificăm parametri, cele care sunt trimise câtre server. Unele parametri afectează mai mult cererea, unele nu sunt obligatorii, unele sunt numai informative dar trebuie să apare. De exemplu pentru limitarea rezultatelor parametrul trimis este des numit: limit,result, rows sau top. Modificând acest parametru putem să obținem un alt numar de rezultate trimise de catre server.
Următorul pas este să facem o căutare in formularul respectiv și să verificăm cum comportă, de exemplu se poate că va face un request la “X” și la succes “X” redirecționează catre “Y” cu rezultatele. Sau se poate că primește rezultatele de la un webservice in format json sau xml. Căutarea facută pe pagină trebuie simulat si in PHP, folosind același parametri de căutare pentru a verifica dacă nu există nici o diferență între rezultate primite. În unele cazuri când facem un request folosind PHP trebuie setat manual header-ul, cea ce in browser este setat automat. Pănă nu primim rezultatul ce am primit și in browser, înseamnă că mai trebuie să adaugăm cevam sau să scoatem ceva din header. Dar se poate că nu folosim url-ul pe care trebuie, in cazul aceasta trebuie să analizăm din nou formularul. Folosind Firebug in browser-ul Mozilla putem să urmărim ce request-uri sunt trimise câtre server si ce primim ca și răspuns.
După ce am reușit cu succes să facem un request in PHP , folosind file_get_contents() sau cURL, putem să încercăm să modificăm anumite parametri care fac parte in căutare si să urmărim ce raspuns primim de la server. Aici putem să incercăm să scoatem anumite parametri sau chiar să adaugăm cea ce este mai greu că trebuie sa ghicim numele parametrului, dar nu are rost să pierdem timpul cu aceasta. Când avem de exemplu un parametru numit limit sau rows, putem să încercăm să dăm un numar mai mare pentru a primi mai multe rezultate. De exemplu pe site afișează maxim zece rezultate dupa o cautare , modificând parametrul adecvat putem să forțăm serverul să dea mai multe rezultate. Methoda aceasta pentru a trece o limitare de rezultate funcționează in foarte multe cazuri, cea ce se poate considera un bug pe site-ul respectiv.
O altă methodă pentru a primi cât mai multe rezultate este că dacă pe site este un input unde trebuie să punem un cuvânt cheie dupa care să facă căutarea, putem să încercăm să punem anumite caractere cele care baza lor de date va înțelege ca și “orice”, de exemplu in SQL “_” (sublinia). Uneori siteul verifică lungimea șirului de caractere și nu ne lasa să facem requestul pănă nu dăm un text cu lungimea cerută, în cazul aceasta putem să încercăm să dăm același caracter mai multe ori, de exemplu textul introdus trebuie să aibă minimum trei caractere, putem să încercăm sa punem trei caractere de sublinie (“_”). Uneori dacă punem spatiu, plus (“+”) sau un punct primim la fel toate rezultatele. Există anumite cazuri când este de ajuns să trimitem numai un singur caracter și primim tot ce începe cu caracterul dat sau tot ce conține caracterul.
Totate metodele prezentate se pot considera bug-rui pe site-ul respectiv, lipsa lor de verificare pot însemnă că ori nu au fost testate ori nu merită corectate. Un utilizator poate că nici nu observă aceste bug-uri, dar pentru un crawler sunt foarte importante. Dacă nu ar exista atunci timpul de rulare a crawlerului pentru a lua datele ar fi mai mare.
Cap III. Soluția propusă pentru rezolvarea problemei
3.1 Fundamente de bază
În browserul de Firefox există un addon numit „Firebug”, cu ajutorul căreia putem să vedem ce request face pagina către server și ce primește ca și răspuns. Tot cu aceasta putem să vedem cum este setat response header-ul și request header-ul și de aici putem să știim ce trebuie să setăm și noi, dacă vrem să simulam un request. În capitolul aceasta am prezentat câteva elemente din http header, cele care afectează cel mai mult pagina. Fiecare element are rolul lui în http header și lipsa sau definirea incorectă poate să afecteze conținutul pagini sau poate că nici nu primim răspuns de la server din cauza lor.
3.1.1 Content-Type
Scopul câmpului Content-Type este de a descrie datele conținute în body suficient pe deplin că user-agent-ul sa poată să aleagă un agent sau un mecanism adecvat să prezinte datele utilizatorului în mod adecvat. ([4] – primul paragraf).
Content-Type-ul este utilizat pentru a specifica natura datelor în corpul unei entități, prin acordarea unui identificatori de tip (type) si subtip (subtype) , precum și prin furnizarea de informații auxiliare care pot fi necesare pentru anumite tipuri. După type și subtype urmează un set de parametri, specificat într-o notație atribut / valoare, setul de parametri diferă semnificativ pentru diferite tipuri. Ordinea parametrilor nu este semnificativă. Printre parametrii definiți este un parametru "charset", prin care poate fi declarat setul de caractere, de exemplu UTF-8.
În general, top-level Content-Type este utilizat pentru a declara tipul general de date, în timp ce subtipul (subtype) specifică un format pentru acel tip de date. De exemplu "image / xyz" este suficient pentru un user-agent, să știe că este o imagine, chiar și în cazul în care agentul nu are cunoștință despre formatului de imaginei "xyz".
Parametrii sunt modificatori ai conținutului subtype, și nu afectează în mod fundamental cerințele. Deși cei mai mulți parametri nu au sens decât cu un anumit content-type , altele sunt "la nivel global", în sensul că s-ar putea aplica oricărui subtip. De exemplu, parametrul "boundary" are sens doar pentru "multipart", dar parametrul "charset" s-ar putea face sens cu mai multe tipuri de content-type.
Definiție generală a content-type-ului este „Content-Type := type "/" subtype *[";" parameter]” . Tipul, subtip și numele parametrilor sunt case insensitive. De exemplu TEXT, Text, si TeXt sunt equivalente. Valorile parametrilor sunt case sensitive, dar sunt anumite cazuri când nu, de exemplu pentru content-type-ul multipart sunt câteva parametru care sunt case insesnsitive.
Dincolo de această sintaxă, singura constrângere asupra definiției subtipului este dorința ca și utilizările lor nu trebuie să intre în conflict.
Cele șapte inițiale predefinite pentru Content-Type sunt urmatoarele ([4]) :
Text – informații de tip text. Subtipul „plain” indică faptul că textul nu este formatat. Nu necesită nici un software special pentru a obtine textul, totuși e bine să știm setul de caractere folosite, pentru afisare.
Multipart – datele constau din mai multe părți ale tipurilor de date independente. Patru subtipuri inițiale sunt definite : mixed , alternative, parallel și digest.
Message – un mesaj încapsulat. Este însăși un RFC 822 mesaj complet formatat care poate conține propriul său Content-Type. Subtipul principal este "RFC822" . Subtipul "partial" este definit pentru mesaje parțiale, pentru a permite transmiterea fragmentată a datelor care sunt considerate prea mari pentru a fi trecut prin transport e-mail. Un alt subtip, "External-body", este definit pentru specificarea datelor mari prin referire la o sursă externă de date.
Image – datele de imagine. Imaginile necesită un dispozitiv de afișare (cum ar fi un display grafic, o imprimantă sau un aparat de fax) pentru a vizualiza informațiile. Subtipuri inițiale sunt definite pentru două formate de imagine utilizate pe scara largă, jpeg si gif.
Audio – date audio, cu subtip inițial "basic". Audio necesită un dispozitiv de ieșire audio (cum ar fi un difuzor sau un telefon) pentru a "a afișa" conținutului.
Video – date video. Video necesită capacitatea de a afișa imagini în mișcare. Subtipul inițial este "mpeg".
Application – un alt tip de date, de regulă fie date binare sau informații care urmează să fie prelucrate de către o aplicație. Subtipul principal, "octet-stream", este utilizat în cazul datelor binare, cazul în care cea mai simplă acțiune recomandată este scrierea informațiilor într-un fișier. Două subtipuri suplimentare, "ODA" și "PostScript", sunt definite pentru transportul documentelor ODA și PostScript.
Content-Type poate să apară și în response header și în request header, primul indică cum trebuie interpretat răspunsul, al doilea cum trebuie tratat requestul către server. Dacă nu sunt setate bine aceste valori, ori la request ori la response putem sa primim errori fatale sau date greșite.
3.1.2 User agent
User-agent este un string care identifică browser-ul și oferă anumite detalii de sistem la serverele care găzduiesc site-urile pe care le vizităm. Aceasta apare în request header nu în response header. În general, request-ul este trimisă din browser-ul către aplicația web. Astfel încât variabila user-agent este completat de către browser. Browsere diferite vor umple acest domeniu, cu valori diferite. Un exemplu de user-agent: „Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0”.
3.1.3 HTTP cookie
Http cookie ( web cookie, Internet cookie, browser cookie sau cookie) este o bucată mică de date trimise de la un site web și stocate în browser-ul web al utilizatorului, în timp ce utilizatorul navighează.
Cookie-urile au fost concepute pentru a fi un mecanism de încredere pentru site-urile să-și amintească informații , cum ar fi elementele adăugate în coșul de cumpărături într-un magazin online, sau pentru a înregistra activitatea de navigare a utilizatorului ,inclusiv făcând click pe butoanele speciale, logare sau de înregistrare care au fost vizitate pagini în trecut. Ele pot fi de asemenea folosite pentru a aminti bucăți arbitrare de informații pe care utilizatorul introduse anterior în câmpurile de formular, cum ar fi nume, adrese, parole sau numere de cărți de credit.
Cookie-ul de autentificare este cea mai des întâlnită metodă utilizată de serverele web pentru a verifica dacă utilizatorul este conectat sau nu. Fără un astfel de mecanism, site-ul nu ar știa dacă să trimită o pagină care conține informații sensibile, sau cere utilizatorului să se autentifice prin logare. Securitatea unui cookie de autentificare depinde in general de securitatea site-ului emitent și browser-ul web al utilizatorului plus și dacă datele din cookie sunt criptate. Vulnerabilități de securitate pot permite datelor din cookie de a fi citit de către un hacker, folosit pentru a obține acces la datele unui utilizator.
Tracking cookies și în special third-party tracking cookies, sunt de obicei folosite ca metode de a compila înregistrările pe termen lung ale indivizilor, istoricul de navigare. O preocupare potențială de confidențialitate, care a determinat Europa și SUA factorii de decizie de drept să ia măsuri de la 2011. Conform legislației europene, toate site-urile care sunt vizitate din statele membre ale Uniunii Europene, trebuie să obține aprobare din partea utilizatorilor înainte de a depozita cookie-uri non-esențiale pe dispozitivul lor.
3.2 Prezentarea funcției file_get_contents și libraria cURL
3.2.1 Funcția file_get_contents.
Această funcție este similară cu file(), cu excepția faptului că file_get_contents () returnează fișierul într-un șir de caractere . În caz de eșec, file_get_contents () se va returna FALSE ([11], primul paragraf).
În principu funcția este folosită pentru a lua conținutul unui fișier, de exemplu un fișier json, xml sau html. Dar funcția aceasta putem să folosim ca să luăm pagina sursă, a unei website.
Are cinci parametri, dintre cele care numai primul parametru este obligatoriu. Parametri sunt urmatoarele :
filename – string ,numelei fișierului
use_include_path – boolean, care indică dacă folosim o listă de directoare unde am specificat unde să caută după fișierul dorit. În mod implicit este false.
Context – este o resursă creată cu funcția stream_context_create(), va fi prezentat mai detaliat în capitolul aceasta.
Offset – integer, de unde sa înceapă citirea.
Maxlen – integer , lungimea maximă a datelor citite. Valoarea implicită este de a citi până când ajunge la sfârșitul fișierului. Parametrul se aplică doar la fluxul prelucrat de filtre.
O eroare de nivel E_WARNING este generat în cazul în care numele fișierului nu poate fi găsit, lungimea maximă este mai mică decât zero, sau contextul nu a fost setat bine.
Un exemplu simplu când putem să folosim funcția de file_get_contents() cu doar primul parametru, care este obligatoriu: file_get_contents('localhost/index.php').
Cu sintaxa de mai sus putem să luăm conținutul fișierului index.php de pe localhost-ul nostru, nu trebuie să trimitem nici un alt parametru în afară de filename.
Sunt multe cazuri când serverul de pe care vrem să cerem anumite resurse, are nevoie de un header, după care să știe , cum să transmite datele, sau prin header verifică dacă putem să accesăm resursa. Sunt multe cazuri diferite, de ce necesită setarea unui context pentru funcția aceasta.
Dacă avem nevoie de un context pentru a face un request, trebuie sa folosim funcția „stream_context_create()” din PHP. Primește doi parametri, fiecare câte un array, doar primul este obligatoriu. Primul parametru trebuie să fie un array associativ , key => value , sub forma $arr['wrapper']['option'] = $value.
În exemplul de mai jos este prezentat cum trebuie creat un context și folosit impreună cu functia file_get_cotents().
$context = stream_context_create(array(
'http'=>array(
‘method'=>"GET",
'header' => implode("\r\n", array(
'Content-Type: application/x-www-form-urlencoded',
'Cookie: JSESSIONID=4D07E6BEE3711E1DFAB10CCFF9F37E1F' ,
'Host: localhost',
'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:22.0) Gecko/20100101 Firefox/22.0',
)) . "\r\n",
),
));
$page = file_get_contents(‘http://localhost/index.php’,false, $context);
Pentru a face un http request folosind un context, creat cu stream_context_create() , trebuie să trimitem ca și parametru un array, care conține cheia „http”. Pe aceasta cheie iarăși trebuie să avem un alt array associativ , key => value , specificând metoda prin care vrem să facem request-ul, în exemplul de mai sus aceasta este „GET”. Pentru a folosi un header personalizat trebuie declarat cheia „header”, care trebuie să fie un string și fiecare componentă din header trebuie despărțit cu linie nouă („\r\n”). În exemplul de mai sus am folosit un array pentru componentele din header, și cu funcția implode sunt transformate într-un singur string. După ce am creat context-ul, funcția file_get_contents() poate să primească ca și al treilea parametru, al doilea este „use_include_path” cea ce punem pe false. Apelând funcția așa când face request-ul cantre server va seta metoda prin care face cererea și request header-ul.
3.2.2 Libraria cURL
PHP suportă libcurl, o bibliotecă creată de Daniel Stenberg, care vă permite să se conecteze și să comunice la mai multe tipuri diferite de servere cu multe tipuri diferite de protocoale. libcurl acceptă în prezent protocoalele HTTP, HTTPS, ftp, gopher, telnet, dict, fișiere și LDAP. libcurl sprijină, de asemenea, certificate de HTTPS, HTTP POST, HTTP PUT, FTP (încărcarea acestei lucru se poate face cu extensia ftp PHP), forma HTTP pe bază de încărcare, proxy-uri, cookie-uri și autentificări. Aceste funcții au fost adăugate în PHP 4.0.2.
După ce am compilat PHP cu suport curl, putem începe să utilizăm funcțiile cURL. Ideea de bază din spatele funcțiile cURL este că trebuie inițializată o sesiune cURL folosind curl_init (), după care setat toate opțiunile pentru transferul prin intermediul curl_setopt (), atunci se poate executa sesiunea cu curl_exec () și apoi trebuie închis sesiunea folosind curl_close () ([5]) .
Pentru funcția curl_init() se poate transmis un parametru, care este un string de url, dar aceasta este optional. De obicei nu este folosit așa, depinde ce vrem să facem.
Probabil cel mai important funcție al cURL-ului este „curl_setopt()”, are trei parametri, fiecare obligatoriu. Primul parametru este resursa cURL pe care am primit după inițializare folosind curl_init(). Al doilea parametru este un int, o constanta, începe cu „CURLOPT_” și reprezintă opținuea pe care vrem să setăm pentru sesiunea curenta. Ultimul parametru este valoarea pe care vrem sa setam pentru al doilea parametru. În bibliografia șase este o tablea cu fiecare opțiune pe care se pot modifica și pe ce valoare. Unele sunt mai importante și trebuie setate mereu. De exemplu dacă vrem să facem un HTTP POST trebuie să setăm optiunea „CURLOPT_POST” pe true.
În exemplul de mai jos am folosit curl_init(), pentru a crea o noua sesiune de cURL. Cu ajutorul funcției de curl_setopt() am setat câteva parametri, de exemplu CURLOPT_URL , care acceptă un URL. Acest lucru poate fi de asemenea setat la inițializarea sesiunei de cURL. Am mai setat și CURLOPT_USERAGENT, un user-agent pe care va folosi în sesiunea aceasta.
$ch = curl_init();
curl_setopt($ch , CURLOPT_URL, „http://localhost/index.php”);
curl_setopt($ch , CURLOPT_USERAGENT, „Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0”);
$response = curl_exec($ch);
curl_close ($ch);
Dacă avem nevoie de informații legat de requestul făcut cu cURL, putem să folosim funcția „curl_getinfo()”, care primește doi parametri. Primul este resursa de cURL iar al doilea este o constantă, care reprezintă ce vrem sa aflăm despre sesiunea respectivă. De exemplu putem să cerem status codul cu constanta „CURLINFO_HTTP_CODE” (ex. : curl_getinfo($ch, CURLINFO_HTTP_CODE); unde „$ch” este o resursa creata cu curl_init()).
Cu funcțiile curl_error() și curl_errno() putem să verificăm errorile primite în sesiunea dată. La apelarea funcțiilor trebuie să trimitem resursă de cURL. Diferența intre ele este că primul returnează un șir ce conține ultima eroare pentru sesiunea curentă iar al doilea numărul acestei eroare.
3.2 Simularea unui formular de căutare
Cele mai cunoscute metode http sunt GET si POST, diferența între cele două la prima vedere este că prin metoda GET parametri sunt vizibile in url iar prin POST nu sunt vizibile. Dar totuși sunt si alte diferente de exemplu GET transimte parametri mai repede și de obicei metoda aceasta este folosită când facem un simplu request către server, de exemplu facem un request după un ID, aceasta poate să fie vizibila. Dar când vrem sa trimitem date câtre o anumita resursa folosim POST, în unele cazuri primim alte date ca și răspuns în unele cazuri doar o confirmare. Un exemplu simplu când POST-ul este folosit este la formulare de inregistrări pentru utilizatori.
Folosind cURL sau file_get_contents() se pot simula aceste requesturi. La un formular de căutare este folosit ori POST ori GET. În exemplele următoare vor fi prezentate cum se pot simula aceste requesturi folosind cURL și file_get_contents().
Exemplul următor va fi un request care folosește POST ca să trimite datele către un webservice. Aceasta va returna un json ca și răspuns. Aceasta e un caz simplu, nu trebuie sa setăm nimic special ca să funcționează, doar CURLOPT_RETURNTRANSFER ca să stocăm raspunsul într-o variabilă.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.mcdonalds.de/search');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'longitude=13.404953999999975&latitude=52.52000659999999&searchfield=Berlin&radius=10000');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
Acest request folosind functia file_get_contents() impreună cu un context este și mai simplu. Trebuie creat un context nou cu funcția stream_context_create() si pasând ca și al treilea parametru la file_get_contents va returna ce returnează și bucată de cod mai sus.
$context = array(
'http'=>array(
'method'=>"POST",
'content' => 'longitude=13.404953999999975&latitude=52.52000659999999&searchfield=Berlin&radius=10000' )
);
$context = stream_context_create($context);
$a = file_get_contents('http://www.mcdonalds.de/search', false, $context);
După cum se vede aici contextul din start este un array, unde am specificat metoda prin care să facă request-ul. În cazul metodei de GET cheia content nu trebuie setată. Dacă este nevoie se poate seta un header.
Uneori trebuie se setăm un header care este trimis către server ca să ajută să înțeleagă mai bine cererea, de exemplu să știe cum trebuie parsat parametri din stringul pe care a primit. În unele cazuri trebuie setat User-Agent-ul că răspunsul poate să depindă de aceasta și dacă nu este setata nu vom primim răspuns de la server sau nu așa cum am așteptat. În exemplul de mai jos am creat un context nou, pe care pot să folosesc împreuna cu file_get_contents().
$context = array(
'http'=>array(
'method'=>"POST",
'content' => 'longitude=13.404953999999975&latitude=52.52000659999999&searchfield=Berlin&radius=10000',
'header' => implode('\r\n', array(
'Content-Type : application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0'
))
));
Pe cheia header trebuie setat un string separat cu \r\n care va reprezenta request header-ul. Aceasta va fi trimis către server care va decide cum să răspundă în funcția de setări primite, uneori aceste setări sunt obligatorii și dacă nu sunt prezente atunci va da erroare de exemplu un http status code 400, care înseamnă ca nu merge requestul.
Acest request folosind cURL are nevoie de extindere fața de primul exemplu. Trebuie setat două opțiuni primul este CURLOPT_USERAGENT care reprezintă User-Agent-ul pe care va folosi. Al doilea este CURLOPT_HTTPHEADER care accepta un array cu string-uri care reprezintă părțile al unei request header și fiecare trebuie să fie un nou element în array.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.mcdonalds.de/search');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'longitude=13.404953999999975&latitude=52.52000659999999&searchfield=Berlin&radius=10000');
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded; charset=UTF-8'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
Cu aceste exemple am prezentat niște chesti de baza despre file_get_contents și cURL, dar si requesturile care par mai complexe se pot simula cu aceste metode. Uneori trebuie setat mai multe componente al request header-ului de exemplu un cookie sau accept-encoding-ul. Sau dacă nu funcționează ceva pur și simplu ceva este greșit ori în url ori în parametri pe care vrem să trimitem.
Fiecare data când vrem sa simulăm un request trebuie să analizăm atent pe aceasta și să folosim un tool care afișează informații mai detaliate, de exemplu cum e și FireBug in Firefox.
Cap IV. Implementarea aplicației de web crawler
4.1 Prezentarea generala a aplicației
Aplicația a fost facută in PHP 7 cu extensia librariei CURL. Scopul aplicației este să găsească bug-urile funcționalițatile greșite și testarea paginilor web. Cu un singur fișier de configurări putem să testăm anumite lucruri si să primim informații despre requesturi, de exemplu cât timp a durat cererea, sau să extragem anumite date de pe pagina web. Se pot folosi si pentru a testa dacă anumite componente au fost incărcate in pagina.
Aproximativ fiecare website are un formular pentru căutari și foarte multe dintre ele au anumite bug-uri, cele care ori nu sunt testate ori nu au meritat să fie testate. Sunt anumite cazuri când nu merită sa fie tratate aceste bug-uri, de exemplu la firme mai mici unde datele sunt publice. Dar sunt anumite firme care au datele pe web, sunt vizibile pentru utilizatori, aduc bani pentru firma si nu se vrea că datele lor sa fie luate cu un web crawler. În aceste cazuri un tool de genul va usura testarea siteului.
Se poate seta input-ul de unde ia cu ce să facă request, aici putem să setăm de exemplu dacă vrem sa facem teste automatizate cu urmatoarele caractere : A, B, C , etc. Aplicația va genera un request cu fiecare caracter si dacă am setat mod debug pentru program, va afișa header-ul si durata requestului. Aceasta ne ajută sa vedem cum raspunde serverul si in cât timp, pentru fiecare cerere.
Putem să setam xpath-uri pentru a verifica daca anumite componente au fost incărcate dupa request.
Aplicația este doar de bază, pentru a deveni un tool de testing trebuie dezvoltat in continu și cercetat ce nevoi sunt in domeniul acesta.
4.2 Detalii de implementare si utilizare
Aplicația are doua parți mai mari, primul este responsabil pentru a construi un tree de execuție, al doilea pentru executarea pașilor. Are nevoie de un fișier cu configurări, care reprezintă ce anume vrem să facem. Sunt niște cuvinte cheie predefinite la care trebuie să dăm valori. Fișierul cu configurări trebuie sa fie in format json. Am ales tipul json pentru că e usor de citit, modificat. În exemplul de mai jos este prezentat un exemplu de json, în care s-au declarat două variabile și în urma execuției va afișa una dintre ele.
{ "program":"Test",
"vars" : {"a" : 2 , "b": 3},
"run" : [
{"step" : "echo",
"settings" :{ "text" : "$a" }
}
]}
Componentele „program” , „vars” și „run” sunt obligatorii, fără aceste valori nu va funcționa programul. „Program” reprezintă un nume, pe care va afisa când începe să execute tree-ul generat din json. Aceasta este inportat când avem mai multe și vrem executăm mai multe pașuri, astfel putem să vedem mereu care set de configurari este în timp de execuție sau care a fost executat.
Al doilea parametru din exemplul de mai sus este „vars”, care este un array de tip cheia și valoare, cu variabilele pe care putem să folosim oriunde mai jos, de exemplu la afișare. Putem să declarăm cate variabile dorim. Variabila poate să fie de mai multe tip-uri: string , int, float, array, etc.
Al treilea cuvânt de cheie este, „run”, din valorile lui este construit un tree de execuție. Acesta este un array cu pașii pe care vrem sa-i executam. Fiecare pas are doua elemente, ambele sunt obligatorii, „step” și „settings”. Primul conține un cuvânt de cheie, prin care aplicația va ști ce trebuie să execute, dacă nu este valid valoarea va da eroare. Al doilea primește un set de setari prin care specificăm cum să fie executat.
Fiecare „step” este o clasă unde este specificat ce setări putem să trimitem și cum trebuie executat. Când initializam aplicația primul pas este transformarea configurației primită într-un tree. Are un punct de pornire, un root , de unde începe executarea pas cu pas. Punctul de pornire mereu e afisrea textului din „program”, după care se execută tot ce a fost declarat în „run”. În timpul când transforma json-ul într-un tree încarca toate variabilele intro clasa de unde putem să cerem valorile sau să modificam pe ele.
După ce a fost construit tree-ul, avem un model de unde programul va știe ce trebuie să execute, ce a fost executat, și ce urmează să fie executat. Acesta este similar cu DOM. Fiecare nod are un parinte, un nod care urmează după el, care este înaintea lui și fiecare nod poate sa aibă copii lui, sau unde nu este setat careva dintre ele valoarea lui este null. La fel este construit și în aplicație, unde pentru fiecare „step”, reprezentat printe-o clasă, este definită un părinte, care urmează după el, și copii dacă e cazul.
Un alt punct important când pregătim aplicația pentru execuție este să avem definit un fel de harta pentru „step” -urile. Aceasta insemnă că trebuie să definim un fișier de json unde vom ține cu cheia și valoare, fiecare „step” prin care clasă să fie reprezentată. În exempul de mai jos putem să vedem că pentru taskul de „echo” va fi initializat clasa de EchoTask.
[
'echo' => EchoTask::class,
]
Din cauza că putem să definim acest lucru, aplicația poate să fie extinsa ușor, și dacă pentru un task definim două clase diferite, putem să decidem pe care vrem să folosim.
Clasa de bază a aplicației este clasa de Program, această clasă construiește tree-ul, variabilele și executa pași definiți. Constructorul clasei primește doi parametri și arată în felul următor : „__construct(array $registry, array $config)”. După cum se vede primul parametru este un array care reprezintă harta cu „step” și clasa asociată, așa cum a fost prezentat mai sus. Al doilea parametru, tot un array, este conținutul fișierului cu configurări transformat într-un array. După ce avem acest doi parametri totul este pregătit pentru a inițializa aplicația, programul va ști de unde poate să inițializeze clasele folosite și ce trebuie să execute.
Tot clasa aceasta va ocupa și de definirea variabilelor setate în json sub cheia de „vars”. Pentru aceasta este folosită o clasă ajutătoare Variables, unde stocăm variabilele într-un array, și care ne asigură că folosim același variabile. Clasa aceasta este importantă pentru că astfel nu trebuie să pasăm pentru fiecare etapă valorile variabilei pe care vrem să le folosească, trebuie sa trimitem doar numele lui și va scoate din clasa Variables. Pe de o altă parte dacă modificăm o valoare în careva etapă va fi vizibil și în etapele următoare. Urmatoarele metode sunt definite în clasa aceasta get(), set(), del() și has(). Metoda „set” primește numele variabilei pe care vrem să setăm și valoarea pe care vrem. Pentru metoda aceasta putem să trimitem ca și primul parametru un string de genul: „a.b.c”, astfel pe valoarea „a” va declara un array de „b” pe aceasta valoare încă un array de „c”. Deci putem să dăm o cale cu ajutorul căreia putem să grupăm anumite valori, dacă vrem. Metoda „get” din clasa Variables funcționează la fel, numai că aceasta returnează valorea dacă există variabila cerută, în cazul în care nu există va returna null.
Următorul aspect al clasei de Program este metoda „parse()”. Această metoda construiește tree-ul de execuție parsând din configurație partea de „run”. Pentru fiecare „step” va inițializa o clasă cu setările date și în plus seteaza un „step” de părinte și un așa numit frate următor și frate precedent, în cazul în care nu exista careva va fi setat pe null. În timpul parsări este verificat dacă etapa curentă este validă, aceasta înseamnă că dacă clasa asociată există și este accesibilă. Corpul metodei de parse arata în felul următor:
protected function parse(array $nodes, Task $parent) {
foreach($nodes as $node) {
$task = $this->factory($node['step'], $node['settings']);
if (!empty($node['children'])) {
$this->parse($node['children'], $task);
}
$parent->appendChild($task);
}
return $parent;
}
Aceasta este o metodă recursivă, primul parametru numit „nodes” sunt pași pe care dorim să-i executăm, în configurația declarată se pot găsi la „run”. Al doilea parametru este părintele care este o instanța a clasei de „Start”, de aici va începe executarea. Clasa aceasata nu face multe, numai afișeaza textul din array-ul cu configurări având cheia „program”. Este doar un punct de pornire în logica aplicației. În corpul metodei de mai sus se afla un foreach, parcurgand valorile din $nodes, cu ajutorul metodei „factory”, care returnează o instanță din clasa cu care este reprezentat „step”-ul respectiv, este construit tree-ul de execuții. Dacă este setat si nu este gol cheia de „children” din $node, aceasta înseamnă că node-ul curent are copii și acestea trebuie incluse în tree, aici devine metoda recursiva. Apelăm din nou metoda, acum primul parametru va fi array-ul cu copii din $node și părintele va fi instanța creată din nodul curent. Nu fiecare nod poate sa aibe copii. În exemplul de mai jos este prezentat cum se poate crea o buclă care afișeaza numerele de la zero pana la zece. Lângă „step” și „settings” se pot declara „children” unde se pot defini ce dorim să fie executat, putem să adaugăm mai multe și putem să declarăm și pentru ele copii.
{"program":"Test",
"vars" : {"a" : 0 , "b": 10},
"run" : [{
"step":"loop",
"settings" : { "var" : "a", "start" : 0, "end" : "$b", "inc" : 1 },
"children": [
{ "step" : "echo",
"settings" :{
"text" : "$a"
}
}] }] }
Păna acum a fost prezentat cum este transformat fișierul json într-un tree, dar nu au fost prezentate componentele acesteia. Fiecare parte al tree-ului este o clasă, care este bazată pe clasa „Task”. Aceasta fiind o clasa abstractă care extinde clasa de „Node”. În clasa Node, sunt specificate urmatoarele proprietăți :
public $parentNode = null;
public $firstChild = null;
public $lastChild = null;
public $nextSibling = null;
public $previousSibling = null;
Aceste proprietatii ne ajută la parsarea configurației și la construirea tree-ului, la fel și metodele declarate în clasa aceasta, care sunt publice și fiecare are un rol important în funcționarea programului. De exemplu metoda „walkChildren()” , care este un generator, este responsabil pentru parcurgerea tree-ului, fără aceasta metoda nu va funcționa aplicația pentru că fără aceasta nu se poate trece la următorul pas.
Dacă vrem sa reprezentăm un „step” trebuie sa creiem o clasă nouă care extinde clasa „Task”, aceasta ne asigură că vom avea metodele următoare :__construct(), getVars(), init(), execute() și executeChildren().
Constructorul este setat ca și final, aceasta ne asigură extinderea acestei clase și nu se va putea suprascrie metoda, deci la fiecare instanțiere clasa va fi același. Are doi parametrii, primul este o instanță de IVariables cu ajutorul căreia vom avea access la variabilele declarate. Al doilea este un array cu setări prin care este specificat cum trebuie executat pasul curent.
Metoda getVars() care este protected, deci e vizibil doar în clasa unde a fost declarat sau în clasele care extind aceasta clasă, ne da posibilitatea de a accesa variabilele. Apelând aceasta metodă va returna instanța de Variables.
Sunt doua metode abstracte pentru a forța implementarea lor în clasele care o extind. Aceste sunt init() si execute(), necesită implementare pentru că aceste doua metode sunt diferite pentru fiecare.
O alta metodă importantă care asigură funcționalitatea bună a aplicației este executeChildren(). Aceasta apelează din clasa de Node, pe care extinde, metoda de walkChildren(). Prin generatorul aceasta parcurge copii lui și le executa pe rând.
Clasa care este responsabil pentru cererile de http este clasa Curl. Cum am specificat mai sus clasa extinde pe clasa Task, aceasta înseamnă că metodele init() și execute() necesită implementare aici.
Clasa are multe proprietăți, fiecare e protected și valorile sunt setate prin al doilea parametru primit în constructor.
Proptietațiile sunt urmatorele :
protected $url;
protected $header;
protected $settings;
protected $param;
protected $postFields;
protected $method;
protected $response;
protected $timeout;
protected $maxRequests;
protected $debug;
protected $output;
protected $curl;
Iar în fișierul de configurări tip json declararea arata în felul următor:
{"step" : "curl",
"settings" : {
"method" : "POST", "postFields":"longitude={input.lng}&latitude={input.lat}&searchfield={input.sf}&radius={input.radius}",
"url" : "$baseUrl",
"header" : {"CURLOPT_USERAGENT" : "Mozilla/5.0 (X11; Linux x86_64; rv:41.0"},
"timeout" : "5",
"maxRequests" : "10",
"output" : "page",
"debug" : "false"
}
}
Când initializăm clasa Curl din fișierul de setări se iau proprietațile date și se asignează valoarile. Proprietatea $url în cazul de mai sus va avea valoarea „$baseUrl”, care trebuie declarata în secțiunea de „vars”. Dacă vrem să folosim o anumită valoare din Variables atunci trebuie să declarăm în felul următor „$baseUrl” dacă punem fără „$” atunci nu se va lua valoarea din Variables. Acesta reprezintă url-ul pe care vrem să navigăm. Metoda prin care vrem să facem request este declarată sub cheia „method” și valoarea va fi asignată la $method. Dacă vrem să facem un POST trebuie sa setăm valoarea lui la „postFields”, aici avem posibilitatea de a înlocui parametri cu un input, declararea se face în felul următor longitude={input.lng}&latitude={input.lat}&searchfield={input.sf}&radius={input.radius}. În cazul acesta „input” este o sursă, generarea unei surse va fi prezentat mai jos, ca să accessam o valoare trebuie specificat sursa și cheia de pe care vrem sa luam valoarea, de exemplu input.lng.
Ca să setăm un request header personalizat, trebuie sa declarăm secțiunea de „header” cu cheia și valoare, cheia trebuie să fie numele optiunei din cURL pe care vrem să setăm iar valoarea dată reprezintă la ce vrem sa setăm. În cazul de mai sus am setat user-agent-ul „Mozilla/5.0 (X11; Linux x86_64; rv:41.0”.
Având în vedere că sunt unele website-uri care răspund mai încet, putem să declarăm câte secunde să aștepte pana va reuși să se conecteze la server. Aceasta este declarată în setari sub cheia „timeout” și stocat în proprietatea $timeout și în mod implicit este setat pe zece secunde.
Uneori când facem un request nu primim răspuns de la server, din anumite motive. Se poate specifica de câte ori vrem să fie repetat cererea așteptând după un răspuns de la server, în mod implicit este setat pe unu.
În modul de „debug” aplicația va afișa o statistică despre request-ul curent, de exemplu cât timp a luat să facă cererea. Aceasta va fi prezentat mai detaliat în continuare.
Dacă dorim să folosim în continuare ce a returnat serverul putem sa o salvăm intr-o variabilă, în cazul de mai sus declarând „output” răspunsul primit va fi salvat în „page”, implicit salvează tot în variabila page, dar e mai bine sa declarăm.
În metoda „init()” toate aceste setări sunt executate, asigurând că vom avea acces la tot ce ne trebuie în continuare. Metoda „execute()” trebuie să fie implementată, efectiv aici trebuie să fie executata cURL-ul. Pentru acesta sunt declarate niște metode ajutătoare, de exemplu „setVars(string $text)”, care este responsabil pentru a lua datele dintr-o sursă și pentru a înlocui în textul trimis ca și parametru. După ce au fost inlocuite parametrii metoda va returna textul nou.
Din exemplul de mai sus textul trimis la „postFields” arată în felul următor longitude={input.lng}&latitude={input.lat}&searchfield={input.sf}&radius={input.radius}, dacă apelăm metoda setVars și primește ca și parametru acest string va încerca să inlocuiască tot ce e intre acolade cu valoriile din „input”, de exemplu după parsare va arată așa longitude=13.40&latitude=52.52&searchfield=Berlin&radius=10. Dacă parametrul trimis începe cu caracterul $ atunci mai întâi va scoate din clasa Variables valoarea respectiva și după aceasta va inlocui ce trebuie. Dacă nu poate sa scoată un anumit câmp din sursa atunci nu va inlocui cu nimic, de exemplu longitude={input.lnag}&latitude=52.52&searchfield=Berlin&radius=1, în sursa input nu s-a găsit câmpul „lnag”.
O metoda care este importanta în clasa Curl este setCurl, această metodă este responsabilă pentru setarea cURL-ului și aici este decis ce fel de request să facă, GET sau POST.
Primește un singur parametru, care este un array de setări pentru cURL, cea ce în exemplul de mai sus a fost pasat sub cheia „header”. Toate acesteia sunt setate prin funcția din libraria cURL curl_setopt().
Urmatoarele opțiunii sunt setate prin funcția curl_setopt():
CURLOPT_URL – prin aceasta opțiune se setează ulr-ul pe care dorim să navigăm. Aici va fi setat url-ul trimis sub cheia „url”.
CURLOPT_CONNECTTIMEOUT – reprezintă timpul de asteptare în secunde, pentru a conecta la server.
CURLOPT_RETURNTRANSFER – acceptă true sau false, și determină dacă răspunsul de la server să fie afișat în momentul când a primit aceasta sau nu. Dacă este pe true atunci nu va afișa.
CURLOPT_VERBOSE – trebuie setat pe true sau pe false, dacă e setat pe true atunci va afișa un header și niște informații suplimentare despre request. Trebuie setat pe false, pentru a nu afișa nimic, pentru că în aplicația dezvoltata este implementată o statistică pentru aceasta.
CURLOPT_HEADER – la fel și acesta acceptă numai true sau false și dacă este setat pe true atunci împreună cu răspunsul de la server, va transmite și header-ul.
CURLOPT_POST si CURLOPT_POSTFIELDS – dacă vrem să facem un POST request către server atunci CURLOPT_POST trebuie să fie setat pe true, iar al doilea CURLOPT_POSTFIELDS, trebuie sa conțină un string cu valorile pe care vrem să pasăm prin POST. De exemplu: longitude=13.40&latitude=52.52&searchfield=Berlin&radius=10.
La executarea acestei părți în tree, ca și la fiecare, va fi apelat functia execute(), nu primește nici un parametru, doar execută ce trebuie și cum a fost setat. În clasa Curl aceasta metoda începe cu trei comenzi: $this->curl = curl_init(); , $this->setCurl($this->header); , $response = curl_exec($this->curl);. Prima comandă inițializează sesiunea de cURL, al doilea setează header-ul și tot ce trebuie pentru a executa sesiunea, iar al treilea execută cURL-ul și răspunsul va fi stocat în variabila $response. După acesta rezultatul va fi pasat mai departe către clasa Variables și salvat în variabila declarată pentru acest scop. Dacă aplicația este în modul de testare, aceasta înseamnă că proprietatea debug este setata pe true, atunci va apela funcția statistic() și care va afișa niște informații despre request. Un exemplu de statistică, după ce a fost executat un request, de unde putem să verificăm de exemplu unde și cu ce a fost executat requestul, status codul returnat, cât timp a luat pană a conectat la server. Sunt informații utile când vrem sa testăm ceva.
Array
(
[method] => POST
[url] => http://www.mcdonalds.de/search
[postFields] => longitude=13.404954&latitude=52.5200066&searchfield=Berlin&radius=10000
[header] => HTTP/1.1 200 OK
Server: nginx
Date: Sun, 26 Jun 2016 09:44:52 GMT
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
Set-Cookie: JSESSIONID=88961E2DD0E59606F438F0C0DA7EBEB4; Path=/; HttpOnly
Front-End-Https: off
[total_time] => 2.279798
[connect_time] => 0.127445
)
O altă clasa care are un rol important în aplicație este clasa LoopInput. Cu ajutorul acestei clase putem să parcurgem o sursă cu datele pentru request și să pasăm mai departe către clasa Curl. Clasa este bazată pe o altă clasă, Loop, cu ajutorul careia aplicația poate să execute o instrucțiune repetitivă. Diferența intre cele doua clase este că LoopInput-ul în timpul ce parcurge o listă, salvazeă valoarea curentă și se poate folosi și altundeva, de exemplu la formarea unei url nouă sau la afișare petru teste. Clasa LoopInput este o clasă foarte importantă in aplicație fară aceasta nu ar fi de folos aceasta sau ar deveni foarte complicat utilizarea aplicației.
Urmatoarele proprietăți sunt declarate în clasă:
protected $var;
protected $start;
protected $end;
protected $inc;
protected $name;
protected $input;
Un json string unde este declarat o sursă pe care o parcurgem arată în felul următor:
{"program":"Test",
"vars" : {"i" : 0, "n" : 2
"input" : [{
"lng" : 13.404953999999975,
"lat" : 52.52000659999999,
"sf" : "Berlin",
"radius" : 10
},{
"lng" : 11.404953999999975,
"lat" : 42.52000659999999,
"sf" : "Bonn",
"radius" : 10
}]
}, "run" :
[{
"step":"loopInput",
"settings" : {
"var" : "i",
"start" : 0,
"end" : {"function" : "count", "var" : "input"},
"inc" : 1,
"input" : "input",
"name" : "valueInput"
}
}]}
Dacă dorim să folosim o sursă atunci acesta trebuie să fie declarată la „vars” și trebuie să fie un array unde este specificat cheia și valoarea. Numele poate să fie orice, în cazul de mai sus este numit input. Dacă vrem că aplicația să parcurgă acest input la setării trebuie specificat numele sursei la cheia „input” și aceasta va fi stocat în proprietatea $input. Cheia „var” reprezintă variabila cu care parcurgem sursa, se pot declara de unde să înceapă cu „start”. Pana cât timp să parcurga sursa respectivă se poate specifica în cheia „end”, în exemplul de mai sus se poate observa că primește o funcție și o variabila, în cazul acesta „end” va fi egal cu numărul total de elemente din sursa. Dar tot aici dacă dorim putem sa setăm o valoare numerică din start de exemplu "end" : 10 sau numele unei variabilă care este declarată în clasa Variables de exemplu "end" :"$n". În cazul acesta trebuie sa punem înainte de numele variabilei caracterul $ că aplicația să știe că aici va trebui să caute după o variabilă. Cheia „inc” specifică cu cât să fie incrementat indexul în fiecare pas, cu valoarea unu va merge unu după celalat. „name” este foarte important pentru că cu acesta putem să identificăm o sursă și o refolosim de altundeva.
Exemplul de mai sus se poate extinde dacă adaugăm următorul set de pași în fișierul de configurari, care reprezintă un subtask. Aceasta trebuie să fie adaugată după „settings” cu virgula.
,"children" : [{
"step" : "curl",
"settings" : {
"method" : "POST",
"postFields" : "longitude={valueInput .lng}&latitude={valueInput .lat}&searchfield={valueInput .sf}&radius={valueInput .radius}",
"url" : "$baseUrl",
"param" : "value",
"header" : {"CURLOPT_USERAGENT" : "Mozilla/5.0 (X11; Linux x86_64; rv:41.0"},
"timeout" : 5,
"maxRequests" : 10,
"output" : "page",
"debug" : true
}
}]
Aici partea de „postFields” are un rol mai important, daca vrem sa alcatuim un string dinamic si avem o sursă de unde putem să scoatem valorile acestea se pot realiza punând intre acolade numele sursei și cheia de pe sursă separate cu punct. Astfel curl-ul va face un request odata cu string-ul „longitude=13.40&latitude=52.52&searchfield=Berlin&radius=10” după care cu string-ul „longitude=11.40&latitude=42.52&searchfield=Bonn&radius=10” .
Acest aspect al aplicației ne ajuta foarte mult la testarea unei request pentru că putem să verificăm cum se comportă cererea cu anumite valori predefinite.
Clasa LoopInput extinde clasa Task, deci și aici sunt implementate metodele init() și execute(). În init() sunt initizalizate proprietațiile prezentate mai sus. Iar în metoda execute() sunt executate anumiți pași. Parcurgerea listei de sursă este realizată cu ajutorul metodelor: rewind(), isDone() și increment(). În prima metoda este setat variabila în clasa Variables pe o valoare de start predefinită, valorile posibile au fost prezentate mai sus. Metoda isDone() verifica dacă trebuie să parcurgă în continuare lista sau nu, va returna true sau false, în cazul în care trebuie sa meargă mai departe va returna true. Ultima metoda, increment(), este responsabil pentru a incrementa indexul curent, așa trecând pe următorul element din lista.
Metoda execute() din clasa LoopInput începe cu apelarea metodei de rewind, după care declară o variabilă unde stochează instanța de Variables ca să aibă acces la varibilele definite în aplicație. Următorul pas este accesarea sursei după care vine o instrucțiune repetitiva care va fi executată pană metoda isDone() returneaza valoarea true. În fiecare buclă este salvată valoarea curenta din sursa după care este apelată metoda executeChildren(), această metodă este declarată în clasa Task. Ultimul pas este apelarea metodei de increment. Acest pași sunt repetate pană isDone() nu returnează false.
Cap V. Concluzii
5.1 Realizarea obiectivului propus
Aplicația prezentată in capitolul anterior este o aplicatie cu ajutorul careia tastarea websiteurilor este foarte ușor. Se pot testa si analiza cererile făcute catre servere si se pot compara rezultatele primite. Având in vedere că funcționează cu un fisier de configurări fără să modificăm codul putem să rulăm teste automatizate, e ușor de întreținut și extins si cel mai important aspect este că nu trebuie să fie programator cine folosește aplicația aceasta, e deajuns să cunoască cum să scrie fisierul de configurări.
Am propus dezvoltarea unei aplicații care este ușor de folosit, integrat in proiecte și cu ajutorul careia putem să detectăm anumite bug-uri înainte de a lansa un website. Totuși cu acest tool putem să testăm cum se comportă siteul nostru dacă facem multe requesturi una dupa celalalt si cerem niste resurse mari. Putem să vedem în cât timp raspunde serverul, cât a luat să transmite resursele și status codul. Putem să verificăm cum raspunde serverul la anumite cereri dacă setam un header.
Dupa toate aceste teste putem să decidem dacă siteul nostru este stabil, funcționează așa cum am planificat și requesturile fac și returneaza ce trebuie. Este important să testăm funcționalitatea siteului înainte de a lansa aceasta și cu ajutorul unei aplicați care simulează cateva aspecte definite.
Pentru a implementa această aplicație am folosit PHP 7 si libraria cURL, nu am implementat să folosească și functia de file_get_contents() impreuna cu un context pentru că aceasta este folosită numai pentru a lua anumite resurse. Totusi in lucrare am prezentat cum se poate face requesturi cu această funcție pentru că este ușor de folsit, este foarte simplu și e o functie built-in in PHP.
Aplicația este bazată pe libraria cURL si DOM. Ambele au niste funcționalități care m-au ajutat să implementez această aplicație de exemplu la executarea unei sesiuni de cURL se poate cere header-ul, statusus codul, durata cererii și multe alte informații, ceea ce m-a ajutat să am o statistică după fiecare request. Din punctul meu de vedere cel mai bun aspect al aplicației este că se pot face requesturi automatizate cu o sursă și se poate verifica cum tratează serverul aceste cereri.
Contribuțile personale la această lucrare sunt urmatoarele:
cercetarea greșerilor în formulări de căutare
prezentarea rezultatelor în urma cercetări
implementarea aplicației pentru teste
prezentarea aplicației
concluzia lucrări
Aplicatia este bazată pe programarea orientată pe obiecte (OOP) și din cauza aceasteia se poate extinde ușor. Este structurat asftel încât că clasele care nu sunt responsabili pentru logica aplicației numai au roluri de sinestatatoare se poate modifica sau înlocui cu ușurința. Aceasta este un aspect foarte important în dezovoltarea aplicației pe viitor pentru că se poate împarți dezvoltarea în echipă și putem să înclouim anumite componente fără să stricăm cea ce funcționează deja.
Pe viitor ar trebui o interfață pentru aplicația aceasta ca să fie mai ușor pentru utilizatori generarea fișierul de configurari. Cu o interfață care permite crearea, modificarea și administrarea acestei configurari aplicația se poate integra în orice proiect în faza de testare.
Un alt aspect pe care se poate dezvolta în aplicație este adaugarea requesturilor folosind un proxy sau o listă de proxy-uri. Aceasta ar permite sa protejam mai bine siteul, pentru ca am putea testa dacă siteul nostru rezista și daca poate să detecteze dacă sunt requesturi ambiguoase.
Aplicația în momentul de fata este numai o baza, care permite câteva teste simple, dar se poate dezvolta în continuare. Dacă va fi dezvoltat în direcția de a deveni un tool de testing va fi de folos și în aplicații de web mai mari sau mai mici. Dacă cineva vrea să dezvolte ca și un web crawler, pentru aduce date și aceasta este posibilă foarte simplu prin adaugarea câteva componente noi.
5.2 Utilitatea aplicației în managementul paginilor web
Sunt programe integrate în aplicații web cele care pot detecta dacă sunt anumite requesturi ambiguoase, de exemplu de pe un IP vin foarte multe cereri intr-un interval mic. Programul poate să interzica IP-ul respectiv să facă încă un request ori pe un anumit timp ori pe veci. Astfel se pot opri un web crawler. Dar dacă web crawlerul poate să ia toate datele noastre cu un singur request sau cu foarte puține requesturi programul nu poate sa-l detecteze. De aceea este foarte important să nu avem pe site anumite bug-uri. Aceste pot influența în mod drastic funcționalitatea site-ului, de exemplu făcând siteul mai lent. Un exemplu real ar fi, dacă avem foarte multe date, peste un milion, și orice tip de date care sunt publice pe site, de exemplu informații despre restaurante. În cazul în care nu este limitată răspunsul de la server să zicem că în mod normal ar trebui sa returneze zece rezultate dar din cauza că limita aceasta poate să fie modificată pe orice numar, poate să returneze toate datele noastre dintr-o baza da date. Generând un request al cărui răspuns este foarte mare rezervă resursele. În exemplul acesta toate datele noastre colectate, prelucrate, publicate, datele care aduc bani din care funcționează firma au fost luate cu un singur request.
Din această cauză în dezvoltarea unei pagini web este important să fie detectate anumite buguri și să fie bine gândit logica site-ului. Pentru un proiect manager în domeniul aceasta ar fi de folos o aplicație de genul care poate să simuleze requesturile și să arate anumite statistici. Dacă testele sunt bine definite în dezvoltarea unei pagini web atunci din start se poate zice dacă siteul funcționează bine sau mai trebuie verificate anumite componente.
Testele predefinite sunt foarte importante, cu această aplicație se pot genera ușor aceste teste. Se pot modifica oricând și de oricine pentru ca e ușor de învățat cum funcționează aplicația. Aceasta este un aspect forate important să fie ușor de folosit ca să nu piarda nimeni mult timp cu invațarea și cu setarea acesteia.
Din cauza că aplicația poate să fie extinsă ușor se poate satisface orice nevoie în timpul dezvoltarii a unei pagini web cea ce face aplicația să fie de folos în orice situație. Dacă aplicația va fi dezvoltată pe un nivel mai mare aceasta va fi un tool de neînlocuit în managementul paginilor web.
Tabela 1. (Rezultatele după primul test)
Tabela 2. (Rezultatele după al doilea test)
Un exemplu real când este de folos această aplicație în domeniul managamentului paginilor web este că se poate înființa o statistică după anumite teste automatizate. Am făcut o pagina simpla pentru a testa cum afectează o mica greșeala încărcarea paginii. Testul constă în prezenta bug-ului că rezultatele returnate după o căutare nu sunt limitate corect, în sesnul că limita aceasta poate să fie modificata de oricine. Folosind aplicația implementată am generat requesturi una după celălalt modificând limita rezultatelor cu câte o suta de mii, începând de la unu. În prima coloană apare limita rezultatelor, în al doilea durata requestului în secunde, iar în al treilea mărimea datelor primite. Cele două teste au fost rulate simultan și diferă numai durata raspunsului.
Am simulat această problemă ca să văd cât timp ar trebui să aștept pănă să am rezultatele dorite. Aceasta este de folos în dezvoltarea unei pagini web, pentru că putem să simulăm cât timp ar trebui să așteaptă un utilizator pănă să primească rezultatele dorite.
Jakob Nielsen in cartea Usability Engineering – 1993, a definiti câteva aspecte despre importanța timpului de raspuns în optimizarea performanței de aplicații web. A scris că zece secunde este aproximativ limita pentru păstrarea atenției utilizatorului, după timpul aceasta utilizatorul va dori să facă altceva. Din testele de mai sus se vede ca la cinci sute de mii, requestul deja a durat mai mult decât zece secunde cea ce nu este bine , problema merita și trebuie fixată.
Bibliografie
Short History of Early Search Engines: Lycos – http://www.thehistoryofseo.com/The-Industry/Short_History_of_Early_Search_Engines.aspx
Googlebot – https://www.google.com/insidesearch/howsearchworks/crawling-indexing.html
Header – http://www.tutorialspoint.com/http/http_header_fields.htm
Content-Type – https://www.w3.org/Protocols/rfc1341/4_Content-Type.html
cURL – http://php.net/manual/en/ref.curl.php și http://php.net/manual/en/function.curl-getinfo.php
cURL Setopt – http://php.net/manual/en/function.curl-setopt.php
Metode HTTP : http://www.w3schools.com/tags/ref_httpmethods.asp
Web Crawlers – https://www.sciencedaily.com/terms/web_crawler.htm
Search Engines – http://computer.howstuffworks.com/internet/basics/search-engine1.htm
Web Crawlers în diverse domeni – https://www.promptcloud.com/web-crawl-use-cases
Functia file_get_contents – http://php.net/manual/en/function.file-get-contents.php
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Utilizarea Tehnici Web Crawling Pentru Depistarea Deficientelor de Securitatea ale Datelor Paginilor Webdoc (ID: 120594)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
