Skip to content

Commit 36b571c

Browse files
committed
feat(bruteforce): Document the new BruteForceProtection attribute
Signed-off-by: Joas Schilling <coding@schilljs.com>
1 parent 790902d commit 36b571c

1 file changed

Lines changed: 42 additions & 8 deletions

File tree

developer_manual/basics/controllers.rst

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -881,36 +881,70 @@ Nextcloud supports brute-force protection on an action basis. By default control
881881

882882
The native brute-force protection will slow down requests if too many violations have been found. This slow down will be applied to all requests against a brute-force protected controller with the same action from the affected IP.
883883

884-
To enable brute force protection the following *Annotation* can be added to the controller:
884+
To enable brute force protection the following *Attribute* can be added to the controller:
885885

886-
* **@BruteForceProtection(action=string)**: "string" is the name of the action. Such as "login" or "reset". Brute-force attempts are on a per-action basis; this means if a violation for the "login" action is triggered, other actions such as "reset" or "foobar" are not affected.
886+
* ``#[BruteForceProtection(action: 'string')]``: "string" is the name of the action. Such as "login" or "reset". Brute-force attempts are on a per-action basis; this means if a violation for the "login" action is triggered, other actions such as "reset" or "foobar" are not affected.
887887

888-
Then the **throttle()** method has to be called on the response in case of a violation. Doing so will increase the throttle counter and make following requests slower.
888+
.. note::
889+
890+
The attribute is only available in Nextcloud 27 or later. In older versions the ``@BruteForceProtection(action=string)`` annotation can be used, but that does not allow multiple assignments to a single controller method.
891+
892+
Then the **throttle()** method has to be called on the response in case of a violation. Doing so will increase the throttle counter and make following requests slower, until a slowness of roughly 30 seconds is reached and the controller returns a ``429 Too Many Requests`` status is returned instead of avoid further requests.
889893

890894
A controller method that would employ brute-force protection with an action of "foobar" would look as following:
891895

892896
.. code-block:: php
897+
:emphasize-lines: 11,18
893898
894899
<?php
895900
namespace OCA\MyApp\Controller;
896901
897902
use OCP\IRequest;
898903
use OCP\AppFramework\Controller;
904+
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
899905
use OCP\AppFramework\Http\TemplateResponse;
900906
901907
class PageController extends Controller {
902908
903-
/**
904-
* @BruteForceProtection(action=foobar)
905-
*/
906-
public function rateLimitedForAll(): TemplateResponse {
909+
#[BruteForceProtection(action: 'foobar')]
910+
public function bruteforceProtected(): TemplateResponse {
907911
$templateResponse = new TemplateResponse(…);
908912
// In case of a violation increase the throttle counter
909913
// note that $this->auth->isSuccessful here is just an
910914
// example.
911-
if(!$this->auth->isSuccessful()) {
915+
if (!$this->auth->isSuccessful()) {
912916
$templateResponse->throttle();
913917
}
914918
return $templateResponse;
915919
}
916920
}
921+
922+
A controller can also have multiple factors to brute force against. In this case you can specify multiple attributes and then in the throttle you specify the action which was violated. This is especially useful when a secret, in the sample below token, could be guessed on multiple endpoints e.g. a share token on the API level, preview endpoint, frontend controller, etc. while another secret (password), is specific to this one controller method.
923+
924+
.. code-block:: php
925+
:emphasize-lines: 11-12,16,20
926+
927+
<?php
928+
namespace OCA\MyApp\Controller;
929+
930+
use OCP\IRequest;
931+
use OCP\AppFramework\Controller;
932+
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
933+
use OCP\AppFramework\Http\TemplateResponse;
934+
935+
class PageController extends Controller {
936+
937+
#[BruteForceProtection(action: 'token')]
938+
#[BruteForceProtection(action: 'password')]
939+
public function getPasswordProtectedShare(string $token, string $password): TemplateResponse {
940+
$templateResponse = new TemplateResponse(…);
941+
if (!$this->shareManager->getByToken($token)) {
942+
$templateResponse->throttle(['action' => 'token']);
943+
}
944+
// …
945+
if (!$share->verifyPassword($password)) {
946+
$templateResponse->throttle(['action' => 'password']);
947+
}
948+
return $templateResponse;
949+
}
950+
}

0 commit comments

Comments
 (0)