Skip to content

Commit 1927591

Browse files
committed
add bruteforce protection on APIgetPositionsPublic, APIgetLastPositionsPublic and APIgetLastPositionsPublic
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
1 parent eeb2590 commit 1927591

3 files changed

Lines changed: 138 additions & 130 deletions

File tree

appinfo/routes.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@
5151
['name' => 'oldPage#updatePoint', 'url' => '/updatePoint', 'verb' => 'POST'],
5252
['name' => 'oldPage#getSessions', 'url' => '/getSessions', 'verb' => 'POST'],
5353
['name' => 'oldPage#APIgetSessions', 'url' => '/api/getsessions', 'verb' => 'GET'],
54-
['name' => 'oldPage#APIgetLastPositionsPublic', 'url' => '/api/getlastpositions/{sessionId}', 'verb' => 'GET'],
55-
['name' => 'oldPage#APIgetLastDevicePositionPublic', 'url' => '/api/getlastpositions/{sessionId}/{deviceName}', 'verb' => 'GET'],
56-
['name' => 'oldPage#APIgetPositionsPublic', 'url' => '/api/getpositions/{sessionid}', 'verb' => 'GET'],
54+
['name' => 'oldPage#APIgetLastPositionsPublic', 'url' => '/api/getlastpositions/{token}', 'verb' => 'GET'],
55+
['name' => 'oldPage#APIgetLastDevicePositionPublic', 'url' => '/api/getlastpositions/{token}/{deviceName}', 'verb' => 'GET'],
56+
['name' => 'oldPage#APIgetPositionsPublic', 'url' => '/api/getpositions/{token}', 'verb' => 'GET'],
5757
['name' => 'oldPage#APIgetLastPositionsUser', 'url' => '/api/getuserlastpositions/{sessionid}', 'verb' => 'GET'],
5858
['name' => 'oldPage#APIgetPositionsUser', 'url' => '/api/getuserpositions/{sessionid}', 'verb' => 'GET'],
5959
['name' => 'oldPage#APIshareDevice', 'url' => '/api/sharedevice/{sessionid}/{devicename}', 'verb' => 'GET'],

lib/Controller/OldPageController.php

