Fix signature validation (#38)

* Add failing test

* Fix signature validation

* Fix tests to generate correct signatures

*  StyleCI fix

* Ignore route params when validating the signature

* Fix tests to add route params next to signature

* StyleCI fixes
This commit is contained in:
Alex Bouma 2019-01-02 22:30:57 +01:00 committed by Marcel Pociot
parent c1f6ffa51b
commit 83dec0b5f1
4 changed files with 122 additions and 138 deletions

View File

@ -3,6 +3,7 @@
namespace BeyondCode\LaravelWebSockets\HttpApi\Controllers;
use Exception;
use Pusher\Pusher;
use Illuminate\Http\Request;
use GuzzleHttp\Psr7\Response;
use Ratchet\ConnectionInterface;
@ -84,18 +85,21 @@ abstract class Controller implements HttpServerInterface
protected function ensureValidSignature(Request $request)
{
$signature =
"{$request->getMethod()}\n/{$request->path()}\n".
"auth_key={$request->get('auth_key')}".
"&auth_timestamp={$request->get('auth_timestamp')}".
"&auth_version={$request->get('auth_version')}";
/*
* The `auth_signature` & `body_md5` parameters are not included when calculating the `auth_signature` value.
*
* The `appId`, `appKey` & `channelName` parameters are actually route paramaters and are never supplied by the client.
*/
$params = array_except($request->query(), ['auth_signature', 'body_md5', 'appId', 'appKey', 'channelName']);
if ($request->getContent() !== '') {
$bodyMd5 = md5($request->getContent());
$signature .= "&body_md5={$bodyMd5}";
$params['body_md5'] = md5($request->getContent());
}
ksort($params);
$signature = "{$request->getMethod()}\n/{$request->path()}\n".Pusher::array_implode('=', '&', $params);
$authSignature = hash_hmac('sha256', $signature, App::findById($request->get('appId'))->secret);
if ($authSignature !== $request->get('auth_signature')) {

View File

@ -2,6 +2,7 @@
namespace BeyondCode\LaravelWebSockets\Tests\HttpApi;
use Pusher\Pusher;
use GuzzleHttp\Psr7\Request;
use Illuminate\Http\JsonResponse;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
@ -19,21 +20,15 @@ class FetchChannelTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channel/my-channel';
$routeParams = [
'appId' => '1234',
'channelName' => 'my-channel',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'InvalidSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channels\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'InvalidSecret');
$request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchChannelController::class);
@ -48,21 +43,15 @@ class FetchChannelTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channel/my-channel';
$routeParams = [
'appId' => '1234',
'channelName' => 'my-channel',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channel/my-channel\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'TestSecret');
$request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchChannelController::class);
@ -87,21 +76,15 @@ class FetchChannelTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channel/invalid-channel';
$routeParams = [
'appId' => '1234',
'channelName' => 'invalid-channel',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channel/my-channel\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'TestSecret');
$request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=invalid-channel&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchChannelController::class);

View File

@ -2,6 +2,7 @@
namespace BeyondCode\LaravelWebSockets\Tests\HttpApi;
use Pusher\Pusher;
use GuzzleHttp\Psr7\Request;
use Illuminate\Http\JsonResponse;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
@ -19,21 +20,14 @@ class FetchChannelsTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channels';
$routeParams = [
'appId' => '1234',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'InvalidSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channels\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'InvalidSecret');
$request = new Request('GET', "/apps/1234/channels?appId=1234&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchChannelsController::class);
@ -49,21 +43,14 @@ class FetchChannelsTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channels';
$routeParams = [
'appId' => '1234',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channels\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'TestSecret');
$request = new Request('GET', "/apps/1234/channels?appId=1234&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchChannelsController::class);
@ -81,26 +68,59 @@ class FetchChannelsTest extends TestCase
], json_decode($response->getContent(), true));
}
/** @test */
public function it_returns_the_channel_information_for_prefix()
{
$this->joinPresenceChannel('presence-global.1');
$this->joinPresenceChannel('presence-global.1');
$this->joinPresenceChannel('presence-global.2');
$this->joinPresenceChannel('presence-notglobal.2');
$connection = new Connection();
$requestPath = '/apps/1234/channels';
$routeParams = [
'appId' => '1234',
];
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath, [
'filter_by_prefix' => 'presence-global',
]);
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchChannelsController::class);
$controller->onOpen($connection, $request);
/** @var JsonResponse $response */
$response = array_pop($connection->sentRawData);
$this->assertSame([
'channels' => [
'presence-global.1' => [
'user_count' => 2,
],
'presence-global.2' => [
'user_count' => 1,
],
],
], json_decode($response->getContent(), true));
}
/** @test */
public function it_returns_empty_object_for_no_channels_found()
{
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channels';
$routeParams = [
'appId' => '1234',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channels\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'TestSecret');
$request = new Request('GET', "/apps/1234/channels?appId=1234&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchChannelsController::class);

View File

@ -2,6 +2,7 @@
namespace BeyondCode\LaravelWebSockets\Tests\HttpApi;
use Pusher\Pusher;
use GuzzleHttp\Psr7\Request;
use BeyondCode\LaravelWebSockets\Tests\TestCase;
use BeyondCode\LaravelWebSockets\Tests\Mocks\Connection;
@ -18,21 +19,15 @@ class FetchUsersTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channel/my-channel';
$routeParams = [
'appId' => '1234',
'channelName' => 'my-channel',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'InvalidSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channels\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'InvalidSecret');
$request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchUsersController::class);
@ -49,21 +44,15 @@ class FetchUsersTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channel/my-channel/users';
$routeParams = [
'appId' => '1234',
'channelName' => 'my-channel',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channel/my-channel/users\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'TestSecret');
$request = new Request('GET', "/apps/1234/channel/my-channel/users?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchUsersController::class);
@ -80,21 +69,15 @@ class FetchUsersTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channel/invalid-channel/users';
$routeParams = [
'appId' => '1234',
'channelName' => 'invalid-channel',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channel/my-channel/users\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'TestSecret');
$request = new Request('GET', "/apps/1234/channel/my-channel/users?appId=1234&channelName=invalid-channel&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchUsersController::class);
@ -108,21 +91,15 @@ class FetchUsersTest extends TestCase
$connection = new Connection();
$auth_key = 'TestKey';
$auth_timestamp = time();
$auth_version = '1.0';
$requestPath = '/apps/1234/channel/presence-channel/users';
$routeParams = [
'appId' => '1234',
'channelName' => 'presence-channel',
];
$queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version'));
$queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath);
$signature =
"GET\n/apps/1234/channel/my-channel/users\n".
"auth_key={$auth_key}".
"&auth_timestamp={$auth_timestamp}".
"&auth_version={$auth_version}";
$auth_signature = hash_hmac('sha256', $signature, 'TestSecret');
$request = new Request('GET', "/apps/1234/channel/my-channel/users?appId=1234&channelName=presence-channel&auth_signature={$auth_signature}&{$queryParameters}");
$request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams));
$controller = app(FetchUsersController::class);