Sécurisez votre API REST avec PHP et Yii 2
Article par Anthony Lavallée
Diplômé de la Technique informatique du Cégep de Sainte-Foy. – See more at: http://atomrace.com/blogue/techno-web/developpement-web/2015/04/creation-dun-api-restful-avec-php-et-yii-2-sur-ubuntu#sthash.Z1mJzsM1.dpuf
Article par Anthony Lavallée
Diplômé de la Technique informatique du Cégep de Sainte-Foy.
Avant de sécuriser votre API, je vous propose de suivre le lien suivant qui explique comment mettre en place un API REST avec PHP et Yii2 sur Ubuntu.
Les méthodes d’authentification
Dans un serveur REST, comme il est dans l’habitude de ne pas utiliser de session ou de «cookies», chaque requête doit être envoyée avec une manière d’identifier l’utilisateur. Il existe plusieurs méthodes d’authentification, par exemple:
- HttpBasicAuth, où un «access token» est envoyé comme nom d’utilisateur dans l’en-tête de la requête HTTP.
- QueryParamAuth, où un «access token» est envoyé comme une chaîne de caractères dans l’URL.
- OAuth2, où un «access token» est obtenu par une autorisation du serveur. Google, Facebook et Twitter utilisent cette méthode.
On peut débuter par aller configurer le fichier config/web.php et définir la variable enableSession à false, afin que le statut d’authentification de l’utilisateur ne soit pas conservé lors des requêtes de session. Cette méthode est optionnelle, mais puisque nous travaillons avec un serveur REST, il est recommandé d’adhérer à cette pratique.
'user' => [ 'enableSession' => false, 'loginUrl' => null, ],
Méthode d’authentification HttpBasicAuth
L’ «access token» est envoyé comme le nom d’utilisateur. À la place d’une session ou de cookies, HttpBasicAuth utilise un BasicAuth Header, qui est envoyé à chaque requête, afin de confirmer l’identité de l’utilisateur.
Dans le fichier controllers/CountryController.php, aller placer les lignes de codes suivantes, afin de permettre à HttpBasicAuth de s’opérer.
'authenticator' => [ 'class' => HttpBasicAuth::className(), 'auth' => [$this, 'auth'] ],
public function auth($username, $password) { return \app\models\User::findOne ([ 'username' => $username, 'password' => $password, ]); }
Ensuite, on peut tester la requête à l’aide de la méthode cURL.
En effectuant une requête avec un utilisateur ayant les informations suivantes:
username: admin
password: admin
La requête cURL sera donc:
$ curl --user admin:admin http://monapp.com/country/index
Si un nom d’utilisateur et un mot passe valides sont utilisés, voici la réponse JSON retournée.
[ {"code":"AU","name":"Australia","population":1000}, {"code":"BR","name":"Brazil","population":170115000}, {"code":"CA","name":"Canada","population":1147000}, {"code":"CN","name":"China","population":1277558000}, {"code":"DE","name":"Germany","population":82164700}, {"code":"FR","name":"France","population":59225700}, {"code":"GB","name":"United Kingdom","population":59623400}, {"code":"IN","name":"India","population":1013662000}, {"code":"RU","name":"Russia","population":146934000}, {"code":"US","name":"United States","population":278357000} ]
Si on entre un utilisateur ou un mot de passe invalide, la requête retourne un message d’erreur, expliquant que l’on ne dispose pas des droits nécessaires pour effectuer cette requête. Par exemple, si j’entre «test» comme mot de passe, alors que le véritable mot de passe est «admin», j’obtiens la réponse ci-dessous.
$ curl --user admin:test http://monapp.com/country/index
{ "name":"Unauthorized", "message":"You are requesting with an invalid credential.", "code":0, "status":401, "type":"yii\\web\\UnauthorizedHttpException" }
Méthode d’authentification QueryPathAuth
L’«access token» est envoyé comme une chaîne de caractères dans l’URL.
https://example.com/users?access-token=xxxxxxxx
Afin de permettre ce type d’authentification, il faut aller définir la classe dans l’espace « authenticator » de nos « behaviors », dans notre contrôleur.
'authenticator' => [ 'class' => QueryParamAuth::className(), ],
Ensuite, on peut tester la requête à l’aide de la méthode cURL, en effectuant une requête avec un utilisateur ayant les informations suivantes:
access-token = 101-token
La requête cURL sera donc:
$ curl --i "http://monapp.com/country/index?access-token=101-token"
Si on utilise un jeton valide, la requête retourne un message de confirmation.
HTTP/1.1 200 OK Date: Fri, 27 Mar 2015 15:32:15 GMT Server: Apache/2.4.6 (Ubuntu) X-Powered-By: PHP/5.5.3-1ubuntu2.6 Set-Cookie: PHPSESSID=f8oafhps; HttpOnly Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache X-Pagination-Total-Count: 10 X-Pagination-Page-Count: 1 X-Pagination-Current-Page: 1 X-Pagination-Per-Page: 20 Link: ; rel=self Set-Cookie: PHPSESSID=f8o4s47uvmphpsnk7e13; path=/; HttpOnly Content-Length: 1860 Content-Type: application/json; charset=UTF-8 [ {"code":"AU","name":"Australia","population":1000}, {"code":"BR","name":"Brazil","population":170115000}, {"code":"CA","name":"Canada","population":1147000}, {"code":"RU","name":"Russia","population":146934000}, {"code":"US","name":"United States","population":278357000} ]
Si on entre un jeton invalide, la requête retourne un message d’erreur, expliquant que l’on ne dispose pas des droits nécessaires pour effectuer cette requête. Par exemple, si on entre «test» comme access-token, alors que le véritable access-token de l’utilisateur admin est «101-token», on obtient la réponse ci-dessous.
$ curl --i "http://monapp.com/country/index?access-token=test"
HTTP/1.1 401 Unauthorized Date: Fri, 27 Mar 2015 15:39:49 GMT Server: Apache/2.4.6 (Ubuntu) X-Powered-By: PHP/5.5.3-1ubuntu2.6 Content-Length: 149 Content-Type: application/json; charset=UTF-8 { "name":"Unauthorized", "message":"You are requesting with an invalid credential.", "code":0,"status":401, "type":"yii\\web\\UnauthorizedHttpException" }
Tests unitaires avec Guzzle
Guzzle est un client PHP HTTP qui rend l’envoi de requête simple et rapide. On peut donc aussi s’en servir afin de créer divers tests unitaires pour son serveur REST et ainsi tester nos requêtes, pour voir si elles fonctionnent correctement.
class GuzzleTest extends PHPUnit_Framework_TestCase { private $apiURL = 'http://monapp.com/'; public function test_ConnectionCountryIndex() { $client = new Client(); $response = $client->get('http://monapp.com/country/index'); $code = $response->getStatusCode(); $this->assertTrue($code === 200); } /** @depends test_ConnectionCountryIndex */ public function testConnectionSiteAbout() { $this->client = new GuzzleHttp\Client( ['base_url' => $this->apiURL], array()); $response = $this->client->get('site/about'); $code = $response->getStatusCode(); $this->assertTrue($code === 200); } }
On démarre les tests en utilisant la commande
$ phpunit BasicAuthTest.php
[sharethis]
J’obtiens les résultats suivants, qui m’expliquent que les tests ont tous passé.
PHPUNIT 3.6.10 by Sebastian Bergmann. .. Time: 0 seconds, Memory: 2.25 Mb OK (2 tests, 2 assertions)
Si on change le code attendu du premier test: $this->assertTrue($code === 201);
On obtient les résultats suivants, qui expliquent que certains tests ont échoué. Grâce à cette interface, on peut savoir combien de tests ont échoué, lesquels et pourquoi.
PHPUnit 3.6.10 by Sebastian Bergmann. Time: 0 seconds, Memory: 2.25Mb There was 1 failure: 1) BasicAuthTest::testConnectionCountryIndex Failed asserting that false is true. /var/www/basic/tests/BasicAuthTest.php:18 FAILURES! Tests: 1, Assertions: 1, Failures: 1, Skipped: 1.
Si vous avez aimé le l’article d’Anthony, n’hésitez pas à le partager!
[sharethis]
Références
- http://en.wikipedia.org/wiki/Basic_access_authentication
- http://www.yiiframework.com/doc-2.0/guide-rest-authentication.html
- http://guzzle.readthedocs.org/en/latest/
Répondre
Want to join the discussion?Feel free to contribute!