Bine ai venit la Construirea dvs. de pornire Cu seria PHP, care este ghidând cititorii prin lansarea unei lansări reale, Planificatorul întâlnirilor. Fiecare episod detaliază diferite provocări de codificare și de afaceri, cu exemple detaliate pe care le puteți folosi pentru a afla.
Recent, ți-am prezentat generația simplă a generației YES REST API și noul API de servicii "RESTful" de la Planner Meeting. În acel moment, am menționat că aceste API-uri au fost doar securizate. Sigur, a existat un secret comun între client și server, dar au existat câteva probleme.
În primul rând, cheile secrete și jetoanele de utilizator au fost transmise în mod repetat în parametrii de interogare ai apelurilor SSL. Și nu există altă verificare a autenticității datelor, permițând un atac de persoană mijlocie.
În episodul de astăzi, vă voi îndruma prin modul în care am asigurat API-ul împotriva acestor slăbiciuni pentru un API mai robust.
Dacă ați citit seria noastră de pornire, probabil ați încercat deja Planificatorul întâlnirilor și Planificatorul simplu, dar dacă nu, vă rugăm să faceți. Programarea unei întâlniri este ușoară:
Ca de obicei, voi participa la comentariile de mai jos, deci vă rugăm să vă oferiți gândurile. Puteți să mă contactați și pe Twitter @lookahead_io. Întotdeauna am intrigat în special dacă doriți să sugerați noi caracteristici sau subiecte pentru tutoriale viitoare.
Ca un memento, întregul cod pentru Planificatorul întâlnirilor este scris în cadrul Yii2 Framework for PHP. Dacă doriți să aflați mai multe despre Yii2, consultați seria noastră paralelă Programming With Yii2.
Să începem să aruncăm o privire la securitatea timpurie a API pe care am codificat-o. Vom presupune că există o aplicație mobilă pe care am împărțit-o $ APP_ID
și $ app_secret
cu. Doar apelanții API cu aceste chei sunt acceptați.
De exemplu, aplicația încearcă să-și înregistreze proprietarul, probabil un nou utilizator al Planificatorului de întâlniri:
funcția publică funcțiaRegister ($ app_id = ", $ app_secret =", $ source = ", $ firstname =", $ lastname = ", $ email =", $ oauth_token = ") format = Răspuns :: FORMAT_JSON; // verificați app_id și app_key dacă (! Service :: verifyAccess ($ app_id, $ app_secret)) // to do - error mesaj return false;
Aplicația cheamă cele de mai sus actionRegister
prin https://api.meetingplanner.io/user-token/register/ cu argumentele după cum urmează:
$ APP_ID
și $ app_secret
pentru autentificare$ sursa = 'facebook'
pentru serviciul OAuth pe care îl folosim și care îl însoțește $ oauth_token
din acel serviciu$ e-mail
, $ firstname
, și $ NumeDeFamilie
furnizate prin OAuthToate acestea sunt argumente de interogare, cum ar fi:
https://api.meetingplanner.io/user-token/register/?app_id=777&app_secret=imwithher&source=facebook&oauth_token=zuckerburger&email=tom@macfarlins.com&firstname=thomas&lastname=macfarlins
Serviciul :: verifyAccess ($ APP_ID, $ app_secret)
caută cheile pentru a autentifica apelul, după cum se arată mai jos:
($ app_id, $ app_secret) if ($ app_id == Yii :: $ app-> params ['app_id'] && $ app_secret == Yii :: $ app-> params [ 'app_secret'])) Yii :: $ app-> params ['site'] ['id'] = SiteHelper :: SITE_SP; return true; altfel return false;
Deoarece cheile și datele au fost trimise prin SSL, ele sunt destul de sigure, dar nu invincibile. Nu este sigur nici cheia secretă pe iPhone-urile utilizatorilor.
Cum putem face acest lucru mai sigur? Iată câteva idei:
Acestea sunt de fapt practicile standard utilizate pentru securizarea API-urilor.
Notă: Un exemplu de risc de transmitere a datelor care ar putea fi expuse în jurnalele de server ar fi e-mailul și jetonul Facebook OAuth. Dacă sunt găsite în jurnale, acestea pot fi utilizate cu API-ul Facebook pentru a accesa contul Facebook al unei persoane.
În primul rând, voi opri transmiterea $ app_secret
. În schimb, vom semna datele înainte de a efectua un apel API.
Deci, vom alfabetiza variabilele și le vom concatena într-un șir, astfel:
$ date = $ e-mail. $ firstname. $ lastname. $ oauth_token. $ source;
Rezultând:
$ data = '[email protected]'
Apoi, vom șterge datele cu hash_hmac și cu PHP SHA256
folosind algoritmul nostru secret.
$ signature = hash_hmac ('sha256', $ date, Yii :: $ app-> params ['app_secret']);
Acest lucru creează un cod hash unic pe baza argumentelor apelului API și a cheii noastre secrete partajate:
$ signature => 9f6d2f7dd7d674e85eff51f40f5f830787c37d84d4993ac9ccfea2800285bd02
Acum, putem apela API fără a transmite cheia secretă. În schimb, transmitem semnătura datelor de mai sus.
Am folosit Postman pentru a testa API-ul, dar puteți utiliza și cURL:
Iată codul de primire API care a răspuns la apelul de mai sus:
funcția publică funcțiaRegister ($ app_id = ", $ source =", $ firstname = ", $ lastname =", $ email = ", $ oauth_token =", $ sig = ") format = Răspuns :: FORMAT_JSON; $ sig_target = hash_hmac ('sha256'; $ email; $ firstname; $ lastname; $ oauth_token; $ source; Yii :: $ app-> params ['app_secret' ! = Yii :: $ app-> params ['app_id'] && $ sig == $ sig_target) retur 'a funcționat!'; Altceva return 'failed!';
Mai mult, după cum am examinat ultima oară, fiecare utilizator primește propriul token atunci când accesează Planificatorul întâlnirilor prin API, de ex. prin intermediul telefonului mobil. Prin urmare, după înregistrare, putem semna apelurile cu jetonul individual și nu este necesar să transmitem fie cheia secretă a aplicației, fie jetonul individual al utilizatorului.
Apoi, vom migra trimiterea datelor în anteturi. Puteți face acest lucru cu ușurință cu Postman sau cURL. Iată poștașul:
Și iată cURL:
funcția publică funcțiaCurl ($ sig) $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, "http: // localhost: 8888 / mp-api / user-token / registru? sig =". $ sig); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, true); $ headers = ['app_id:' .'imwithher ',' email: '.'tom @ macfarlins.com', 'firstname:'. 'thomas', 'lastname:'. 'macfarlins', 'oauth_token: zuckerburger "," sursă: ".facebook",]; curl_setopt ($ ch, CURLOPT_HTTPHEADER, $ anteturi); $ server_output = curl_exec ($ ch); var_dump ($ server_output); curl_close ($ ch);
Iată codul de primire care primește datele API din anteturile HTTPS:
funcția publică funcțiaRegister ($ sig = ") Yii :: $ app-> response-> format = Răspuns :: FORMAT_JSON; $ headers = Yii :: $ app-> request-> headers; $ email = $ headers-> get (nume de familie); $ firstname = $ headers-> get ('firstname'); $ lastname = $ headers-> get ('lastname'); $ oauth_token = $ headers-> get ('oauth_token' = $ header_-> get ('source'); dacă ($ headers-> are ('app_id')) $ app_id = $ headers-> get ('app_id'); $ sig_target = ($ app_id! = Yii :: $ app-> params ['app_id'] && $ parola ['app_secret'] && $ sig == $ sig_target) retur "a funcționat!"; altceva return 'failed!';
Am început astăzi cu următoarele obiective:
Și am realizat toate aceste obiective doar cu modificări modeste ale codului nostru API. A fost distractiv să facem aceste schimbări și să vedem cât de ușor putem securiza mai bine un API. Sper că ți-a plăcut să urmăriți împreună cu episodul de astăzi.
Monitorizez periodic comentariile, așa că te rog să participi la discuție. Puteți să mă contactați și pe Twitter @lookahead_io direct. Și, bineînțeles, urmăriți tutoriale viitoare aici în seria Building Your Startup With PHP.
Dacă nu ați făcut-o mai devreme, încercați să programați o întâlnire la Planificatorul de întâlniri și să-mi spuneți ce credeți. Apreciez în special solicitările de caracteristici.