Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redirect to FE preview page after login and ensure integrity of target links #1164

Merged
merged 5 commits into from Jan 10, 2020

Conversation

@Toflar
Copy link
Member

Toflar commented Jan 8, 2020

This PR addresses two issues:

  • Currently we only handle insert tags to be potentially dangerous when outputting the _target_path in the login forms (both, back end and front end). Although it seems to solve the most obvious potential vulnerability, I'd like to use URI signing to make sure these URIs were not tampered with.

  • The new preview bar does not support automated redirect after login. So if I send https://contao.org/prevew.php/about-us.html to a user, they are redirected to the back end login and aren't redirected back automatically after successful login.

This PR solves both of these issues by doing the following:

Both, the ModuleLogin (for the front end) as well as the BackendIndex (for the back end) accepted a referer query parameter which is used to redirect you back to the pages you were coming from after successful login. We have two potential security issues here:

  1. We must not allow any absolute URI as this would be an OWASP "Unvalidated redirects and forwards" security vulnerability.

  2. We must ensure the URIs do not contain mailicious query parameters (such as containing an insert tag) as we output the redirect URI as <form input="hidden" name="_target_path" value="<target-uri-here>">. The insert tags would be interpreted and protected content might be revealed.

This PR addresses 1) by only accepting redirect (I renamed referer to redirect to make sure I don't miss anything) parameters that are properly signed using the UriSigner. So both, the ModuleLogin as well as BackendIndex validate the URIs.

That means, nobody can do something like ?redirect=https://www.google.com...... However, we still have the problem that the "redirect to where you were coming from" is essentially user input. It will always be the URI you were visiting before so I could do some ?evil={{insert_tag.....
This is issue 2) and is addressed by a new controller named Base64EncodedRedirectController. So instead of exposing the provided value directly in HTML (<form input="hidden" name="_target_path" value="https://contao.org?evil={{insert_tag..."> we do a double redirect. So the actual target is protected behind our own route: <form input="hidden" name="_target_path" value="https://contao_org/_contao/base64_redirect?redirect=<base64encoded-which-ensures-security>&_hash=<again-uri-signed-hash-to-prevent-from-random-redirect-targets">.
Again, those target URIs also have to be signed, otherwise anyone can abuse our /_contao/base64_redirect route to redirect people anywhere (which would be a 1) vulnerability again).

