@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Tune cookie behaviors for 'phcid', 'phreg', etc

Summary:
Fixes T3471. Specific issues:

- Add the ability to set a temporary cookie (expires when the browser closes).
- We overwrote 'phcid' on every page load. This creates some issues with browser extensions. Instead, only write it if isn't set. To counterbalance this, make it temporary.
- Make the 'next_uri' cookie temporary.
- Make the 'phreg' cookie temporary.
- Fix an issue where deleted cookies would persist after 302 (?) in some cases (this is/was 100% for me locally).

Test Plan:
- Closed my browser, reopned it, verified temporary cookies were gone.
- Logged in, authed, linked, logged out.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T3471

Differential Revision: https://secure.phabricator.com/D8537

+95 -17
+61 -7
src/aphront/AphrontRequest.php
··· 287 287 288 288 final public function getCookie($name, $default = null) { 289 289 $name = $this->getPrefixedCookieName($name); 290 - return idx($_COOKIE, $name, $default); 290 + $value = idx($_COOKIE, $name, $default); 291 + 292 + // Internally, PHP deletes cookies by setting them to the value 'deleted' 293 + // with an expiration date in the past. 294 + 295 + // At least in Safari, the browser may send this cookie anyway in some 296 + // circumstances. After logging out, the 302'd GET to /login/ consistently 297 + // includes deleted cookies on my local install. If a cookie value is 298 + // literally 'deleted', pretend it does not exist. 299 + 300 + if ($value === 'deleted') { 301 + return null; 302 + } 303 + 304 + return $value; 291 305 } 292 306 293 307 final public function clearCookie($name) { 294 308 $name = $this->getPrefixedCookieName($name); 295 - $this->setCookie($name, '', time() - (60 * 60 * 24 * 30)); 309 + $this->setCookieWithExpiration($name, '', time() - (60 * 60 * 24 * 30)); 296 310 unset($_COOKIE[$name]); 297 311 } 298 312 ··· 348 362 return (bool)$this->getCookieDomainURI(); 349 363 } 350 364 351 - final public function setCookie($name, $value, $expire = null) { 365 + 366 + /** 367 + * Set a cookie which does not expire for a long time. 368 + * 369 + * To set a temporary cookie, see @{method:setTemporaryCookie}. 370 + * 371 + * @param string Cookie name. 372 + * @param string Cookie value. 373 + * @return this 374 + * @task cookie 375 + */ 376 + final public function setCookie($name, $value) { 377 + $far_future = time() + (60 * 60 * 24 * 365 * 5); 378 + return $this->setCookieWithExpiration($name, $value, $far_future); 379 + } 380 + 381 + 382 + /** 383 + * Set a cookie which expires soon. 384 + * 385 + * To set a durable cookie, see @{method:setCookie}. 386 + * 387 + * @param string Cookie name. 388 + * @param string Cookie value. 389 + * @return this 390 + * @task cookie 391 + */ 392 + final public function setTemporaryCookie($name, $value) { 393 + return $this->setCookieWithExpiration($name, $value, 0); 394 + } 395 + 396 + 397 + /** 398 + * Set a cookie with a given expiration policy. 399 + * 400 + * @param string Cookie name. 401 + * @param string Cookie value. 402 + * @param int Epoch timestamp for cookie expiration. 403 + * @return this 404 + * @task cookie 405 + */ 406 + final private function setCookieWithExpiration( 407 + $name, 408 + $value, 409 + $expire) { 352 410 353 411 $is_secure = false; 354 412 ··· 370 428 371 429 $base_domain = $base_domain_uri->getDomain(); 372 430 $is_secure = ($base_domain_uri->getProtocol() == 'https'); 373 - 374 - if ($expire === null) { 375 - $expire = time() + (60 * 60 * 24 * 365 * 5); 376 - } 377 431 378 432 $name = $this->getPrefixedCookieName($name); 379 433
+30 -2
src/applications/auth/constants/PhabricatorCookies.php
··· 4 4 * Consolidates Phabricator application cookies, including registration 5 5 * and session management. 6 6 * 7 - * @task next Next URI Cookie 7 + * @task clientid Client ID Cookie 8 + * @task next Next URI Cookie 8 9 */ 9 10 final class PhabricatorCookies extends Phobject { 10 11 ··· 48 49 const COOKIE_NEXTURI = 'next_uri'; 49 50 50 51 52 + /* -( Client ID Cookie )--------------------------------------------------- */ 53 + 54 + 55 + /** 56 + * Set the client ID cookie. This is a random cookie used like a CSRF value 57 + * during authentication workflows. 58 + * 59 + * @param AphrontRequest Request to modify. 60 + * @return void 61 + * @task clientid 62 + */ 63 + public static function setClientIDCookie(AphrontRequest $request) { 64 + 65 + // NOTE: See T3471 for some discussion. Some browsers and browser extensions 66 + // can make duplicate requests, so we overwrite this cookie only if it is 67 + // not present in the request. The cookie lifetime is limited by making it 68 + // temporary and clearing it when users log out. 69 + 70 + $value = $request->getCookie(self::COOKIE_CLIENTID); 71 + if (!strlen($value)) { 72 + $request->setTemporaryCookie( 73 + self::COOKIE_CLIENTID, 74 + Filesystem::readRandomCharacters(16)); 75 + } 76 + } 77 + 78 + 51 79 /* -( Next URI Cookie )---------------------------------------------------- */ 52 80 53 81 ··· 83 111 } 84 112 85 113 $new_value = time().','.$next_uri; 86 - $request->setCookie(self::COOKIE_NEXTURI, $new_value); 114 + $request->setTemporaryCookie(self::COOKIE_NEXTURI, $new_value); 87 115 } 88 116 89 117
+1 -3
src/applications/auth/controller/PhabricatorAuthLinkController.php
··· 79 79 80 80 $panel_uri = '/settings/panel/external/'; 81 81 82 - $request->setCookie( 83 - PhabricatorCookies::COOKIE_CLIENTID, 84 - Filesystem::readRandomCharacters(16)); 82 + PhabricatorCookies::setClientIDCookie($request); 85 83 86 84 switch ($this->action) { 87 85 case 'link':
+1 -1
src/applications/auth/controller/PhabricatorAuthLoginController.php
··· 181 181 $account->save(); 182 182 unset($unguarded); 183 183 184 - $this->getRequest()->setCookie( 184 + $this->getRequest()->setTemporaryCookie( 185 185 PhabricatorCookies::COOKIE_REGISTRATION, 186 186 $registration_key); 187 187
+2 -4
src/applications/auth/controller/PhabricatorAuthStartController.php
··· 30 30 // it and warn the user they may need to nuke their cookies. 31 31 32 32 $session_token = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); 33 + 33 34 if (strlen($session_token)) { 34 35 $kind = PhabricatorAuthSessionEngine::getSessionKindFromToken( 35 36 $session_token); ··· 87 88 88 89 if (!$request->isFormPost()) { 89 90 PhabricatorCookies::setNextURICookie($request, $next_uri); 90 - 91 - $request->setCookie( 92 - PhabricatorCookies::COOKIE_CLIENTID, 93 - Filesystem::readRandomCharacters(16)); 91 + PhabricatorCookies::setClientIDCookie($request); 94 92 } 95 93 96 94 $not_buttons = array();