Welkom bij de Bouw je Startup met PHP-serie, dat is lezers begeleiden bij het opstarten van een daadwerkelijke startup, Meeting Planner. Elke aflevering bevat verschillende coderings- en zakelijke uitdagingen, met gedetailleerde voorbeelden die u kunt gebruiken om te leren.
Onlangs heb ik u kennis laten maken met Yii's eenvoudige REST API-generatie en de nieuwe "RESTful" service-API van Meeting Planner. In die tijd zei ik dat deze API's slechts losjes beveiligd waren. Natuurlijk was er een gedeeld geheim tussen de client en de server, maar er waren een aantal problemen.
Ten eerste werden de geheime sleutel en gebruikertokens herhaaldelijk verzonden in queryparameters van SSL-oproepen. En er was geen andere authenticiteitscontrole voor de gegevens, waardoor een aanval van een gemiddelde persoon mogelijk was.
In de aflevering van vandaag zal ik u laten zien hoe ik de API tegen deze zwakke punten heb beveiligd voor een robuustere API.
Als u onze startup-serie hebt gelezen, hebt u waarschijnlijk al vergaderingsplanner en Simple Planner geprobeerd, maar zo niet, doe het dan. Een vergadering plannen is eenvoudig:
Zoals gewoonlijk, neem ik deel aan de onderstaande opmerkingen, dus geef alstublieft uw mening. Je kunt me ook bereiken op Twitter @lookahead_io. Ik ben altijd vooral geïntrigeerd als je nieuwe functies of onderwerpen voor toekomstige zelfstudies wilt voorstellen.
Ter herinnering: alle code voor Meeting Planner is geschreven in het Yii2 Framework voor PHP. Als je meer wilt weten over Yii2, bekijk dan onze parallelle serie Programming With Yii2.
Laten we beginnen met een kijkje te nemen naar de vroege API-beveiliging die ik heb gecodeerd. We gaan ervan uit dat er een mobiele app is die ik heb gedeeld $ APP_ID
en $ app_secret
met. Alleen API-bellers met deze sleutels worden geaccepteerd.
De app probeert bijvoorbeeld zijn eigenaar te registreren, waarschijnlijk een nieuwe Meeting Planner-gebruiker:
public function actionRegister ($ app_id = ", $ app_secret =", $ source = ", $ firstname =", $ lastname = ", $ email =", $ oauth_token = ") Yii :: $ app-> response-> format = Response :: FORMAT_JSON; // verifieer app_id en app_key if (! Service :: verifyAccess ($ app_id, $ app_secret)) // to do - error msg return false;
De app roept het bovenstaande actionRegister
via https://api.meetingplanner.io/user-token/register/ met argumenten als volgt:
$ APP_ID
en $ app_secret
voor authenticatie$ source = 'facebook'
voor de OAuth-service die we gebruiken en die we vergezellen $ oauth_token
van die dienst$ e-mail
, $ voornaam
, en $ achternaam
aangeboden via OAuthDit zijn allemaal queryargumenten zoals:
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
Dienst :: verifyAccess ($ APP_ID, $ app_secret)
zoekt de sleutels op om het gesprek te authenticeren zoals hieronder getoond:
class Service breidt Model public static function verifyAccess ($ 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; geef waar terug; else return false;
Omdat de sleutels en de gegevens via SSL zijn verzonden, zijn ze redelijk veilig maar niet onoverwinnelijk. De geheime sleutel is ook niet zeker op de iPhones van gebruikers veilig.
Hoe kunnen we dit veiliger maken? Hier zijn een paar ideeën:
Dit zijn eigenlijk standaardmethoden die worden gebruikt voor het beveiligen van API's.
Notitie: Een voorbeeld van het risico van het verzenden van gegevens die in serverlogboeken kunnen worden weergegeven, is het e-mailadres en het OAuth-token van Facebook. Als deze in logboeken worden gevonden, kunnen deze met de Facebook-API worden gebruikt om iemands Facebook-account te openen.
Eerst stop ik het verzenden van de $ app_secret
. In plaats daarvan ondertekenen we de uitgaande gegevens ermee voordat een API-aanroep wordt gedaan.
Dus we zullen de variabelen alfabetiseren en ze samenvoegen tot een tekenreeks, zoals deze:
$ data = $ email. $ voornaam. $ lastname. $ oauth_token. $ source;
Met als resultaat:
$ data = '[email protected]'
Vervolgens zullen we de gegevens hashen met PHP's hash_hmac en de sha256
algoritme met behulp van onze geheime sleutel.
$ signature = hash_hmac ('sha256', $ data, Yii :: $ app-> params ['app_secret']);
Hiermee wordt een unieke hash-code gemaakt op basis van de argumenten van de API-aanroep en onze gedeelde geheime sleutel:
$ signature => 9f6d2f7dd7d674e85eff51f40f5f830787c37d84d4993ac9ccfea2800285bd02
Nu kunnen we de API bellen zonder de geheime sleutel te verzenden. In plaats daarvan verzenden we de handtekening van de gehashte gegevens hierboven.
Ik gebruik Postman om de API te testen, maar u kunt ook cURL gebruiken:
Dit is de API-code voor ontvangen die op de bovenstaande oproep heeft gereageerd:
public function actionRegister ($ app_id = ", $ source =", $ firstname = ", $ lastname =", $ email = ", $ oauth_token =", $ sig = ") Yii :: $ app-> response-> format = Reactie :: FORMAT_JSON; $ sig_target = hash_hmac ('sha256', $ email. $ voornaam. $ achternaam. $ oauth_token. $ source, Yii :: $ app-> params ['app_secret']); if ($ app_id ! = Yii :: $ app-> params ['app_id'] && $ sig == $ sig_target) return 'het werkte!'; Else return 'failed!';
Verder, zoals ik de vorige keer heb beoordeeld, ontvangt elke gebruiker zijn eigen token wanneer hij toegang heeft tot Meeting Planner via de API, bijvoorbeeld via hun mobiele telefoon. Dus na de registratie kunnen we oproepen met hun individuele token ondertekenen en hoeven we niet de geheime sleutel van de toepassing of de individuele token van de gebruiker te verzenden.
Vervolgens migreren we het verzenden van gegevens in de headers. Je kunt dit eenvoudig doen met Postman of cURL. Hier is Postman:
En hier is CURL:
public function actionCurl ($ sig) $ ch = curl_init (); curl_setopt ($ ch, CURLOPT_URL, "http: // localhost: 8888 / mp-api / user-token / register? sig =". $ sig); curl_setopt ($ ch, CURLOPT_RETURNTRANSFER, true); $ headers = ['app_id:' .'imwithher ',' email: '.'tom @ macfarlins.com', 'firstname:' .'thomas ',' lastname: '.'macfarlins', 'oauth_token:'. ' zuckerburger ',' source: '.'facebook',]; curl_setopt ($ ch, CURLOPT_HTTPHEADER, $ headers); $ server_output = curl_exec ($ ch); var_dump ($ server_output); curl_close ($ ch);
Hier is de ontvangende code die de API-gegevens van HTTPS-headers ontvangt:
public function actionRegister ($ sig = ") Yii :: $ app-> response-> format = Response :: FORMAT_JSON; $ headers = Yii :: $ app-> request-> headers; $ email = $ headers-> get ('email'); $ firstname = $ headers-> get ('firstname'); $ lastname = $ headers-> get ('lastname'); $ oauth_token = $ headers-> get ('oauth_token'); $ source = $ headers-> get ('source'); if ($ headers-> has ('app_id')) $ app_id = $ headers-> get ('app_id'); $ sig_target = hash_hmac ('sha256', $ email. $ voornaam. $ achternaam. $ oauth_token. $ source, Yii :: $ app-> params ['app_secret']); if ($ app_id! = Yii :: $ app-> params ['app_id'] && $ sig == $ sig_target) return 'het werkte!'; else return 'failed!';
We zijn vandaag begonnen met de volgende doelen:
En we hebben al deze doelen bereikt met slechts bescheiden wijzigingen in onze API-code. Het was leuk om deze wijzigingen aan te brengen en te zien hoe gemakkelijk we een API beter kunnen beveiligen. Ik hoop dat je het leuk vond om mee te gaan met de aflevering van vandaag.
Ik volg regelmatig de opmerkingen, dus doe mee aan de discussie. Je kunt me ook rechtstreeks op Twitter @lookahead_io bereiken. En, natuurlijk, kijk voor de komende tutorials hier in de Building Your Startup With PHP-serie.
Als je dat niet eerder hebt gedaan, probeer dan een vergadering te plannen bij Meeting Planner en laat me weten wat je ervan vindt. Ik stel vooral functieverzoeken op prijs.