Core: Using SSO authentication to login to an existing un/pw account

Created on 30 Mar 2016  Â·  27Comments  Â·  Source: flarum/core

I created an administrative user authenticated with a local password; later, I logged it out, and selected to login using github.

I was immediately logged in to my administrator account.

This means if the upstream services do not verify email addresses sufficiently it is possible to impersonate/use the account of a local administrative user, etc.

This has been an area where facebook in particular has had a history of security bugs (e.g. myopera accounts not requiring email address verification).

If a user account has a password, it should be necessary to enter the password to allow access from facebook, IMO.

typfeature

Most helpful comment

Just wanted to comment here, because I recently dealt with this in another project. Here's how I solved the problem (This is just for input, feel free to take it or leave it).

When a user attempts to log in using OAuth from another provider, and the application discovers an email collision, the application will inform the user that it appears as though they already have an account.

The message contains something along the lines of:

It appears as though, you may already have an account.
However, this provider is not linked to it.

You'll need to log in using your existing account before you can
link your {providerName} account.

If you are having trouble logging in, click [here](/forgotUsername)

Basically, they must log in using existing credentials before they can link another provider and then they can use this new provider to log in in the future.

All 27 comments

Thanks for bringing this up! I agree that we should be more strict in such a situation.

Applied the necessary tags. I can't agree more that this is a security issue, the Abstract SSO provider should take this into consideration.

Still needs discussion as to how we want to solve it (from both a user and technical perspective)

make sure Authentication user id (e.g. _twitter_id_, _facebook_id_) has been saved in authentication for first time (when user want to register in Flarum)
in this case when user want to login if Authentication user id from both provider and Flarum DB were same we can let user login otherwise user have to choose a username for registering

@sijad I agree about having an Auth user_id kind of field, but how would that resolve the email issue?

If Im registered with email [email protected] and someone SSO's with the [email protected], then yeah, create a new user, but the emails would be the same then no? Is there a duplicate check on the email if the user hasn't got any auth user_ids? And can they, in the future, link their identities in their profile?

I believe I've solved this issue. See https://github.com/flarum/core/pull/1033, https://github.com/flarum/flarum-ext-auth-facebook/pull/5, and https://github.com/flarum/flarum-ext-auth-github/pull/5 . Twitter was already using IDs so there was no need to modify it.

The only possible issue I still see is this because if someone writes an SSO extension and uses email instead of IDs they may be opening a security hole. Should we disallow the use of email for this?

Thanks @dav-is! I'll try my best to review them during the next few days. :)

Thanks for taking a stab at this @dav-is, I appreciate your effort to keep things moving forward.

I think you've made a good start but it's not quite there. It seems to me if someone has an account with a password, and tries to log in via Facebook (with a matching email address), Flarum will prompt them to create a _new account_ and auto-fill that email address.

Rather, if you try to log in via SSO and there's already an account with the same email, we want to prompt the user to enter the password for their account. If they do so, then that SSO login will be associated with their account and they will no longer need to enter their password in the future.

One complication is this scenario: if a user creates their account initially using SSO, then their account won't have a password. Then how can we validate subsequent SSO logins without asking them for a password?

Perhaps instead of asking for a password to validate ownership of an account, we should send a confirmation email each time. So if you try to log in via SSO and there's already an account with the same email, send an email to that address with a link to activate the new SSO login? Thoughts @franzliedke?

@dav-is Just as a side note, in the future it might be preferable to reopen a dialogue about the best way to go about solving an issue before jumping in and sending a PR. Could potentially save a bit of trouble!

how can we validate subsequent SSO logins without asking them for a password

The ID of the SSO user is stored in the database

@dav-is I'm talking about subsequent SSO logins from new providers - ones which don't already have an ID stored in the database. e.g. Create a new account for [email protected] via Facebook, then log out, then log in via GitHub for [email protected].

Don't all the SSO providers we use confirm the email address when the user
registers with them?

What additional security would that email-reconfirmation at our end provide?

On Sunday 25 September 2016, Toby Zerner [email protected] wrote:

@dav-is https://github.com/dav-is I'm talking about subsequent SSO
logins from new providers - ones which don't already have an ID stored in
the database. e.g. Create a new account for [email protected]
then log in via GitHub for [email protected]

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/flarum/core/issues/897#issuecomment-249404065, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AHCrLHS414u4ry7lzcBI-wlu76NDv8tvks5qtgi2gaJpZM4H7SHw
.

