Page MenuHomePhabricator

final class AphrontWriteGuard
Phabricator Technical Documentation (Aphront)

Guard writes against CSRF. The Aphront structure takes care of most of this for you, you just need to call:

AphrontWriteGuard::willWrite();

...before executing a write against any new kind of storage engine. MySQL databases and the default file storage engines are already covered, but if you introduce new types of datastores make sure their writes are guarded. If you don't guard writes and make a mistake doing CSRF checks in a controller, a CSRF vulnerability can escape undetected.

If you need to execute writes on a page which doesn't have CSRF tokens (for example, because you need to do logging), you can temporarily disable the write guard by calling:

AphrontWriteGuard::beginUnguardedWrites();
do_logging_write();
AphrontWriteGuard::endUnguardedWrites();

This is dangerous, because it disables the backup layer of CSRF protection this class provides. You should need this only very, very rarely.

Tasks

Protecting Writes

  • public static function willWrite() — Declare intention to perform a write, validating that writes are allowed. You should call this method before executing a write whenever you implement a new storage engine where information can be permanently kept.

Disabling Protection

  • public static function beginScopedUnguardedWrites() — Enter a scope which permits unguarded writes. This works like @{method:beginUnguardedWrites} but returns an object which will end the unguarded write scope when its __destruct() method is called. This is useful to more easily handle exceptions correctly in unguarded write blocks:
  • public static function beginUnguardedWrites() — Begin a block which permits unguarded writes. You should use this very sparingly, and only for things like logging where CSRF is not a concern.
  • public static function endUnguardedWrites() — Declare that you have finished performing unguarded writes. You must call this exactly once for each call to @{method:beginUnguardedWrites}.
  • public static function allowDangerousUnguardedWrites($allow) — Allow execution of unguarded writes. This is ONLY appropriate for use in script contexts or other contexts where you are guaranteed to never be vulnerable to CSRF concerns. Calling this method is EXTREMELY DANGEROUS if you do not understand the consequences.

Managing Write Guards

  • public function __construct($callback) — Construct a new write guard for a request. Only one write guard may be active at a time. You must explicitly call @{method:dispose} when you are done with a write guard:
  • public function dispose() — Dispose of the active write guard. You must call this method when you are done with a write guard. You do not normally need to call this yourself.
  • public static function isGuardActive() — Determine if there is an active write guard.

Internals

No methods for this task.

Other Methods

  • public static function getInstance() — Return on instance of AphrontWriteGuard if it's active, or null

Methods

public function __construct($callback)

Construct a new write guard for a request. Only one write guard may be active at a time. You must explicitly call dispose() when you are done with a write guard:

$guard = new AphrontWriteGuard($callback);
// ...
$guard->dispose();

Normally, you do not need to manage guards yourself -- the Aphront stack handles it for you.

This class accepts a callback, which will be invoked when a write is attempted. The callback should validate the presence of a CSRF token in the request, or abort the request (e.g., by throwing an exception) if a valid token isn't present.

Parameters
callable$callbackCSRF callback.
Return
this//Implicit.//

public function dispose()

Dispose of the active write guard. You must call this method when you are done with a write guard. You do not normally need to call this yourself.

Return
void

public static function isGuardActive()

Determine if there is an active write guard.

Return
bool

public static function getInstance()

Return on instance of AphrontWriteGuard if it's active, or null

Return
AphrontWriteGuard|null

public static function willWrite()

Declare intention to perform a write, validating that writes are allowed. You should call this method before executing a write whenever you implement a new storage engine where information can be permanently kept.

Writes are permitted if:

If none of these conditions are true, this method will throw and prevent the write.

Return
void

public static function beginScopedUnguardedWrites()

Enter a scope which permits unguarded writes. This works like beginUnguardedWrites() but returns an object which will end the unguarded write scope when its __destruct() method is called. This is useful to more easily handle exceptions correctly in unguarded write blocks:

// Restores the guard even if do_logging() throws.
function unguarded_scope() {
  $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
  do_logging();
}
Return
AphrontScopedUnguardedWriteCapabilityObject which ends unguarded writes when it leaves scope.

public static function beginUnguardedWrites()

Begin a block which permits unguarded writes. You should use this very sparingly, and only for things like logging where CSRF is not a concern.

You must pair every call to beginUnguardedWrites() with a call to endUnguardedWrites():

AphrontWriteGuard::beginUnguardedWrites();
do_logging();
AphrontWriteGuard::endUnguardedWrites();
Return
void

public static function endUnguardedWrites()

Declare that you have finished performing unguarded writes. You must call this exactly once for each call to beginUnguardedWrites().

Return
void

public static function allowDangerousUnguardedWrites($allow)

Allow execution of unguarded writes. This is ONLY appropriate for use in script contexts or other contexts where you are guaranteed to never be vulnerable to CSRF concerns. Calling this method is EXTREMELY DANGEROUS if you do not understand the consequences.

If you need to perform unguarded writes on an otherwise guarded workflow which is vulnerable to CSRF, use beginUnguardedWrites().

Parameters
$allow
Return
void