6

Looking at posts such as this on StackOverflow, the consensus seems to be that you should hide incrementing ID's from external API's for security.

However, I'm having trouble working out a good implementation.

I don't want to use Guids as the ID, because as stated here, it leads to page fragmentation and when I did it before, the DB had performance issues (in part due to the Id's).

I came up with the following class structure. It has an int ID, then a Guid for external identification. The problem with this is that is now means there are excessive calls to the db for inserts and updates.

Models:

public class Blog
{
    public int BlogId { get; set; }
    public Guid BlogGuid { get; set; } = Guid.NewGuid();
    public string Url { get; set; }
    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public Guid PostGuid { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

DTO:

public class BlogDto
{
    // No int id on dto
    public string Url { get; set; }
    public Guid BlogGuid { get; set; }
    public List<PostDto> Posts { get; set; }
}

public class PostDto
{
    // No int id on dto
    public Guid PostGuid { get; set; }  
    public string Title { get; set; }
    public string Content { get; set; }

    public BlogDto Blog { get; set; }
}

Getting the data is fine. As before, we just get and convert to the DTO.

When wanting to do an update or insert, the data comes back with just the Guid id's on all objects. I can't just send entities back, as they do in the tutorials such as this.

I now need to get the original data (by Guid, with includes), then I need to get the related data (by Guid) and work out what to do (something could have been inserted, deleted, or just altered). It all feels a bit messy and unnecessarily chatty with the database.

Is this just a case of more secure = slower, or is this design (as I suspect) somehow inefficient?

  • 3
    How about encrypting the ID? – CodesInChaos Dec 5 '16 at 17:16
  • @CodesInChaos - Good suggestion. That would be much simpler than my Guid approach and lead to easier attachment back in EF. – HockeyJ Dec 5 '16 at 17:56
  • 8
    I think this may be a productive of 'Security through obscurity', generally speaking you should implement a security mechanism to ensure the user has authorization to view a given ID and not rely on the knowledge of the ID itself. – Matthew Dec 5 '16 at 20:27
  • 1
    @Matthew This is not about authorization, but the secondary (business) data gleaned from an incrementing id - number of orders processed, number of registered customers etc etc – Steve Dec 5 '16 at 23:10
  • 1
    @Steve No, all we know is that the OP read an SO question with relatively low views and votes that suggested that there might be a "consensus" that using GUIDs has "security benefits" over sequential IDs. Notably, the OP doesn't mention any actual vulnerabilities to protect from or business requirements they need to meet. – jpmc26 Dec 6 '16 at 0:33
19

The advice at the post you linked about using GUIDs for clustered primary keys is seven years old and almost certainly bad advice today. The article it refers to references SQL Server 7.

We use GUIDs for every primary key in our database, and expose those GUIDs in the URLs that hit our JSON services. We can do this because we don't expose our database internals to the outside world; absent this information, you might as well be exposing random numbers. We don't have any of the performance problems that are predicted in that post.

GUIDs have some unique properties that make them very useful as primary and foreign keys. Because they are globally unique, you can use a single JOIN without worrying about which table you are joining to. Unlike sequential ID's, guessing a neighboring GUID is very nearly impossible, for the same reasons that make GUIDs ideal identifiers in the first place.

The ideal way to prevent exposing incremental ID's? Use GUIDs as primary keys instead. While GUIDs are not a magic security feature, neither is obfuscating ints. Make sure the rest of your security (SSL, logins, role-based security, hardening from X-site request forgeries, etc. etc.) is solid first, and exposing GUIDs to represent what they're designed to represent anyway (i.e. unique resources) will be the least of your problems.

10

Looking at posts such as this on StackOverflow, the consensus seems to be that you should hide incrementing ID's from external API's for security.

Whenever you're talking about security, your first question should be, "What threat am I protecting from?" So let's take a look at the threats outlined in the question you link.

  1. Sequential IDs allow an attacker to enter an ID that doesn't belong to them somewhere. If the data doesn't have proper authorization around it, they can access data they shouldn't.
  2. They make finding data easier in a SQL injection attack.
  3. You might expose some information about how much usage your site is getting.
  4. They might be able to guess a session ID (if they were sequential) and take over someone else's session.

To summarize, the central theme is that the attacker might have some idea what IDs are in your system. But this information is mostly useless by itself. An ID must be combined with another vulnerability that makes the ID usable by the attacker. Specifically,

  1. Requires vulnerability that allows unauthorized access via ID.
  2. Requires a SQL injection vulnerability.
  3. This is true broadly, but do you care?
  4. Requires lack of verification to prevent session hijacking. (This normally involves some kind of encryption technique, so sequential IDs actually should be thrown out here. But a plain text GUID would be equally vulnerable once obtained by an attacker.)

Aside from site usage statistics, all of these are much more dangerous vulnerabilities than just leaking your IDs. Ensuring that your system is free of these other gaping security holes should be a much higher priority to you than worrying about what kind of ID to use, and plugging them will make your ID a useless piece of information.

Now let's think a bit about how using GUIDs might not even help here. Attackers could gain access to even GUID IDs using

  1. Social engineering: The user is often the weakest link in security.
  2. SQL injection: If your app is vulnerable to SQL injection, it's very possible that an attacker can use this vulnerability to get the IDs anyway. (It depends on the exact details of how you introduced the vulnerability, but it's definitely a possibility you should concern yourself with.)
  3. Stolen database dump: If an attacker breaches your server and obtains a full copy of the DB, they've got all the IDs (and all the password or token data you stored and any other info in the DB).

GUIDs are fine, but security is not their primary purpose, and you shouldn't think for a moment that blindly using GUIDs is a security measure. They are at best a form of security through obscurity, and obscurity is not something your system should rely on if the information in it needs to be kept private. Focus on avoiding the vulnerabilities that make having IDs useful.

GUIDs are primarily useful as a globally unique ID, particularly when you need to store data in two different places and then combine it later. If you don't need this, then you're probably fine without GUIDs. StackOverflow doesn't use them for users, posts, or anything else after all, and this doesn't automatically expose them to vulnerabilities. If you need to use them to hide how much usage your site gets, that's reasonable. But keeping that information secret would be a business requirement that you should evaluate with whoever sets the requirements for your application, and using GUIDs probably won't be the only component to solving that problem anyway. You should decide whether to use GUIDs based on your application's requirements, not out of a vague notion of being "more secure."

0
  1. Go ahead and use GUIDs
  2. Do not use a clustered index.
  3. If you must use a clustered index, new NEWSEQUENTIALID() to generate them.

I disagree with Robert Harvey's statement that the advice against using GUIDs is dated. There are more recent posts talking about GUIDs in clustered indexes, including the SQL Server 2012 documentation, that suggests using NewSequentialID() if you must use GUIDs in a clustered index. That advice is still relevant today.

  • 1
    Using NEWSEQUENTIALID means that you have sequential IDs, defeating the point here... At that point, you may as well use integer if you don't have some other pressing reason to use GUIDs. – jpmc26 Dec 6 '16 at 0:50
  • @jpmc26 NEWSEQUENTIALID is not always sequential. It makes them somewhat more sequential, this minimizing the impact on the clustered index. It also isn't obvious to the caller that they are incrementing. Check the linked article for details. – Moby Disk Dec 7 '16 at 15:08
  • The article says it only changes when you reboot the machine, something that doesn't happen frequently in prod. Additionally, the article contains this warning: "If privacy is a concern, do not use this function. It is possible to guess the value of the next generated GUID and, therefore, access data associated with that GUID," which is exactly the point I made. – jpmc26 Dec 7 '16 at 16:50
  • @jpmc26 Touchě! Then follow the advice and don't use a clustered index. – Moby Disk Dec 8 '16 at 23:52

protected by gnat Jun 7 '17 at 5:45

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.