Lines changed: 133 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -3566,28 +3566,30 @@ private function logMultiple(string $token, string $devicename, array $points) {
35663566

35673567
#[NoAdminRequired]
35683568
#[NoCSRFRequired]
3569-
public function APIPing() {
3569+
public function APIPing(): DataResponse {
35703570
return new DataResponse([$this->userId]);
35713571
}
35723572

35733573
#[NoAdminRequired]
35743574
#[PublicPage]
35753575
#[NoCSRFRequired]
3576-
public function APIgetLastDevicePositionPublic(string $sessionId, ?string $deviceName = null) {
3577-
return $this->APIgetLastPositionsPublic($sessionId, $deviceName);
3576+
#[BruteForceProtection(action: 'PhonetrackAPIGetLastDevicePositionPublic')]
3577+
public function APIgetLastDevicePositionPublic(string $token, ?string $deviceName = null): DataResponse {
3578+
return $this->APIgetLastPositionsPublic($token, $deviceName);
35783579
}
35793580

35803581
#[NoAdminRequired]
35813582
#[PublicPage]
35823583
#[NoCSRFRequired]
3583-
public function APIgetLastPositionsPublic(string $sessionId, ?string $deviceName = null) {
3584+
#[BruteForceProtection(action: 'PhonetrackAPIGetLastPositionsPublic')]
3585+
public function APIgetLastPositionsPublic(string $token, ?string $deviceName = null): DataResponse {
35843586
$result = [];
35853587
// check if session exists
35863588
$dbToken = null;
35873589
$sqlGet = '
35883590
SELECT publicviewtoken, token
35893591
FROM *PREFIX*phonetrack_sessions
3590-
WHERE publicviewtoken=' . $this->db_quote_escape_string($sessionId) . '
3592+
WHERE publicviewtoken=' . $this->db_quote_escape_string($token) . '
35913593
AND public=1 ;';
35923594
$req = $this->dbConnection->prepare($sqlGet);
35933595
$res = $req->execute();
@@ -3597,55 +3599,59 @@ public function APIgetLastPositionsPublic(string $sessionId, ?string $deviceName
35973599
}
35983600
$res->closeCursor();
35993601

3600-
// session exists
3601-
if ($dbToken !== null) {
3602-
// get list of devices
3603-
$devices = [];
3604-
$sqlDev = '
3605-
SELECT name, id
3606-
FROM *PREFIX*phonetrack_devices
3607-
WHERE session_token=' . $this->db_quote_escape_string($dbToken);
3608-
if ($deviceName !== null) {
3609-
$sqlDev .= ' AND name=' . $this->db_quote_escape_string($deviceName);
3610-
}
3611-
$sqlDev .= ' ;';
3612-
$req = $this->dbConnection->prepare($sqlDev);
3602+
// session does not exist
3603+
if ($dbToken === null) {
3604+
$response = new DataResponse(['error' => 'Session not found']);
3605+
$response->setStatus(Http::STATUS_NOT_FOUND);
3606+
$response->throttle(['reason' => 'session not found']);
3607+
return $response;
3608+
}
3609+
// get list of devices
3610+
$devices = [];
3611+
$sqlDev = '
3612+
SELECT name, id
3613+
FROM *PREFIX*phonetrack_devices
3614+
WHERE session_token=' . $this->db_quote_escape_string($dbToken);
3615+
if ($deviceName !== null) {
3616+
$sqlDev .= ' AND name=' . $this->db_quote_escape_string($deviceName);
3617+
}
3618+
$sqlDev .= ' ;';
3619+
$req = $this->dbConnection->prepare($sqlDev);
3620+
$res = $req->execute();
3621+
while ($row = $res->fetch()) {
3622+
$devices[] = [
3623+
'id' => $row['id'],
3624+
'name' => $row['name'],
3625+
];
3626+
}
3627+
$res->closeCursor();
3628+
3629+
// get the coords for each device
3630+
$result[$dbPubToken] = [];
3631+
3632+
foreach ($devices as $device) {
3633+
$deviceName = $device['name'];
3634+
$deviceId = $device['id'];
3635+
3636+
$entry = [];
3637+
$sqlGet = '
3638+
SELECT lat, lon, timestamp, batterylevel, useragent,
3639+
satellites, accuracy, altitude, speed, bearing
3640+
FROM *PREFIX*phonetrack_points
3641+
WHERE deviceid=' . $this->db_quote_escape_string($deviceId) . '
3642+
ORDER BY timestamp DESC LIMIT 1 ;';
3643+
$req = $this->dbConnection->prepare($sqlGet);
36133644
$res = $req->execute();
36143645
while ($row = $res->fetch()) {
3615-
$devices[] = [
3616-
'id' => $row['id'],
3617-
'name' => $row['name'],
3618-
];
3646+
$entry['useragent'] = $row['useragent'];
3647+
unset($row['useragent']);
3648+
foreach ($row as $k => $v) {
3649+
$entry[$k] = is_numeric($v) ? floatval($v) : null;
3650+
}
36193651
}
36203652
$res->closeCursor();
3621-
3622-
// get the coords for each device
3623-
$result[$dbPubToken] = [];
3624-
3625-
foreach ($devices as $device) {
3626-
$deviceName = $device['name'];
3627-
$deviceId = $device['id'];
3628-
3629-
$entry = [];
3630-
$sqlGet = '
3631-
SELECT lat, lon, timestamp, batterylevel, useragent,
3632-
satellites, accuracy, altitude, speed, bearing
3633-
FROM *PREFIX*phonetrack_points
3634-
WHERE deviceid=' . $this->db_quote_escape_string($deviceId) . '
3635-
ORDER BY timestamp DESC LIMIT 1 ;';
3636-
$req = $this->dbConnection->prepare($sqlGet);
3637-
$res = $req->execute();
3638-
while ($row = $res->fetch()) {
3639-
$entry['useragent'] = $row['useragent'];
3640-
unset($row['useragent']);
3641-
foreach ($row as $k => $v) {
3642-
$entry[$k] = is_numeric($v) ? floatval($v) : null;
3643-
}
3644-
}
3645-
$res->closeCursor();
3646-
if (count($entry) > 0) {
3647-
$result[$dbPubToken][$deviceName] = $entry;
3648-
}
3653+
if (count($entry) > 0) {
3654+
$result[$dbPubToken][$deviceName] = $entry;
36493655
}
36503656
}
36513657
return new DataResponse($result);
@@ -3654,19 +3660,20 @@ public function APIgetLastPositionsPublic(string $sessionId, ?string $deviceName
36543660
#[NoAdminRequired]
36553661
#[PublicPage]
36563662
#[NoCSRFRequired]
3657-
public function APIgetPositionsPublic($sessionid, $limit = null) {
3663+
#[BruteForceProtection(action: 'PhonetrackAPIGetLastPositionsPublic')]
3664+
public function APIgetPositionsPublic(string $token, ?int $limit = null): DataResponse {
36583665
$result = [];
36593666
// check if session exists
3660-
$dbtoken = null;
3667+
$dbToken = null;
36613668
$sqlget = '
36623669
SELECT publicviewtoken, token
36633670
FROM *PREFIX*phonetrack_sessions
3664-
WHERE publicviewtoken=' . $this->db_quote_escape_string($sessionid) . '
3671+
WHERE publicviewtoken=' . $this->db_quote_escape_string($token) . '
36653672
AND public=1 ;';
36663673
$req = $this->dbConnection->prepare($sqlget);
36673674
$res = $req->execute();
36683675
while ($row = $res->fetch()) {
3669-
$dbtoken = $row['token'];
3676+
$dbToken = $row['token'];
36703677
$dbpubtoken = $row['publicviewtoken'];
36713678
}
36723679
$res->closeCursor();
@@ -3675,15 +3682,15 @@ public function APIgetPositionsPublic($sessionid, $limit = null) {
36753682
$dbDevicename = null;
36763683
$dbLastPosOnly = null;
36773684
$dbGeofencify = null;
3678-
if ($dbtoken === null) {
3685+
if ($dbToken === null) {
36793686
$sqlget = '
36803687
SELECT session_token, sharetoken, filters, devicename, lastposonly, geofencify
36813688
FROM *PREFIX*phonetrack_pubshares
3682-
WHERE sharetoken=' . $this->db_quote_escape_string($sessionid) . ' ;';
3689+
WHERE sharetoken=' . $this->db_quote_escape_string($token) . ' ;';
36833690
$req = $this->dbConnection->prepare($sqlget);
36843691
$res = $req->execute();
36853692
while ($row = $res->fetch()) {
3686-
$dbtoken = $row['session_token'];
3693+
$dbToken = $row['session_token'];
36873694
$dbpubtoken = $row['sharetoken'];
36883695
$dbFilters = json_decode($row['filters'], true);
36893696
$dbDevicename = $row['devicename'];
@@ -3693,80 +3700,84 @@ public function APIgetPositionsPublic($sessionid, $limit = null) {
36933700
$res->closeCursor();
36943701
}
36953702

3696-
// session exists
3697-
if ($dbtoken !== null) {
3698-
// get list of devices
3699-
$devices = [];
3703+
// session does not exist
3704+
if ($dbToken === null) {
3705+
$response = new DataResponse(['error' => 'Session not found']);
3706+
$response->setStatus(Http::STATUS_NOT_FOUND);
3707+
$response->throttle(['reason' => 'session not found']);
3708+
return $response;
3709+
}
3710+
// get list of devices
3711+
$devices = [];
37003712

3701-
$deviceNameRestriction = '';
3702-
if ($dbDevicename !== null && $dbDevicename !== '') {
3703-
$deviceNameRestriction = ' AND name=' . $this->db_quote_escape_string($dbDevicename) . ' ';
3704-
}
3705-
$sqldev = '
3706-
SELECT id
3713+
$deviceNameRestriction = '';
3714+
if ($dbDevicename !== null && $dbDevicename !== '') {
3715+
$deviceNameRestriction = ' AND name=' . $this->db_quote_escape_string($dbDevicename) . ' ';
3716+
}
3717+
$sqldev = '
3718+
SELECT id
3719+
FROM *PREFIX*phonetrack_devices
3720+
WHERE session_token=' . $this->db_quote_escape_string($dbToken) . '
3721+
' . $deviceNameRestriction . ' ;';
3722+
$req = $this->dbConnection->prepare($sqldev);
3723+
$res = $req->execute();
3724+
while ($row = $res->fetch()) {
3725+
$devices[] = $row['id'];
3726+
}
3727+
$res->closeCursor();
3728+
3729+
// get the coords for each device
3730+
$result[$dbpubtoken] = [];
3731+
3732+
foreach ($devices as $devid) {
3733+
$name = null;
3734+
$color = null;
3735+
$sqlname = '
3736+
SELECT name, color
37073737
FROM *PREFIX*phonetrack_devices
3708-
WHERE session_token=' . $this->db_quote_escape_string($dbtoken) . '
3709-
' . $deviceNameRestriction . ' ;';
3710-
$req = $this->dbConnection->prepare($sqldev);
3738+
WHERE session_token=' . $this->db_quote_escape_string($dbToken) . '
3739+
AND id=' . $this->db_quote_escape_string($devid) . ' ;';
3740+
$req = $this->dbConnection->prepare($sqlname);
37113741
$res = $req->execute();
3742+
$col = '';
37123743
while ($row = $res->fetch()) {
3713-
$devices[] = $row['id'];
3744+
$name = $row['name'];
3745+
$color = $row['color'];
37143746
}
37153747
$res->closeCursor();
37163748

3717-
// get the coords for each device
3718-
$result[$dbpubtoken] = [];
3719-
3720-
foreach ($devices as $devid) {
3721-
$name = null;
3722-
$color = null;
3723-
$sqlname = '
3724-
SELECT name, color
3725-
FROM *PREFIX*phonetrack_devices
3726-
WHERE session_token=' . $this->db_quote_escape_string($dbtoken) . '
3727-
AND id=' . $this->db_quote_escape_string($devid) . ' ;';
3728-
$req = $this->dbConnection->prepare($sqlname);
3729-
$res = $req->execute();
3730-
$col = '';
3731-
while ($row = $res->fetch()) {
3732-
$name = $row['name'];
3733-
$color = $row['color'];
3734-
}
3735-
$res->closeCursor();
3736-
3737-
$entries = [];
3738-
$sqlLimit = '';
3739-
if (intval($dbLastPosOnly) === 1) {
3740-
$sqlLimit = 'LIMIT 1';
3741-
} elseif (is_numeric($limit)) {
3742-
$sqlLimit = 'LIMIT ' . intval($limit);
3743-
}
3744-
$sqlget = '
3745-
SELECT lat, lon, timestamp, batterylevel, useragent,
3746-
satellites, accuracy, altitude, speed, bearing
3747-
FROM *PREFIX*phonetrack_points
3748-
WHERE deviceid=' . $this->db_quote_escape_string($devid) . '
3749-
ORDER BY timestamp DESC ' . $sqlLimit . ' ;';
3750-
$req = $this->dbConnection->prepare($sqlget);
3751-
$res = $req->execute();
3752-
while ($row = $res->fetch()) {
3753-
if ($dbFilters === null || $this->filterPoint($row, $dbFilters)) {
3754-
$entry = [];
3755-
$entry['useragent'] = $row['useragent'];
3756-
unset($row['useragent']);
3757-
foreach ($row as $k => $v) {
3758-
$entry[$k] = is_numeric($v) ? floatval($v) : null;
3759-
}
3760-
array_unshift($entries, $entry);
3749+
$entries = [];
3750+
$sqlLimit = '';
3751+
if (intval($dbLastPosOnly) === 1) {
3752+
$sqlLimit = 'LIMIT 1';
3753+
} elseif (is_numeric($limit)) {
3754+
$sqlLimit = 'LIMIT ' . intval($limit);
3755+
}
3756+
$sqlget = '
3757+
SELECT lat, lon, timestamp, batterylevel, useragent,
3758+
satellites, accuracy, altitude, speed, bearing
3759+
FROM *PREFIX*phonetrack_points
3760+
WHERE deviceid=' . $this->db_quote_escape_string($devid) . '
3761+
ORDER BY timestamp DESC ' . $sqlLimit . ' ;';
3762+
$req = $this->dbConnection->prepare($sqlget);
3763+
$res = $req->execute();
3764+
while ($row = $res->fetch()) {
3765+
if ($dbFilters === null || $this->filterPoint($row, $dbFilters)) {
3766+
$entry = [];
3767+
$entry['useragent'] = $row['useragent'];
3768+
unset($row['useragent']);
3769+
foreach ($row as $k => $v) {
3770+
$entry[$k] = is_numeric($v) ? floatval($v) : null;
37613771
}
3772+
array_unshift($entries, $entry);
37623773
}
3763-
$res->closeCursor();
3764-
if (count($entries) > 0) {
3765-
$result[$dbpubtoken][$name] = [
3766-
'color' => $color,
3767-
'points' => $entries
3768-
];
3769-
}
3774+
}
3775+
$res->closeCursor();
3776+
if (count($entries) > 0) {
3777+
$result[$dbpubtoken][$name] = [
3778+
'color' => $color,
3779+
'points' => $entries
3780+
];
37703781
}
37713782
}
37723783
return new DataResponse($result);

tests/php/controller/OldPageControllerTest.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,8 +1497,7 @@ public function testPage() {
14971497
// API
14981498
$resp = $this->pageController->APIgetLastPositionsPublic($sharetoken2);
14991499
$data = $resp->getData();
1500-
1501-
$this->assertEquals((count($data[$sharetoken2]) > 0), true);
1500+
$this->assertGreaterThan(0, count($data[$sharetoken2]));
15021501
$this->assertEquals($timestamp, $data[$sharetoken2]['renamedTestDev']['timestamp']);
15031502

15041503
// SET SESSION PRIVATE
@@ -1525,9 +1524,7 @@ public function testPage() {
15251524

15261525
// API
15271526
$resp = $this->pageController->APIgetLastPositionsPublic($sharetoken2);
1528-
$data = $resp->getData();
1529-
1530-
$this->assertEquals((count($data) === 0), true);
1527+
$this->assertEquals(Http::STATUS_NOT_FOUND, $resp->getStatus());
15311528

15321529
// ADD PUBLIC SHARE
15331530
$resp = $this->pageController->addPublicShare($token2 . 'a');

0 commit comments

Comments
 (0)