public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); // If the user already has a full session, just kick them out of here. $has_partial_session = $viewer->hasSession() && $viewer->getSession()->getIsPartial(); if (!$has_partial_session) { return id(new AphrontRedirectResponse())->setURI('/'); } $engine = new PhabricatorAuthSessionEngine(); // If this cookie is set, the user is headed into a high security area // after login (normally because of a password reset) so if they are // able to pass the checkpoint we just want to put their account directly // into high security mode, rather than prompt them again for the same // set of credentials. $jump_into_hisec = $request->getCookie(PhabricatorCookies::COOKIE_HISEC); try { $token = $engine->requireHighSecuritySession($viewer, $request, '/logout/', $jump_into_hisec); } catch (PhabricatorAuthHighSecurityRequiredException $ex) { $form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm($ex->getFactors(), $ex->getFactorValidationResults(), $viewer, $request); return $this->newDialog()->setTitle(pht('Provide Multi-Factor Credentials'))->setShortTitle(pht('Multi-Factor Login'))->setWidth(AphrontDialogView::WIDTH_FORM)->addHiddenInput(AphrontRequest::TYPE_HISEC, true)->appendParagraph(pht('Welcome, %s. To complete the login process, provide your ' . 'multi-factor credentials.', phutil_tag('strong', array(), $viewer->getUsername())))->appendChild($form->buildLayoutView())->setSubmitURI($request->getPath())->addCancelButton($ex->getCancelURI())->addSubmitButton(pht('Continue')); } // Upgrade the partial session to a full session. $engine->upgradePartialSession($viewer); // TODO: It might be nice to add options like "bind this session to my IP" // here, even for accounts without multi-factor auth attached to them. $next = PhabricatorCookies::getNextURICookie($request); $request->clearCookie(PhabricatorCookies::COOKIE_NEXTURI); $request->clearCookie(PhabricatorCookies::COOKIE_HISEC); if (!PhabricatorEnv::isValidLocalWebResource($next)) { $next = '/'; } return id(new AphrontRedirectResponse())->setURI($next); }
public function testLocalWebResource() { $map = array('/' => true, '/D123' => true, '/path/to/something/' => true, "/path/to/\nHeader: x" => false, 'http://evil.com/' => false, '//evil.com/evil/' => false, 'javascript:lol' => false, '' => false, null => false, '/\\evil.com' => false); foreach ($map as $uri => $expect) { $this->assertEqual($expect, PhabricatorEnv::isValidLocalWebResource($uri), "Valid local resource: {$uri}"); } }
public function processRequest() { $request = $this->getRequest(); $failures = array(); if (!$request->getStr('phusr')) { throw new Exception("Login validation is missing expected parameters!"); } $expect_phusr = $request->getStr('phusr'); $actual_phusr = $request->getCookie('phusr'); if ($actual_phusr != $expect_phusr) { if ($actual_phusr) { $cookie_info = "sent back a cookie with the value '{$actual_phusr}'."; } else { $cookie_info = "did not accept the cookie."; } $failures[] = "Attempted to set 'phusr' cookie to '{$expect_phusr}', but your " . "browser {$cookie_info}"; } if (!$failures) { if (!$request->getUser()->getPHID()) { $failures[] = "Cookies were set correctly, but your session " . "isn't valid."; } } if ($failures) { $list = array(); foreach ($failures as $failure) { $list[] = '<li>' . phutil_escape_html($failure) . '</li>'; } $list = '<ul>' . implode("\n", $list) . '</ul>'; $view = new AphrontRequestFailureView(); $view->setHeader('Login Failed'); $view->appendChild('<p>Login failed:</p>' . $list . '<p><strong>Clear your cookies</strong> and try again.</p>'); $view->appendChild('<div class="aphront-failure-continue">' . '<a class="button" href="/login/">Try Again</a>' . '</div>'); return $this->buildStandardPageResponse($view, array('title' => 'Login Failed')); } $next = nonempty($request->getStr('next'), $request->getCookie('next_uri')); $request->clearCookie('next_uri'); if (!PhabricatorEnv::isValidLocalWebResource($next)) { $next = '/'; } return id(new AphrontRedirectResponse())->setURI($next); }
/** * Format a URI for use in a "Location:" header. * * Verifies that a URI redirects to the expected type of resource (local or * remote) and formats it for use in a "Location:" header. * * The HTTP spec says "Location:" headers must use absolute URIs. Although * browsers work with relative URIs, we return absolute URIs to avoid * ambiguity. For example, Chrome interprets "Location: /\evil.com" to mean * "perform a protocol-relative redirect to evil.com". * * @param string URI to redirect to. * @param bool True if this URI identifies a remote resource. * @return string URI for use in a "Location:" header. */ public static function getURIForRedirect($uri, $is_external) { $uri_object = new PhutilURI($uri); if ($is_external) { // If this is a remote resource it must have a domain set. This // would also be caught below, but testing for it explicitly first allows // us to raise a better error message. if (!strlen($uri_object->getDomain())) { throw new Exception(pht('Refusing to redirect to external URI "%s". This URI ' . 'is not fully qualified, and is missing a domain name. To ' . 'redirect to a local resource, remove the external flag.', (string) $uri)); } // Check that it's a valid remote resource. if (!PhabricatorEnv::isValidRemoteWebResource($uri)) { throw new Exception(pht('Refusing to redirect to external URI "%s". This URI ' . 'is not a valid remote web resource.', (string) $uri)); } } else { // If this is a local resource, it must not have a domain set. This allows // us to raise a better error message than the check below can. if (strlen($uri_object->getDomain())) { throw new Exception(pht('Refusing to redirect to local resource "%s". The URI has a ' . 'domain, but the redirect is not marked external. Mark ' . 'redirects as external to allow redirection off the local ' . 'domain.', (string) $uri)); } // If this is a local resource, it must be a valid local resource. if (!PhabricatorEnv::isValidLocalWebResource($uri)) { throw new Exception(pht('Refusing to redirect to local resource "%s". This URI is not ' . 'formatted in a recognizable way.', (string) $uri)); } // Fully qualify the result URI. $uri = PhabricatorEnv::getURI((string) $uri); } return (string) $uri; }