5 tipů pro lepší zabezpečení API v PHP

PHP

POZOR! Článek jsem napsal před více jak rokem, a tudíž už nemusí reflektovat můj nynější názor nebo může být zastaralý.

API je část aplikace, která si víc než jiná zaslouží větší pozornost na zabezpečení. V tomhle malém článku uvedu 5 tipů, které možná už znáte, ale možná taky ne a i kdyby, tak proč si je nepřipomenout :)

1. Používejte SSL

Dnes už je skoro samozřejmostí, že celé weby jsou pod HTTPS a pro API je to bezpodmínečně nutné. Koukněte jestli máte správně nastavená přesměrování z nezabezpečeného HTTP.

2. Povolte pouze ty HTTP hlavičky, která chcete

Prvně upozorním, že respektování CORS HTTP hlaviček je záležitost prohlížeče. Pokud použijete například cUrl v konzoli, tak lze podstrčit jakoukoli hlavičku s vlastním obsahem. Proto se nejedná o absolutní ochranu.

Nicméně i tak je pro prohlížeč dobré uvést hlavičky, které umožňujete přijmout. Stačí uvádět pouze ty hlavičky, které nespadají mezi ty základní (Accept, Accept-Language, Content-Language, Content-Type). To se hodí pro hlavičku ve které posíláte autorizační token.

header("Access-Control-Allow-Headers: Authorization");

3. Povolte pouze ty URL, které mohou k API přistupovat

Zde platí to samé jako pro bod 2. I zde má ale smysl povolit pouze tu URL, kterou pro kterou API provozujete, aby vaše API nebylo zneužité parazitující stránkou.

header("Access-Control-Allow-Origin: https://vasweb.cz");

Pokud API provozujete pro více než jeden web, je nutné už více řádků:

$httpOrigin = $_SERVER['HTTP_ORIGIN'];

if ($httpOrigin == "https://vasweb.cz" || $httpOrigin == "https://jinyvasweb.cz") {  
    header("Access-Control-Allow-Origin: " . $httpOrigin);
}

4. Komunikujte s API pomocí tokenu s časovou platností

Už jsem párkrát viděl API, které měla token u něhož se nijak neověřovalo, jestli není třeba 2 roky starý. Proč je to důležité? Může se stát, že dojde k jeho krádeži a je jedno, jestli ho ukládáte do cookies, localStorage nebo sessionStorage. Pokud se token nemění a nemá časovou platnost, je pro zloděje stálou možností, jak se pro API autorizovat.

Osobně mi vyhovuje standard JWT, kde do jednoho tokenu uložíte informace o ID uživateli (například, ale nesmí jít o citlivá data, protože jsou pouze zakódována v base64), časovou platnost a celé to podepíšete vlastním tajným klíčem. Takže pokud ho někdo nezná, můžete si být jistí tím, co je obsahem tokenu.

Například pomocí knihovny Firebase/JWT:

define("JWT_SERCRET", "takný klíč");
$data = [
    "userId": 12,
    "exp": 1356999524 // Čas expirace
];
$jwt = \Firebase\JWT\JWT::encode($id, JWT_SERCRET, 'HS512');
// $jwt = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyLCJleHAiOjEzNTY5OTk1MjR9.ZT70Ok3YZn3osdnM6uGvgzDTWS1Mknvj2EJb7Ar7jFwkSqpKrqSK3Lrz3MzxaWWvSczX-TugYwH4FGWYG9EzOQ"
$data = \Firebase\JWT\JWT::decode($jwt, JWT_SERCRET, ['HS512']);

5. Validujte a sanitizujte vstupní data

Nikdy nevěřte lidem, kteří používají vaše API.

Každý vstup (i když ho validujete už v JavaScriptu) je nutný zvalidovat (říct si, jestli odpovídá očekávanému formátu) a sanitizovat (odstranit od nechtěných symbolů).

$email = "jaroslav(.)@le/xa.com";

if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
  echo "Adresa je validní";
} else {
  echo "Adresa není validní";
}

$email = "jaroslav(.)@le/xa.com";
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
echo $email; // jaroslav.@lexa.com

Užitečné odkazy

https://developer.mozilla.org/en-US/docs/Web/HTTP/Server-Side_Access_Control
https://dzone.com/refcardz/rest-api-security-1
https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
http://php.net/manual/en/filter.filters.validate.php
http://php.net/manual/en/filter.filters.sanitize.php

Znáte někoho, komu by článek mohl pomoct? Zasdílejte mu ho :)

Komentáře