-Tarun

@tarunmarkose The idea is that although third-party providers do tend to verify email addresses (especially the providers we officially support), we can't necessarily trust them to. So if someone creates an SSO extension for a provider that _doesn't_ verify email addresses, that's a security issue for Flarum.

Now that I think about it, I don't think this is critical and I don't think it should be blocking beta 6. Realistically, fb/twitter/github are trustworthy enough. I think it would be reasonable to have the extra layer of security as a per-extension opt-out thing, so that providers that are trustworthy beyond reasonable doubt don't require email-reconfirmation.

I agree with you @tobscure , let's not have this block B6. I think if someone decides to implement a SSO that does not verify the email address, they should take measures to at least inform the user. We (as staff) can inform the users additionally in the forum thread (if it exists).

Are we ok to move this to B7 (or any follow up release) @flarum/core ?

Just wanted to comment here, because I recently dealt with this in another project. Here's how I solved the problem (This is just for input, feel free to take it or leave it).

When a user attempts to log in using OAuth from another provider, and the application discovers an email collision, the application will inform the user that it appears as though they already have an account.

The message contains something along the lines of:

It appears as though, you may already have an account.
However, this provider is not linked to it.

You'll need to log in using your existing account before you can
link your {providerName} account.

If you are having trouble logging in, click [here](/forgotUsername)

Basically, they must log in using existing credentials before they can link another provider and then they can use this new provider to log in in the future.

@wackyMole Thanks for the input. What about if their existing account was created via OAuth too? i.e. You create an account using Facebook. Then you log out, and try to log in via Twitter with the same email address. Does it require you to log in via Facebook again before it'll let you link Twitter?

I think that's less than ideal UX. What if I don't remember how I created my account in the first place... do I just have to randomly try SSO buttons until I get in?

Why not just prompt for the password like when changing settings GitHub and If they signed up with OAuth initially, confirm with an email? If they are spoofing the email, they could just as easily send a password reset email so it's no like making them log in manually would prevent spam.

@tobscure If you initially created your account with facebook, then yes, you'll need to log in with it before you can link your twitter account. Ideally you'd be able to link new social media sites on the "edit profile" page or whatever makes sense. If you don't remember how you created the account initially, you click the /forgotUsername link. Enter your email and we can send them a reminder link.

It looks like you used your facebook account to create an account.

You can [set a password on this account](/forgotPassword) or [sign in now using facebook.] (Link to whatever URL kicks off the facebook login process).

I would not include a link to the reset password page with this email. I'd make them enter their email into the forgot password page to receive a separate email. That way the username information (or the fact they created it through social media) are not included in the same email that contains a link to change their password. The second link in the email would kick them directly into the "login with facebook" flow instead of just to the login page where they have to click login with facebook manually.

The point in this flow is that it removes the dependency on social media to access accounts created via social media and returns the emphasis to the email. Unfortunately, there is no "fantastic" way to handle this from a UX POV that I've found. It does require some jumping and some hoops to get it done while still maintaining the security of the system and user accounts.

I'd make them enter their email into the forgot password page to receive a separate email. That way the username information (or the fact they created it through social media) are not included in the same email that contains a link to change their password.

But it they have control of the (lets say) Twitter account, they could easily find the email it uses in the control panel. There's no extra level of security, just another hoop to jump through.

Simply send an email saying "Are you attempting to log in with Facebook?" And if they click the link in the email, it links the Facebook account and signs them in

If the user ignores said email, the account is never linked. And some rate limiting would be put in place for sending confirmation emails for SSO. Instead of saying what you're doing and justifying it, tell me what's wrong with this approach.

@dav-is I'm not trying to justify anything. I'm simply providing information on how I solved the problem in my situation.

As far as your idea for sending a link to an email address, you don't want to do that because email should never be assumed to be secure. First of all, your approach does not confirm someones identity before linking an account. Let's say I created an account through Facebook and then I try to log in using my Twitter account with the same email. Now, with your approach, and from the information you've given me to work with, I get an email with a link to "link" my Twitter account. I click that link and now I have access to my Flarum account.

