Chef Tony
Chef Tony

Reputation: 475

What's a good way to "mask" a progressive ID?

In our CRM we have a PHP page that, given a 6-digit invoice ID (e.g. 314529), fetches product rows and totals from database and prints the invoice.

To implement some smart working measures we have now made this invoice generation page public, so we can send the invoice link to clients via email, such as https://example.com/get-invoice.php?id=314529

The problem is, invoice IDs are progressive, so any customer can access any other customers' invoices simply by incrementing the number.

My idea was to generate a random UUID and map it to the actual invoice number in our database, so that customers can't "guess" other people invoices.

CREATE TABLE email_invoice (
 public_uuid VARCHAR(128) UNIQUE,
 actual_order_id INT
);

What's an efficient, secure way to implement this unique ID? Should I MD5 the invoice ID and substring it, or rely on PHP uniqid?

Upvotes: 1

Views: 658

Answers (2)

GMB
GMB

Reputation: 222582

As a starter: you should manage the authorization layer within your application; each user should be authenticated and is access rights checked when they attempt to access the stored data.

That being said, adding some obsfucation on top of that is not a bad thing either. Your use case looks like a good spot to use UUID() or UUID_SHORT().

These functions produce unique, unpredictable identifiers, which seem to be what you are looking for. You can just use an UUID column instead of the primary key.

The downside is that it is more expensive to compute that an auto-incremented key, and that there is no option to generate a default value in the insert queries.

create table email_invoice (
    id varchar(36) primary key,
    email_id int,
    invoice_id int
); 

insert into email_invoice(id, email_id, invoice_id) values(uuid(), 1, 2);

Demo on DB Fiddle:

id                                   | email_id | invoice_id
:----------------------------------- | -------: | ---------:
b86d0d8b-7022-11ea-8095-00163e561f6d |        1 |          2

You can also create a before trigger that assigns an uuid() for each and every insert, so you don't need to worry about calling the function in the statement itself.

Upvotes: 1

danblack
danblack

Reputation: 14736

Authenticate the client in the web request and verify they are authenticated before giving a result.

What you have is a insecure direct object reference. There are a number of ways to prevent this.

Recommend getting some web security expertise into the development of you CRM as this is a fairly basic mistake.

Upvotes: 1

Related Questions