database - How to "secure" database against "guessing" records - php

I have a small dilemma, namely how to secure data in the database before "guessing" their ID, eg we have an address:
http://example.com/users/34/edit
After that one can conclude that 35 will be another user and someone may try to guess the entry. So how is it best to protect yourself from something like that?
I was thinking about replacing the PK with INT AI on UUID (generated on the application side), but will not it significantly reduce the efficiency?
The estimated number of entries is about 12,000, additionally after this ID will be linked, for example, the profile.
What do you think about it?

Go for the UUID. 12,000 records will not have any significant impact on performance regardless of how you generate the primary key. As a bonus, if you ever want to do sharding or any kind of distributed system you will quickly find that UUIDs are the only way to maintain such systems.

Other options (besides using UUID as the key):
Create a random number or UUID in addition to (not instead of) the ID. Store this in the DB as a column in the same table. Then generate URLs like http://example.com/users/34/{UUID or random number}/edit. This will allow edit only if the UUID or random number matches the one in the DB.
If you do not want to store an extra column you can encrypt the id and have the URL like http://example.com/users/{encryptedId}/edit. During edit you would decrypt the id, and edit the decrypted id. Encrypted Id can be also be used in addition to the ID - so you would validate that the decrypted id is the same as id in the DB, corresponding to the id, before allowing edit.
Or use another column from table in the URL e.g. http://example.com/users/34/{LastName}/edit and allow edit only if the last name matches the existing data in the DB corresponding to the id.
Or if you can simply have a session or HTTP Basic authentication on the server. Almost all browsers support Basic authentication (https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#Basic_authentication_scheme) - they will prompt you for userid/password when you enter http://example.com/users/34/edit. Or, you can have the URL like http://userid:password#example.com/users/34/edit

Related

What chances are to have token collision/duplication during token generation upon multi-server php app?

I have a table containing urls that a user can visit:
id PK AUTOINCREMENT
token VARCHAR (8)
user_id_visited INTEGER
visited BOOLEAN
The url is in the form of https://example.com/mypage/^token^ where ^token^ is retrieved from ^token^ field above. What I want to achive it to block a user having user_id_visited revisit a page having the very same token.
I generate the token like this:
$token = bin2hex(random_bytes(5));
// Insert token in the database here
But my application is in 2 webserver instances writing in the same database. What I want to avoid generating the same token hence an idea is to use the server's hostname:
$token = gethostname()."_".bin2hex(random_bytes(5));
So my question is if I avoid the usage of gethostname what chances are to generate duplicate tokens using only the random_bytes?
Based on this thread, probably with random_bytes(5) you could have something close to 2^20 possible unique tokens, which is translated to 1048576 tokens.
Thus, depending on the amount of entries you are going to have in your DB, the probability to have duplicates is quite big.
Maybe the best possible solution, is to modify your DB, to make it accept UUIDs, and generate UUIDs instead which has better uniqueness rates touching the 2^122 possible unique IDs.

Collision free, short and unique Integer-only Hashes based on known values

I am trying to get started with web development by creating a Hotel Reservation System for practice and I have the following problem. I need to create two short, unique, collision free and Integer-only hashes as I have the following requirement:
The number has to be long enough to be believable by a human as it will be sent to them via email as their Booking Code but also short enough to be usable by a human as a 128-Bit Booking Code is weird and it would also be a primary key (unique identifier) for the Bookings Table and Customers Table in the database.
So I need one BookingId and one CustomersID. I know, I could use increment in the MySQL database but I wish to go the rougher route here for learning purposes.
Almost all the Hash functions create longer Hashes with digits and alphabets. But I wish the Hashes to be short and Integer only.
The entire Hash logic would execute in a PHP script right before inserting all the details into the DB using the awesome PDO functionality.
Now, this is the situation:
Each Customer has one unique email address which I know as they insert it in the Booking Form, so I could base the CustomerID on the unique email address. One Customer can only have one CustomerID but he can have multiple unique BookingIDs.
So the CustomerID can be based on the Email Address and the BookingID could be based on the Email and the timestamp.
The difficulty here is to find a Hash function which would result in a 6 Digit collision free Integer only Hash.
How can I generate these two short, unique, collision-free and Integer-only Hashes using PHP?
Rather then storing your "random ids" in your database, I would just work with a classic auto increment primary key. To the outside world you can encode these id's to make them look like the long integers you desire. And before querying or storing anything in your database you just decode them back to the actual primary key.
As for your customers table, email sounds like a valid option for the primary key, but what if the user decides to change his email address? Or what if you want to support multiple email addresses in the future? I would go for a numeric id for the customers as well, as it is the easy and future proof solution imo.
No need to re-invent the wheel either, I usually use this small library for this:
https://github.com/ivanakimov/hashids.php
Don't let the name fool you by the way, this is strictly speaking an Encrypter, and not a Hasher, since it works in two directions.
It allows you to set a custom alphabet, so your requirement to use only numeric values shouldn't be a problem. Your code would look something like this:
$hasher = new Hashids(MY_APP_SALT, 6, '0123456789');
$hashId = $hasher->encode($idFromDb);
$id = $hasher->decode($idFromRequest);
Update:
To elaborate on your questions in the comments, you could also do
$customerId = $hasher->encode($email);
And indeed, as long as the parameters you provide on construct of the $hasher you'll results remain the same.
The parameters for the constructor are, in order:
salt, I would put this in a constant or some sort of config value or environment variable, so you can maintain the actual value in a single location
minimum number of characters in the resulting hash. In my experience the actual hash remains quite close to that.
the allowed characters in the resulting hash.
By the way, I just read about an alternative library in the docs for if you only want to work with numeric values. No experience with that one, but it may be an even better fit.

In a news feed, is it secure if the id of post is visible to user?

I am creating news feed in my website. I want to capture likes and comments on a particular post. My basic implementation is, I provide post id as an ID to 'like' button which will call a method to add an entry in post_like mapping table.
But the method/webservice and the ID of post will be visible to end user in source code. which may lead phishing attack etc. How can I secure such data so that user can not access any other post.
If you want to avoid link forgery to control access to other posts you need some type of hash, not an ID, some solutions could be:
use the same trick calling cards and other serialized cards do, the card number is the concatenation of an id with a fixed amount of digits and a random generated password which also has a fixed amount of digits.
create a method to generate a unique hash and store it as a field in the same table of the the posts, then use this as a reference instead of the id.
If your IDs aren't guessable (i.e., if they are randomly assigned from a large enough number space that only a tiny fraction of that number space be valid IDs at any given time) you should be fine. Sequential IDs can of course easily be guessed simply by observing one.
Using random (i.e. 'Version 4') UUIDs should suffice. (And equivalently, any random value with at least 122 bit of entropy will, too.)
You might want to also look into the related topic of cross site request forgery prevention.
You should properly handle post view request .
In your PHP check every time post is viewed separately that the post is shared_with a particular or non login users.
But that will be a complex users basically you can also use long sha1 or md5 encryption and store that hash in a separately column and you can also update hash timely using crone jobs. As a user can't guess other posts hash by using one post's hash so according to your current implementations hashing user_id and storing them in db is nice idea and you can also set the hash column unique in your db table. No decryption of hash is required as they are not decrypt-able and you can directly compare them in where cluase while processing like and comment request.

Minimum information to authenticate a user

I have a table who's PK is INT type (roughly 4 billion possible unsigned values, however, I will have at most 2 million). The table also has a CHAR(32) column which contains a random value (created using bin2hex(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM))), as well as a FK column indicated the userID.
I will send out some emails which will contain links in them, and the links will contain the above table's PK and/or random value, as well as potentially the userID. The links will also contain an answer (yes, no, etc). For instance, a link might look like:
Yes
I have a PHP page which will accept a GET request (initiated from the above mentioned emails of course) and update the appropriate record in the database with the provided answer if authenticated.
Is solely confirming the 16 byte random value exists in the database enough to authenticate that it came from the user who received the email? If not, why not and what would you recommend?
I would also use the user id as it is faster.
Your url will become: ckr.php?userid=1&rsp=eae8a14011e82cbf385f69b431a17e49&ans=yes
Then, when you check in database, you only check for userid. If the user exists, then check for the rsp code. If everthing ok, do your job.
Keep in mind, everything send over e-mail is unencrypted an thus not suitable for authenticating a user.
Some things you could do to make it a bit more secure:
Make the key only available for a short timespan. Maybe a 24h window?
Ask for the users password when receiving the get request.
Invalidate the key when an answer is received.

Database/Server Design (Cookies and Indexes) - Ints or Text?

This is a design question relating to a website I'm building.
I have a 'Player' table that will store names, passwords, last IP, dates of birth, links to avatars, locations, etc.
When the player logs in, the database will be searched for the username they entered, and their password will be checked out as well (yes, of course the passwords are hashed). Then, they will be given some cookie that will keep them logged in.
Every time they visit a new page, the correct player will be looked up in the database using the information in the cookie. If their current IP matches the last IP they logged in with (probably 10 seconds ago), the page is outputted with their name on it and whatnot.
Here's my question: should I have the primary key for the Player table be the player's name (a text field that I know will be unique), or should I create some arbitrary auto-incremented index for that?
Keep in mind that this also has an effect on the information stored in the cookie - whether to store an int or the user's name in text. As well, I want to do some sort of hashing on that value (just for a little added security), so that the cookie doesn't just contain the int or the username.
So, in terms of both efficiency and design, which is the better choice?
EDIT Using VARCHAR for the database would also be ok, and probably faster, I imagine.
EDIT2 This primary key will also be referenced by other tables.
As Marc's comment indicates, int will be more efficient for both memory and performance.
I'd recommend against tying logins to IP addresses, some users will have each request to the server come from a different IP address (onion routers, AOL, who knows what other kind of weird corporate NATs), and being logged out all the time will be super annoying.
You may also want to consider using sessions instead of setting a cookie saying who they are logged in as. Even though having a sig would make it a bit more secure, using sessions would be safer still, along with giving you more flexibility to track information about users before they log in (for example, what page they should be redirected to after a successful login).
Here's my question: should I have the primary key for the Player table be the player's name (a text field that I know will be unique), or should I create some arbitrary auto-incremented index for that?
The latter.
Names tend to change.
whether to store an int or the user's name in text.
As well, I want to do some sort of hashing on that value
Please make your mind first, then ask
is it going to be hash or a plain value finally? If the latter - what's the difference then?
So, in terms of both efficiency and design, which is the better choice?
Oh. This one absolutely doesn't matter.

Categories