@Toflar Toflar requested review from ausi, aschempp, leofeyer and richardhj Jan 8, 2020
…ed it for the new FE preview toolbar
@Toflar Toflar force-pushed the feature/secure-redirects-after-auth branch from c0d6a67 to 66c4ca5 Jan 9, 2020
@Toflar Toflar marked this pull request as ready for review Jan 9, 2020
@Toflar Toflar requested a review from bytehead Jan 9, 2020
@Toflar Toflar self-assigned this Jan 9, 2020
@Toflar Toflar added the feature label Jan 9, 2020
@leofeyer leofeyer added defect and removed feature labels Jan 9, 2020
@leofeyer leofeyer added this to the 4.9 milestone Jan 9, 2020
core-bundle/src/Controller/BackendController.php Outdated Show resolved Hide resolved
$queryString = '?'.base64_decode($request->query->get('referer'), true);
// we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering)
if ($uriSigner->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().(null !== ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''))) {
return new RedirectResponse($request->query->get('redirect'));

This comment has been minimized.

Copy link
@aschempp

aschempp Jan 9, 2020

Contributor

Shouldn't this base64_decode the redirect?

This comment has been minimized.

Copy link
@Toflar

Toflar Jan 9, 2020

Author Member

No, it's not encoded here. Only for the Base64Controller which redirects to this one after decoding. I think.

This comment has been minimized.

Copy link
@aschempp

aschempp Jan 10, 2020

Contributor

So it's encoded for the front end (ModuleLogin) but not for the back end??

This comment has been minimized.

Copy link
@Toflar

Toflar Jan 10, 2020

Author Member

No. The value you get here, is not base64 encoded. The base64 encoding is only used in places where you echo the target URL in the HTML.

This comment has been minimized.

Copy link
@Toflar

Toflar Jan 10, 2020

Author Member

The PreviewAuthenticationListener redirects without base64 encoding.

This comment has been minimized.

Copy link
@aschempp

aschempp Jan 10, 2020

Contributor

For the back end, yes. But the front end seems to work with encoding: https://github.com/contao/contao/pull/1164/files#diff-fced920e5b0ab4f5a25d857b2c04fa02R80

This comment has been minimized.

Copy link
@Toflar

Toflar Jan 10, 2020

Author Member

No, you don't understand. It works exactly the same in both cases. The ones that read the redirect query parameter and acutally redirect to the target page, both work the same.
The base64 encoding is only needed if you want to output the desired target URL in HTML which is never done in the back end.

$queryString = '?'.base64_decode($request->query->get('referer'), true);
// we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering)
if ($uriSigner->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().(null !== ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''))) {
return new RedirectResponse($request->query->get('redirect'));

This comment has been minimized.

Copy link
@aschempp

aschempp Jan 10, 2020

Contributor

So it's encoded for the front end (ModuleLogin) but not for the back end??

core-bundle/src/Controller/BackendController.php Outdated Show resolved Hide resolved
@leofeyer leofeyer dismissed stale reviews from ausi, bytehead, and richardhj via 89cf7d9 Jan 10, 2020
@leofeyer leofeyer force-pushed the feature/secure-redirects-after-auth branch from 89cf7d9 to 4664962 Jan 10, 2020
@leofeyer leofeyer dismissed stale reviews from m-vo and richardhj via 96b3d13 Jan 10, 2020
@leofeyer leofeyer merged commit ae86d71 into master Jan 10, 2020
9 checks passed
9 checks passed
Coverage
Details
Coding Style
Details
PHP 7.2
Details
PHP 7.3
Details
PHP 7.4
Details
Prefer Lowest
Details
Bundles
Details
Windows
Details
codecov/project 89.66% (-0.03%) compared to 03c84e7
Details
@leofeyer leofeyer deleted the feature/secure-redirects-after-auth branch Jan 10, 2020
@leofeyer

This comment has been minimized.

Copy link
Member

leofeyer commented Jan 10, 2020

Thank you @Toflar.

Tastaturberuf pushed a commit to Tastaturberuf/contao that referenced this pull request Jan 13, 2020
…t links (see contao#1164)

Description
-----------

This PR addresses two issues:

* Currently we only handle insert tags to be potentially dangerous when outputting the `_target_path` in the login forms (both, back end and front end). Although it seems to solve the most obvious potential vulnerability, I'd like to use URI signing to make sure these URIs were not tampered with.

* The new preview bar does not support automated redirect after login. So if I send `https://contao.org/prevew.php/about-us.html` to a user, they are redirected to the back end login and aren't redirected back automatically after successful login.

This PR solves both of these issues by doing the following:

Both, the `ModuleLogin` (for the front end) as well as the `BackendIndex` (for the back end) accepted a `referer` query parameter which is used to redirect you back to the pages you were coming from after successful login. We have two potential security issues here:

1. We must not allow any absolute URI as this would be an OWASP "Unvalidated redirects and forwards" security vulnerability.

2. We must ensure the URIs do not contain mailicious query parameters (such as containing an insert tag) as we output the redirect URI as `<form input="hidden" name="_target_path" value="<target-uri-here>">`. The insert tags would be interpreted and protected content might be revealed.

This PR addresses 1) by only accepting `redirect` (I renamed `referer` to `redirect` to make sure I don't miss anything) parameters that are properly signed using the `UriSigner`. So both, the `ModuleLogin` as well as `BackendIndex` validate the URIs.

That means, nobody can do something like `?redirect=https://www.google.com.....`. However, we still have the problem that the "redirect to where you were coming from" is essentially user input. It will always be the URI you were visiting before so I could do some `?evil={{insert_tag....`.
This is issue 2) and is addressed by a new controller named `Base64EncodedRedirectController`. So instead of exposing the provided value directly in HTML (`<form input="hidden" name="_target_path" value="https://contao.org?evil={{insert_tag...">` we do a double redirect. So the actual target is protected behind our own route: `<form input="hidden" name="_target_path" value="https://contao_org/_contao/base64_redirect?redirect=<base64encoded-which-ensures-security>&_hash=<again-uri-signed-hash-to-prevent-from-random-redirect-targets">`.
Again, those target URIs also have to be signed, otherwise anyone can abuse our `/_contao/base64_redirect` route to redirect people anywhere (which would be a 1) vulnerability again).

Commits
-------

66c4ca5 Use UriSigner for secure redirects after authentication and implemented it for the new FE preview toolbar
55e491c Added missing test for new controller
4664962 Fix the coding style
e8a823d Use correct failure path target
96b3d13 Add a FIXME so we do not forget to adjust the uri_signer subscription
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.