Skip to content

Commit

Permalink
Fix unsecure redirect code.
Browse files Browse the repository at this point in the history
  • Loading branch information
JC5 committed Oct 2, 2021
1 parent 4e84a5c commit 8662dfa
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 2 deletions.
57 changes: 57 additions & 0 deletions app/Exceptions/Handler.php
Expand Up @@ -29,8 +29,11 @@
use ErrorException;
use FireflyIII\Jobs\MailError;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException as LaravelValidationException;
Expand Down Expand Up @@ -187,4 +190,58 @@ private function shouldntReportLocal(Throwable $e): bool
)
);
}

/**
* Convert a validation exception into a response.
*
* @param Request $request
* @param LaravelValidationException $exception
*
* @return Application|RedirectResponse|Redirector
*/
protected function invalid($request, LaravelValidationException $exception): Application|RedirectResponse|Redirector
{
// protect against open redirect when submitting invalid forms.
$previous = $this->getPreviousUrl();
$redirect = $this->getRedirectUrl($exception);

return redirect($redirect ?? $previous)
->withInput(Arr::except($request->input(), $this->dontFlash))
->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag));
}

/**
* Only return the previousUrl() if it is a valid URL. Return default redirect otherwise.
*
* @return string
*/
private function getPreviousUrl(): string
{
$safe = route('index');
$previous = url()->previous();
$previousHost = parse_url($previous, PHP_URL_HOST);
$safeHost = parse_url($safe, PHP_URL_HOST);

return null !== $previousHost && $previousHost === $safeHost ? $previous : $safe;
}

/**
* Only return the redirectTo property from the exception if it is a valid URL. Return NULL otherwise.
*
* @param LaravelValidationException $exception
*
* @return string|null
*/
private function getRedirectUrl(LaravelValidationException $exception): ?string
{
if (null === $exception->redirectTo) {
return null;
}
$safe = route('index');
$previous = $exception->redirectTo;
$previousHost = parse_url($previous, PHP_URL_HOST);
$safeHost = parse_url($safe, PHP_URL_HOST);

return null !== $previousHost && $previousHost === $safeHost ? $previous : $safe;
}
}
1 change: 0 additions & 1 deletion app/Http/Middleware/StartFireflySession.php
Expand Up @@ -45,7 +45,6 @@ protected function storeCurrentUrl(Request $request, $session): void
$url = $request->fullUrl();
$forbiddenWords = strpos($url, 'offline') || strpos($url, 'jscript') || strpos($url, 'delete') || strpos($url, '/login') || strpos($url, '/json') || strpos($url, 'serviceworker') || strpos($url, '/attachments/view');

// also stop remembering "delete" URL's.
if (false === $forbiddenWords
&& 'GET' === $request->method()
&& !$request->ajax()) {
Expand Down
2 changes: 1 addition & 1 deletion app/Support/Http/Controllers/UserNavigation.php
Expand Up @@ -183,7 +183,7 @@ final protected function rememberPreviousUri(string $identifier): ?string
// get host of previous URL:
$previous = parse_url($return, PHP_URL_HOST);

if ($default === $previous && (null === $errors || (0 === $errors->count())) && !Str::contains($return, $forbidden)) {
if (null !== $previous && $default === $previous && (null === $errors || (0 === $errors->count())) && !Str::contains($return, $forbidden)) {
Log::debug(sprintf('Saving URL %s under key %s', $return, $identifier));
session()->put($identifier, $return);

Expand Down

0 comments on commit 8662dfa

Please sign in to comment.