8

In my Angular app, I use the MongoDB id in the URLs. Are there any security risks to this?

Should I use a counter instead, and then in my DB have some sort of collection that links this counter to the actual ID? So instead of mysite.com/story/56ede7fsdfdsfdsfs2a7283 I would use mysite.com/story/32?

Anders
  • 64,406
  • 24
  • 178
  • 215
userMod2
  • 181
  • 1
  • 3

4 Answers4

5

I can't think of any real security risk to exposing the mongoDB id (versus some other counter id), other than exposing creation time according to the server to the users. Having the mongo primary key vs some other unique key (like a counter) shouldn't make a difference in terms of exposure.

Granted you should be aware, the mongo _id consists of:

  • a 4-byte value representing the seconds since the Unix epoch,
  • a 3-byte machine identifier,
  • a 2-byte process id, and
  • a 3-byte counter, starting with a random value.

and possibly you don't want to expose some of this information to your users (like creation time).

So when you have the id 56ede7f0dfdsfdsfs2a7283 (which has a non-hex digit in there s and seems to be missing a hex digit at the end -- it should be 12 bytes or 24 hex (0-9a-f) digits, so I've replaced the 's' with '0'), I can tell from 56ede7f0 that it was created at 2016-03-19T23:59:44.000Z. (See for example: https://steveridout.github.io/mongo-object-time/ or try: ObjectId("56ede7f0dfd0fd0f20a72830").getTimestamp() in the shell).

dr jimbob
  • 38,768
  • 8
  • 92
  • 161
  • Thanks for that - the date of creation doesn't matter too much as I display that on the page anyway - Is there another security risk around that I'm missing? – userMod2 Apr 19 '16 at 17:22
3

The OWASP mentions that simply having any sort of direct identifier can be bad, as explained in the Top 10 2007-Insecure Direct Object Reference and Top 10 2010-A4-Insecure Direct Object References entries. An attacker that can figure out how to exploit such an direct reference will have far more power than they should.

The OWASP actually recommends using an index value, as you've asked about here, but those can come with their own problems. Giving attackers a nice, easy way to access all your data sequentially can allow them to easily read, update, or even delete your entire database if they find an authentication or privilege escalation exploit.

In the real world, such occurrences have happened before. Like several email address dumps gained from services using a simple primary key in a password reset page. Or systemically having all of their messages deleted from the service. The list can go on, but the point is, if you decide to use a auto-increment field as the primary key, make sure that all pages that use that ID validate the request to be sure that (a) the user has sufficient permission, and (b) the request has not been forged, probably by way of including cross-site request forgery tokens, essentially nonce values that verify that the request is legitimate.

phyrfox
  • 5,724
  • 20
  • 24
  • So as per an answer above, if i generate a long enough random ID (of letters and numbers), ensure it doesn't already exist I should be ok? – userMod2 Apr 19 '16 at 17:51
  • @userMod2 the *intention* of the value is the important part. For example, if the user can only *read* values that are public to everyone, then an incremental index is probably okay. If users can view private records, or edit/delete records, then at minimum, make sure your security won't leak data, and consider using longer, randomized ID values that are neither the MongoDB Id values or an index. – phyrfox Apr 19 '16 at 18:00
  • Yes, users can only read values that are public to everyone to incremental will be fine. Nevertheless I'll make it a reasonable length - like in the URL above :-) – userMod2 Apr 19 '16 at 18:07
2

Giving away information in the URL

An URL might be accessible even to a person that is not supposed to have access to the actual page it leads to. After all URLs can show up in links all over the web or leak via referer headers. Therefore there should be no sensitive information in the URL.

The Mongo ID contains information about the time the object was created (from the first part). The counter only contains information about in what order objects were created.

The counter also gives away information about how many objects there are in total (see the german tank problem). But so does the Mongo ID, since the last part is a global counter. It starts from a random number, so it conveys less information, but an attacker with lots of URLs can still estimate the total number of documents.

Enumeration attacks

If URLs are sequential it is easy for an attacker to obtain a list of all resources. This is called an enumeration attack.

For the counter this is obviously easy to do. For the Mongo ID it is harder, since you have to guess at what millisecond objects were created. If you have a vague idea around what time objects are created, you could still brute force it.

Conclusion

If you are worried about giving away information about your documents in the URLs or letting users enumerate all documents both approaches are pretty bad. If you are not, both approaches are fine.

A third solution

If the issues above worry you, you could use a random ID instead. Use a large space to decrease the probability of collisions, and have some error handling so that things don't break if you are unlucky.

The ID could either be stored as the _id (you can override the default) or in a field named url_id or something like that. Put a unique hash index on it to make lookups fast.

Anders
  • 64,406
  • 24
  • 178
  • 215
1

This exposes meta information in the MongoDB ID as stated in the answer from dr jimbob which is a security concern in some aspects.

Another way to post this question is

What is the best practice for not exposing meta information about the entries in my DB in links and URIs?

The answer to that question is simple: Use a unique identifier field for the entry that isn't tied to any meta information. These can be generated easily, checked against other entries, and if a collision is found, generate another one. You can then store this in a hash/index/whatever system MongoDB uses for finding these documents faster.

However this doesn't address people sharing them over social media, so make sure your security model takes that into account and prevents people from seeing things they shouldn't.

Robert Mennell
  • 6,968
  • 1
  • 13
  • 38
  • Because it could expose patterns, entries with sensitive data, and such. When you send it over HTTP it is IMMEDIATELY available to anyone listening, and if this links to a users profile document then you're really screwed. However HTTPS negotiates the SSL before ever sending over user input like Querystrings so it keeps it hidden. It's a scope thing, but still considered security for keeping people from guessing entry IDs and finding access to places they shouldn't have. – Robert Mennell Apr 19 '16 at 16:26
  • I submitted a less-detailed answer for which all details are included in yours so have deleted mine and up-voted this instead. – Arran Schlosberg Apr 19 '16 at 16:29
  • Where in the OPs question did they specify if they are using HTTP? I see them leaving the protocol off of the URL. – dr jimbob Apr 19 '16 at 16:42
  • The default behavior of ALL browsers to support HTTP 1.1 is that if a URL is typed in without protocol, use HTTP. Hence if a user types in the URL without a protocol HTTP will be used by default, meaning this case needs to be checked(based on firefox, chrome, IE/Edge) – Robert Mennell Apr 19 '16 at 16:58