There is a good chance that a failed password attempt is an indication of something shady going on, so exponential delays are appropriate.
How are you going to tell the difference between an API hit for the phone number of someone you know vs. a phone number you made up? Or are you going to increase the delay after every query? If you do that, then the "find friends" feature will just break down for people with sufficiently large contact lists.
Maybe don't reveal the username unless that user has your phone number in their contacts? Sounds good. But how are you going to check? Snapchat would need to store every user's contact list, which HN also considers unacceptable. Hashing is not an effective mitigator here because it's so easy to bruteforce the space of 10-digit numbers.
Maybe rate-limiting as such is not the right idea, but how about just limiting accounts? I think there is a substantial difference between the 4.6mm collected and even a "sufficiently large contact list." There must be a number at which you can be banned from requesting any more.
Maybe don't reveal the username unless that user has your phone number in their contacts? Sounds good. But how are you going to check? Snapchat would need to store every user's contact list, which HN also considers unacceptable. Hashing is not an effective mitigator here because it's so easy to bruteforce the space of 10-digit numbers.
There's a partial fix to this that minimises what is stored by Snapchat. It does introduce a delay as it requires the other user to be logged in or eventually log in:-
User_A joins and has 10 numbers in his contacts list and Snapchat wants to find if they are friends (i.e. they both have each other's phone number in their contact lists). All 10 numbers are provided to Snapchat, only 5 numbers correspond to existing Snapchat users, the other 5 numbers are discarded by Snapchat. These remaining numbers, now linked to snapchat Usernames, then go into a table:-
When User_B next logs in the app checks for any people seeking connection with them; an API call returns a list of phone numbers which the app should check (i.e. User_B executing this would get the phone number of User_A) to see if they are in the local contact list (rather than uploading their entire contact list to Snapchat). If any numbers are in that list then they can be provided back to Snapchat and the server examines the table to find out which friends can be paired together, upon return of these results all rows for people seeking User_B will be either actioned (in User_B's contact list) or cleared (not in the contact list).
This means only a subset of a user's contact list is stored (the numbers that are known to use Snapchat), and only until the other party logs in, all of which is akin to an automated 'friend request'.
No persistent storage of all users' contact lists required, nor is there any need to store anything else about the contact list other than it contained the phone number of an existing Snapchat user.
Entries in this DB would timeout (pick an appropriate timeout) so they are not retained in perpetuity, new entries would only be created if the other user (i.e. User_B) has been active in Snapchat recently.
The app would also monitor for additions/modifications to the contact list on the phone and attempt new friend discovery by the same method. (This can be done via hashing, for each contact the app stores locally just a hash of the contact's information - name and phone numbers at least - and then on each startup (or at a certain interval) it compares the hashes it knows about with the hashes obtained from the current contact list, the friend discovery is then performed for any contacts relating to new hashes.)
User_A doesn't need to worry about missing users who join subsequently (i.e. one of the 5 numbers that were unknown to Snapchat when they first joined) as these will be handled by the subsequent user joining and going through this same process.
Comments
There is a good chance that a failed password attempt is an indication of something shady going on, so exponential delays are appropriate.
How are you going to tell the difference between an API hit for the phone number of someone you know vs. a phone number you made up? Or are you going to increase the delay after every query? If you do that, then the "find friends" feature will just break down for people with sufficiently large contact lists.
Maybe don't reveal the username unless that user has your phone number in their contacts? Sounds good. But how are you going to check? Snapchat would need to store every user's contact list, which HN also considers unacceptable. Hashing is not an effective mitigator here because it's so easy to bruteforce the space of 10-digit numbers.
Maybe rate-limiting as such is not the right idea, but how about just limiting accounts? I think there is a substantial difference between the 4.6mm collected and even a "sufficiently large contact list." There must be a number at which you can be banned from requesting any more.
There's a partial fix to this that minimises what is stored by Snapchat. It does introduce a delay as it requires the other user to be logged in or eventually log in:-
User_A joins and has 10 numbers in his contacts list and Snapchat wants to find if they are friends (i.e. they both have each other's phone number in their contact lists). All 10 numbers are provided to Snapchat, only 5 numbers correspond to existing Snapchat users, the other 5 numbers are discarded by Snapchat. These remaining numbers, now linked to snapchat Usernames, then go into a table:-
When User_B next logs in the app checks for any people seeking connection with them; an API call returns a list of phone numbers which the app should check (i.e. User_B executing this would get the phone number of User_A) to see if they are in the local contact list (rather than uploading their entire contact list to Snapchat). If any numbers are in that list then they can be provided back to Snapchat and the server examines the table to find out which friends can be paired together, upon return of these results all rows for people seeking User_B will be either actioned (in User_B's contact list) or cleared (not in the contact list).This means only a subset of a user's contact list is stored (the numbers that are known to use Snapchat), and only until the other party logs in, all of which is akin to an automated 'friend request'.
No persistent storage of all users' contact lists required, nor is there any need to store anything else about the contact list other than it contained the phone number of an existing Snapchat user.
Entries in this DB would timeout (pick an appropriate timeout) so they are not retained in perpetuity, new entries would only be created if the other user (i.e. User_B) has been active in Snapchat recently.
The app would also monitor for additions/modifications to the contact list on the phone and attempt new friend discovery by the same method. (This can be done via hashing, for each contact the app stores locally just a hash of the contact's information - name and phone numbers at least - and then on each startup (or at a certain interval) it compares the hashes it knows about with the hashes obtained from the current contact list, the friend discovery is then performed for any contacts relating to new hashes.)
User_A doesn't need to worry about missing users who join subsequently (i.e. one of the 5 numbers that were unknown to Snapchat when they first joined) as these will be handled by the subsequent user joining and going through this same process.