CWE 639: Insecure Direct Object Reference

Flaw

CWE 639: Insecure Direct Object Reference is an access control problem that allows an attacker to view data by manipulating an identifier (for example, a document or account number).

Direct object references are maps of an identifier directly to a resource; they are insecure direct object references when they allow an unauthorized user to access data. For example, a method that retrieves a record from a database to later display to a user:

@RequestMapping(value = "/records/{id}"),
    method = RequestMethod.GET,
    produces = MediaType.APPLICATION_JSON_VALUE)
@Timed

public ResponseEntity<Record> get(@PathVariable Long id) {
    log.debug("REST request to get record" {}", id);
...

So a user might visit:

http://example.org/#/records/921106108

And this will display record 921106108; the get method takes the id of a record to fetch from a URL parameter.

This example code is insecure, because it does not check to see if the authenticated user is authorized to see that record. An attacker can simply change the parameter in the URL to see any record they wish. This sort of attack is easy to automate, which can lead to massive data breaches.

Attackers can use similar flaws to learn about your sytems. For example, attackers have used this flaw enumerate users when changing a userID parameter results in either a "user not found" page or that user's profile page.

NB: insecure direct object references are not limited to URLs, though that's where they're most commonly found. Any client-controlled value -- cookies, form values, header values, etc. -- can potentially be the source of an insecure direct object reference flaw.

Fix

To fix an Insecure Direct Object Reference, you have two options. The first is to add an authorization check before displaying any information that might be useful to an attacker. For example:

method = RequestMethod.GET,
     produces = MediaType.APPLICATION_JSON_VALUE)
 @Timed
+@PreAuthorize("hasRole('ADMIN') OR hasRole('RecordOwner')")
 
 public ResponseEntity<Record> get(@PathVariable Long id) {
     log.debug("REST request to get record" {}", id);
view fixed code only

This approach is preferred, since as long as your authorization system is effective, an unauthorized user can't access data through this path, which makes things very difficult for attackers.

NB: There are many different authorization mechanisms, so beware of just using this example verbatim. Make sure you understand the authentication and authorization capabilities your application is using, and follow those patterns.

Unfortunately, there are times when information needs to be available to anonymous users, but you still don't want attackers to easily enumerate the data. In this case, you can create a level of indirection by creating a map connecting unpredictable values to the real, predictable IDs. A good way to do this is to use java.util.UUID.randomUUID() to generate Universally Unique IDs that can be mapped to your more-predictable keys. Your code might then look something like:

-@RequestMapping(value = "/records/{id}"),
+import java.util.UUID;
+...
+
+@RequestMapping(value = "/records/{safe_id}"),
     method = RequestMethod.GET,
     produces = MediaType.APPLICATION_JSON_VALUE)
 @Timed
+@PreAuthorize("hasRole('ADMIN') OR hasRole('RecordOwner')")
 
-public ResponseEntity<Record> get(@PathVariable Long id) {
+public ResponseEntity<Record> get(@PathVariable UUID safe_id) {
+    id = getRealIDforUUID(safe_id);
     log.debug("REST request to get record" {}", id);
 ...
view fixed code only

With this in place, your URLs would look something like:

http://example.org/#/records/946933e0-fab5-419b-8910-cc3d0367d95b

and the getRealIDforUUID method accepts 946933e0-fab5-419b-8910-cc3d0367d95b as its parameter, and retrieves the real id (921106108) from a key-value store, database, etc.. Because there are so many UUID possibilities, and random UUIDs aren't predictable, an attacker would have a nearly impossible task to attempt to enumerate these records.

References

CWE ↪

Ask the Community

Ask the Community