He iedereen, ik heb een vraagje. Hoe maak ik een 'veilige API'? Ik ben namelijk bezig met een iOS applicatie volledig geschreven in Swift, deze applicatie haalt diverse JSON bestanden op van een server. Dit werkt allemaal prima, alleen het liefste wil ik de niet dat zomaar iemand de JSON bestanden kan opvragen door de URL bijvoorbeeld in een browser in te typen. Hoe maak ik hier een beveiliging voor met bijvoorbeeld tokens/headers.
Heb namelijk een beetje onderzoek gedaan naar iOS applicatie, als je bv de urls van de feeds van AD.nl, Foxsports, RTV Noord (nieuws applicatie framework die door veel regionale omroepen worden gebruikt) weet kun je deze feeds gewoon aanroepen in je browser en eventueel gebruiken in je websites, applicaties etc. Alleen die van de NOS is beveiligd met een custom key header, maar als ik die key header in de browser mee geef kan ik als nog de feeds bekijken en aanroepen. Hoe kan ik dit beveiligen?
Je kan natuurlijk met speciale headers werken, of GET-argumenten die key's bevatten. Maar een waterdichte en 100% veilige API bestaat gewoon niet. Elke App is te sniffen met Whireshark of andere tooltjes, en dus ook de locatie waar de data vandaan komt.
Je zou een controle op de useragent kunnen doen, om het moeilijker te maken.
Hoeft ook niet 100% veilig te worden, zolang de data maar niet door de eerste de beste gebruiken opgehaald/bekeken te kan worden. Want als je eenmaal de key uit de header gehaald hebt kun je die mee sturen met je eigen request, is hier niet iets tegen te doen?
Dacht zelf een beetje zoiets als dit:
Ik stuur een deviceToken (iPhone heeft zo'n ding die uniek is met 32 karakters) en een unieke hash die bij die token hoort. Die token en hash worden mee gestuurd in een header, de api op de server vergelijkt deze token en hash dan met de gegevens in de database, komen deze overeen dan mag die json terug geven, en anders niet. Maar als de 'hacker' deze token en hash achterhaald heeft van een iPhone, kan die alle gevens alsnog ophalen. Oftewel niet echt veilig, dit zorgt alleen maar voor extra werk voor de 'hacker'.
Om te beginnen alles via HTTP/S c.q. SSL laten lopen, anders is de boel sowieso niet goed dichtgetimmerd. Het uitwisselen van een unieke API-key heeft namelijk geen enkele zin als die key te onderscheppen is.
Met een device-token zit je op de goede weg, maar dan herken je een device, niet de gebruiker. Ik zou daarom voor de variant gaan die Aar suggereert: headers meesturen met gebruikersnaam en wachtwoord die per gebruiker uniek zijn. Eventueel kun je de app daarmee ook laten inloggen, waarna deze tijdelijk, voor de duur van de sessie, een unieke API-key krijgt.
Gebruikersnaam/wachtwoord wil ik eerst achterwege laten in versie 1 van de applicatie, in een latere versie word er wel gebruik gemaakt van inlog gegevens (aangezien er dan een reactie systeem komt). De applicatie is namelijk voor een regionale nieuws website. Het is de bedoeling dat er een iOS applicatie komt met een API (word later uitgebreid voor Android) die eigenlijk alleen te benaderen is via de iOS applicatie.
Ik zou daarom voor de variant gaan die Aar suggereert: headers meesturen met gebruikersnaam en wachtwoord die per gebruiker uniek zijn. Eventueel kun je de app daarmee ook laten inloggen, waarna deze tijdelijk, voor de duur van de sessie, een unieke API-key krijgt.
Ik ben het met Ward eens alleen zou ik geen wachtwoord en misschien ook geen gebruikersnaam meesturen waarmee een gebruiker ook "gewoon" kan inloggen. Hiervoor zou ik een eenmalig random gegenereerd token opslaan in de database dat vervolgens als wachtwoord dient voor de api. Dit om te voorkomen dat ook de combi username/wachtwoord ontfutseld wordt van gebruikers.
Daarnaast een opmerking over het laten inloggen met gebruik van $_SESSION: Hiervoor dient de software aan de client-side wel cookies te kunnen bewaren en meesturen in de header voor de SESSIONID. Dit betekend dat de api met CURL aangeroepen moet worden en niet met de veel simpelere file_get_contents() vanuit een PHP applicatie. Een alternatief is om de gebruiker eerst te laten aanmelden met zijn eigen unieke token waarna deze een tijdelijke sleutel terug ontvangt welke hij vervolgens bij iedere nieuwe api aanroep meegeeft in de url.
eerste aanroep:
api/v1/123456890/login
antwoord:
apitoken=abcdefghij
tweede aanroep:
api/v1/abcdefghij/newsfeeds
een HTTPS verbinding is eigenlijk gewoon verplicht.