sakurity / securelogin

This version won't be maintained!
MIT License
1.22k stars 35 forks source link

Backwards compatible protocol / scheme #35

Closed menzow closed 7 years ago

menzow commented 7 years ago

Hey,

One problem of deterministic schemes is that it can't store variable data. This especially becomes an issue when you need do deprecate or refactor the scheme used for password generation. A change means losing access to existing logins.

I would suggest versioning the hashing schemes, and creating a way of communicating the scheme version between the requesting and signing party. SL will always incrementally add hashing schemes while maintaining support for older ones. When a requesting party requests a deprecated hashing scheme; the password is hashed according to this scheme; but a update of the password is also sent to the requesting party along with the new version number.

This way requesting and signing parties maintain backwards compatibility and granular upgrading.

A point of concern with this would be requesting known weak schemes to attempt cracking the result. This could be solved through TTL (expiration dates) of older schemes in favour of the latest schemes. If an older scheme is requested past the TTL time frame, SL can either refuse hashing or display a warning to the user that the website is using an outdated scheme.

homakov commented 7 years ago

I'm not sure I understand what is requesting party here. Websites cannot request much from the app. They only offer provider and scope.

The hashing algo will be changed indeed, and there will be list like Weak, Medium, Recommended etc but it's only alg that will be applied to email+pw, and will never be exposed to websites. Users may migrate from email1+pw1+alg1 to email1+pw2+alg2 using Change Password page.

homakov commented 7 years ago

If you're talking about how verify method is implemented on the server side (signature verification, not hashing): https://github.com/andrewda/node-securelogin/blob/master/index.js#L53 then there's no planned versions in this direction. NaCl is a solid choice of primitives and if the industry chooses something else we will manually migrate to it. It doesn't happen often so far.

menzow commented 7 years ago

The requesting party is the website the user wants to login to. To maintain backwards compatibility incase of hashing scheme changes, you need to be able to specify which hashing scheme to use.

Lets say SL has hashing scheme "SLv1", and a user registers like so (Psuedocode ahead):

{
    email: 'user@example.com',
    password: md5(email + password),
    scheme: 'SLv1'
}

Now SL is updated, the hashing scheme is udpated to "SLv2":

{
    email: 'user@example.com',
    password: sha1(email + password),
    scheme: 'SLv2'
}

We will lose access to all websites, because of the new password result. md5(email+password) !== sha1(email+password)

To solve this issue, we want the requesting party to be able to specify the hashing scheme.

Lets say website A has user stored as: {user: 'user@example.com', password: 'hashV1', scheme: 'SLv1'}

In the browser, we let the user enter their email. We use this email to request a hashing scheme:

>> https://example.com/scheme-for?user=user@example.com // Request the active hashing scheme for registered user user@example.com
<< SLv1 // user exists with scheme SLv1

With the returned hashing scheme, we open the secure login app.

>> securelogin://?scheme=SLv1
<< {
    user: email, password: md5(email + password), scheme: 'SLv1',  // Login result matching requested hashing scheme 
    update: {user: email, password: sha1(email + password), scheme: 'SLv2'} // Latest hashing scheme.
    }

Website A will receive the object returned in <<, validate the result, and update the existing entry if the result has a update attribute.

// Website:
if(`SELECT user FROM users WHERE (
    email == result.email 
    && password == result.password
    && scheme == result.scheme`
) {
    // Login Successful
    if(result.update) {
        `UPDATE {password: result.update.password, scheme: result.update.scheme}
        WHERE user.email == user.email`
    }

} else {
    // Login Failed
}

Now website A's database will look like this: {user: 'user@example.com', password: 'hashV2', scheme: 'SLv2'}

When the user attempts to sign in again:

Request scheme:

>> https://example.com/scheme-for?user=user@example.com // Request the active hashing scheme for registered user user@example.com
<< SLv2 // user exists with scheme SLv2

Login user:

>> securelogin://?scheme=SLv2
<< {
    user: email, password: sha1(email + password), scheme: 'SLv2',  // Login result matching requested hashing scheme 
    }
deltaidea commented 7 years ago

@menzow There're two alternatives with your approach:

I'm with @homakov on this one.

... it's only alg that will be applied to email+pw, and will never be exposed to websites. Users may migrate from email1+pw1+alg1 to email1+pw2+alg2 using Change Password page.

homakov commented 7 years ago

We neither use sha or md5, only crypto box of nacl: https://github.com/sakurity/securelogin/blob/master/js/main.js#L275

There's also nothing like a "password" that's ever returned, only sltoken. Have a look at node lib above to see how it's parsed: no hashing again. if there would be a need to change from crypto box to something else (e.g. flaw in curve) that would be a different and unlikely scenario to add version to it. Now no need to bloat it with &ver=1

Websites cannot command what kind of derivation users should have used when they created their profiles. This is stored for future use https://github.com/sakurity/securelogin/blob/master/js/main.js#L412

menzow commented 7 years ago

Hey @homakov, the examples I gave are Psuedo code. They're meant to show backwards compatibility in a general sense, without diving into exact implementation.

I'll try to reiterate tomorrow.

homakov commented 7 years ago

Yeah I can't follow, so it's better if you can give direct links so i see what you mean

homakov commented 7 years ago

Not heard back yet @menzow , and from what I understood, at this point, there's no versioning. It might be added in the future and no need to bloat with v=1 requests right now.