Let say we are both sitting in a coffee shop and let's say I'm trying to gain access to your Flarum account. I know you have an account. I don't know how you signed up, but I realize that the particular instance of Flarum allows me to sign in using LinkedIn. I also happen to notice that your email address is not in use on LinkedIn. So I create a LinkedIn account using your email address and then log in to Flarum using "my" new LinkedIn account. Now because we are both on the same WIFI network, I sniff your email and guess what. I now have a link that I can use to "link" my LinkedIn account. Even if you never click it. I now have access to your Flarum account.

I realize this example is a bit extreme but it illustrates why you should require a reasonable number of hoops and jumps. You should ALWAYS require the user to enter their password. If they don't know the password, then send them a one time use link in a separate email. Don't make it easy for that guy sitting in the coffee house with you to get access to your username and your password by sending them in the same email, because, even if you click that one-time link before me. I now know your username and can reset the password again and sniff it and click it this time, before you even notice it.

Let say we are both sitting in a coffee shop and let's say I'm trying to gain access to your Flarum account. I know you have an account. I don't know how you signed up, but I realize that the particular instance of Flarum allows me to sign in using LinkedIn. I also happen to notice that your email address is not in use on LinkedIn. So I create a LinkedIn account using your email address and then log in to Flarum using "my" new LinkedIn account. Now because we are both on the same WIFI network, I sniff your email and guess what. I now have a link that I can use to "link" my LinkedIn account. Even if you never click it. I now have access to your Flarum account.

This is probably getting excessive for a forum account. Yes, blindly trusting upstream providers' email authentication is probably a bad idea because they've been known to have issues with it in the past (though it's mostly good now and major ones-- FB, Linkedin, github, etc-- will only hand verified email addresses when they're a signon provider)... But falling down in scenarios where the user is being sniffed in an active attack AND an upstream SSO provider has a vulnerability is maybe not that big of a concern.

I sniff your email and guess what

Couldn't you just request a password reset and achieve the same thing? It only takes your email and in this scenario you have the user's email address.

Issue #1050 is probably related

This is how I handle this in a recent project, which only allows social logins; and some info about the vulnerability: https://securityintelligence.com/spoofedme-social-login-attack-discovered-by-ibm-x-force-researchers/.
I hope it helps!

public function handleProviderCallback($provider){
        $socialUser = Socialite::driver($provider)->user();

        /*
         * We have in this operation 3 different cases ordered by rarity:
         *  - Who is logging in is a new user and the email account does not exists
         *  - Who is logging in is an existent user and is using the same social auth as the first time
         *  - Who is logging in is an existent user but is not using the same social auth as the first time
         *
         * In the last case, we have to deal with two different accounts with the same email which is an unique field.
         * Then, as the user model will always be on a relational database cause is mandatory to relate bills,
         * we use PDO error codes to intercept the duplicated unique field constraint and then we ask auth
         * to the user by his first social authentication method.
         * This solves the SpoofedMe vulnerability.
         */

        try{
            $user = User::firstOrCreate([
                "email" => $socialUser->email,
                "provider" => $provider,
                "provider_id" => $socialUser->id
            ],[
                "name" => $socialUser->name
            ]);

            $user->update(['avatar' => $socialUser->avatar]);

            \Auth::login($user, true);
        } catch (QueryException $e){
            if($e->errorInfo[0] == 23000){
                $currentUser = User::where("email", $socialUser->email)->first();
                return redirect()->to(\URL::previous())->with("provider", $currentUser->provider)->withErrors(trans("front.auth.duplicated", [
                    "current" => $currentUser->provider,
                    "new" => $provider,
                    "email" => $socialUser->email
                ]));
            }

            throw $e;
        }

        return redirect()->to(\URL::previous());
    }

For example, Github kindly provides if an email is verified or not when you use the scope read:email (which I'm guessing flarum/flarum-ext-github already does).

image

1514 partially fixes this by getting login providers to specify that the email they are providing is trusted. However, for providers where the email is not trusted, there is still no way to link them into an existing account. Thus I am leaving this issue open.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. We do this to keep the amount of open issues to a manageable minimum.
In any case, thanks for taking an interest in this software and contributing by opening the issue in the first place!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tjrgg picture tjrgg  Â·  3Comments

MichaelBelgium picture MichaelBelgium  Â·  4Comments

jordanjay29 picture jordanjay29  Â·  3Comments

Ralkage picture Ralkage  Â·  3Comments

datitisev picture datitisev  Â·  3Comments