2

Im building a REST api for an ios application!

Would it be a good idea to send the user id (primary autoincrementing token in my mySQL database) of an account when authenticating with user token? (Happens on each get/post)

I thought it might significantly increase performance when checking for authencity of the token? What do you guys think? Does it reduce security? Is this common practice?

I am using HTTPS only of course.

10
  • 1
    An autoincrement token itself is a potential problem, as it's possible for an attacker to extract information from the knowledge IDs exist. For example, an attacker might be able to enumerate all users (if you ever expose data to a user via their ID) and can also find information like how many users are registered at any point or even how many users register in any timeframe. It's not mandatory to hide that information but it's usually a good idea.
    – VLAZ
    Commented Mar 3, 2019 at 16:03
  • @VLAZ Hhmm. Yeah I would have to store the user id inside the application. But thats what alot of apps do? Atleast many games have ”user id 192727” or similar mentioned in the settings! Many popular games! Why do they do that then? Dont they auto increment? I thought everyone used autoincrement. Anyway, would the performance boost be significant? Commented Mar 3, 2019 at 16:19
  • Some do it because they simply don't know better. Others know but don't care - exposing yourself to enumeration isn't necessarily bad. And others still may not even have sequential IDs but random ones, so "user id 192727” wouldn't be preceded by 192726 or followed by 192728. At any rate, as I said - it's a potential consideration. You may actually decide this is OK.
    – VLAZ
    Commented Mar 3, 2019 at 16:27
  • @vlaz But I dont even need to expose the id in any way not even to the app right? I can just use the accessToken for everything and the id never leaves my servers, right? Commented Mar 3, 2019 at 16:33
  • That's possible yes. Yet again, I'm just pointing out a potential issue. I don't know either your app nor your intention, so I can't say what exactly you need to do. It's up to you to decide.
    – VLAZ
    Commented Mar 3, 2019 at 16:36

5 Answers 5

2

Sharing the internal ID of a user with the user himself is not considered a bad practice, many apps do it. Relying on the secrecy of such an ID is, especially if it is autoincremented. This is called security through obscurity.

Would it be a good idea to send the user id (primary autoincrementing token in my mySQL database) of an account when authenticating with user token? (Happens on each get/post)

This can work, but be aware of the checks you need to make on the server side.

For identification and authentication

  • Make sure the user ID exists in your app
  • Make sure the token is valid
  • Make sure the token belongs to the user id passed with the request

For authorization

  • Make sure the user, who is now authentication, has access to the requested resource

I thought it might significantly increase performance when checking for authencity of the token? What do you guys think? Does it reduce security? Is this common practice?

Depending on what authentication scheme you use, you may need to hit the DB anyway. The performance gain if any depends on your use-case. Generally, I would avoid premature optimization. Build the app, measure your bottleneck and if it ever looks like the authentication, authorization needs speeding up, that's when you start thinking.

For your use-case, I would probably go with JWT tokens, where you can encode the user id inside the token. This will take care of the identification and authentication part: as only you can issue such tokens, and the token content cannot be tampered with. Beware, JWT has some limitations when dealing with session management and you still need to deal with authorization!

For alternative authentication methods take a look at the Web Authentication Guide I compiled a while ago.

2

It depends on what you mean by sending user ID "with user token".

If you mean "as a part of the token", then it is fine. On server you verify tokens and you will detect if token was modified, for instance if user has modified user ID (to access account of someone else).

If you mean "additionally to the token", then it is a bad idea. Because user can replace user ID with an ID of some other user and you will not be able to detect it. It means, every authenticated user will be able to work in the name of any other user: Access data he is not allowed to access, trigger some functions in the application that he is not allow to use.

1

If you find yourself concerned about passing the ID to either GET or POST a few changes to your design or approach may be needed.

  • When creating a user account, generate a unique identifier (UUID) based either on ID or some other means like first-name, last-name. If the backend is python have a look at this
  • Generate a slug based on first-name, or last-name eg. (firstname.lastname) or any other thing to generate a slug with.
  • Encrypt the ID and decrypt it at the backend (quite expensive on memory)

This way you don't need to worry about predictive eyes.

1

While it's OK, ideally you would include the userid in a claim of the jwt token.

That's easier said than done. If it's your own service that includes libraries for jwt and issues the tokens only for your own service, then you could add the claim.

If your system uses an external standalone Oidc system that's shared by multiple systems, you would need to login and add claims manually.

Having the claim in the jwt token means it's signed. So you don't need to do a database lookup. Apart from "federation" capabilities, I like that tokens result in fewer queries to the database.

0

Based on your answer to my comment, I think you have a much simpler solution.

Including the user id shouldn't impact performance at all, because you should have an index on your AccessKey column which allows your database to find your user in constant time. In other words you're using a query like this:

SELECT id,email,password_hash FROM users WHERE access_key=?

And you're thinking about changing to something like this so that the primary index can be used:

SELECT id,email,password_hash FROM users WHERE id=? AND access_key=?

However, there is no difference between using the primary key or adding an index to a unique column. At risk of overkill,I did a simple example using MySQL:

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(255) NOT NULL DEFAULT '',
  `api_token` char(32) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE `users_api_token` (`api_token`)
);

You can see that whether you query on the id or the api token, the query plans are exactly the same (the only difference is the choice of index to use):

mysql> describe select id from users where id=1;                
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

mysql> describe select id from users where api_token='asdf';
+----+-------------+--------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type  | possible_keys   | key             | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | users  | NULL       | const | users_api_token | users_api_token | 32      | const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+-------+-----------------+-----------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

But there is a big difference if you were to search on the first_name column which does not have an index:

mysql> describe select id from users where first_name='asdf';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1547 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

Now it is doing a full table scan, which you certainly want to avoid. So if you don't have an index on your AccessKey column then you need to create an index there, or you need to also pass up the user id. I'd personally just put an index on the AccessKey column, as it will be a much simpler and smaller change that reaches the same goal (better performance).

4
  • 1
    Just to play devil's advocate: the index for a varchar is going to be a lot larger than for an autoincrement integer, and the user id doesn't really need to be secret anyway. Of course it's unlikely it'll make any practical difference either way unless you have a lot of users. Commented Aug 5, 2019 at 20:06
  • @AndrolGenhald Further nitpicking: an API token is being presumably generated with a fixed length, so a VARCHAR is the wrong choice anyway - something more like a CHAR(32) would be a better fit (although in a secure production system this table structure doesn't make sense regardless, since the user is assigned an API token when they login, and therefore may have more than one token at a time - one for each device). Commented Aug 5, 2019 at 20:25
  • Although regardless I would do the lookup by API Token rather than by id, since conceptually that is what you actually are doing. After all you are literally authenticating an API token. Therefore, I would to do the lookup by API Token and make the lookup-by-api-token as efficient as possible. Also passing up the user id so that the query can be made more efficient seems like a lot of extra work and a deviation from the core goal here, which leaves more room for mistakes in the future. Commented Aug 5, 2019 at 20:28
  • @AndrolGenhald Ironically your comment is visible in the describe results. The query on id has a key_len=4, while the query by api_token has key_len=257. In practice though indeed, I doubt the difference will matter. Now the difference in key length is smaller :p Commented Aug 5, 2019 at 20:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .