This is probably a repeated question but I really can't find any answers. I have a forgotten password script which grabs the users email address from the URL using $_GET. The script then checks the $_GET for error messages then passes through to mysql to check weather the user id is the same.
Now the script itself is fine but the user id comes back as a string instead of an integer in var_dump(). I was going to let it go but then I read that if the user id is not an integer in a $_GET variable it could lead to an attack, which has got me a little bit worried. I have tried to change the num_rows value to an int but with no success, because num_rows returns an array. The code that checks the user id is:
if (mysql_num_rows($result) == 1) {
// NEED TO TURN THIS ARRAY TO AN INT
list($userId) = mysql_fetch_array($result, MYSQL_NUM);
}
//elseif (mysql_num_rows($result) <= 0) {
else {
$wrong = '<p style="color: red">Something went wrong. Please try again</p>';
}
Like I said before the script itself is fine, it's just that I read something and now can't get it out of my head, which has lead me to ask you guys. Doe's a user id have to be an integer or can i get away with it as a string?
EDIT: Thanks for all your comments and suggestions. There's still a lot to learn in php. Cheers again.
The user ID doesn't have to be an integer, however it is usually
unique (one per user)
formatted a way that it is searchable efficiently in a database (for instance)
short so that it doesn't take much space, especially for large users bank
therefore, ideally, that would be an integer (which number of bits must cover the largest number of expected users)
unique - for instance allocated automatically (incrementally) by a database
efficiently indexed, and being the small pointer to a larger user table
short
one of the drawbacks is that if the integer is given to the client browser (eg in a form), the client may guess the number of user / change the form user ID since numbers in sequence can be easily guessed... (that needs to be prevented via further security)
Everything you pull from the $_GET array is going to be a string, owing to the fact that there's no type information sent through http.
You should, however, always be sanitizing user input or using prepared statements to prevent SQL injection attacks.
You mixed things up, I'll try to clarify them. When PHP receives data via HTTP protocol, be it GET or POST - all the data is considered to be a string because there is no way to safely identify what the data should be.
That means you can try to coerce a piece of data you got via $_GET or $_POST into a different type internally, using typecasting, such as $id = (int)$_GET['id'];
As for databases, the id (let's call it Primary Key) should always be an integer.
Why:
- InnoDB uses a certain principle internally when it comes to actual data-structure organisation and writing the information to the disk. It accomplishes a significant performance gain by using sequentially incremented integers
- It's easy to create unique identifiers using integers. Just increment some number by an offset every time you do an insert and you get a unique number assigned to the row, no need for a complex algorithm that calculates unique identifier such as UUID() etc.
Long story short - keep the user's ID as integer in MySQL and if you receive data via $_POST/$_GET - type cast them into what you want them to be (int, float, string etc.)
A user ID can be anything you want it to be ... I would only suggest that it be something that has a unique key on it in the database and doesn't change over time.
ID's are traditionally used because it is good practice to have an auto-incrementing primary key on your tables anyway and it meets all of the criteria to be an effective user id.
Related
My situation: My website will look at a cookie for a remember me token and a user ID. If the cookie exists it will unhash it and look up the user ID and compare the token. with a "WHERE userid = '' and rememberme = ''".
My question is: Will MySQL optimize this query on the unique userid so that the query does not scan the entire database for this 20+ character token? Or instead should I just select the token from the database and then use a php if comparison to check if the tokens are the same?
In short (tl;dr): Would it be better to check if a token matches in with a MySQL select query, or to grab all the tokens from a databases database and compare the values with a php if conditional?
Thanks!
Simple answer:
YES, the database will definitely optimism your search AS LONG AS THE variable you are searching in the WHERE ... portion is indexed! You definitely should not retrieve all the information via SQL and then do a PHP conditional if you are worried about performance.
So if the id column in your table is not indexed, you should index it. If you have let say... 1 million rows already in your table and run a command like SELECT * FROM user WHERE id = 994321, you would see a definite increase in performance.
Elaborating:
A database (like MySQL) is made to be much faster at executing queries/commands than you would expect that to happen in php for instance. In your specific situation, lets say you are executing this SQL statement:
$sql = "SELECT * FROM users WHERE id = 4";
If you have 1 million users, and the id column is not indexed, MySQL will look through all 1 million users to find all the rows with id = 4. However, if it is indexed, there is something called a b tree that MySQL makes (behind the scenes) which works similarly to how the indexing of a dictionary work.
If you try to find the world slowly in a dictionary, you might open the book in the middle, find words that start with the letter M and then look in the middle again of the pages on your right side hoping to find a letter closer to S. This method of looking for a word is much faster than looking at each single page from the beginning 1 by 1.
For that very reason, MySQL has created indexes to help performance and this feature should definitely be taken advantage of to help increase the speed of your queries.
Comparing it on MySQL-side should be fast. It should find the corresponding row by ID first (fast) and then compare the hash (also fast, since there will be only 1 row to check).
Try analyzing the query with EXPLAIN to find out the actual execution plan.
In my opinion it will be always faster to use WHERE clause no matter what (real) database server will be used. Database engines have strong algorithms for searching data written in language that is compiling to low-level code dedicated to platform, so it cannot be even compared with some loop written in interpreted PHP.
And remember that for PHP loop you will have to send all records from DB to PHP.
If you Data Base its on a separate server than you Apache PHP there is not doubt it would be faster if you write a query in MySQL.
If your PHP and MySQL server is on the same physical server probably PHP would be faster cause the comparison will be made on the RAM But have all the User Id array into RAM would be a waste of RAM so you can use Indexes that would speed up your query
ALTER TABLE table ADD INDEX idx__tableName__fieldName (field)
I'm using mysql database auto-increment as an order ID. When I display the order ID to the user, I want to somehow mask/obfuscate it.
Why?
So at first glance, it is obvious to admin users what the number
refers to (orders start with 10, customers start with 20 etc)
To hide, at first glance, that this is only my 4th order.
Based on this this answer, I want the masked/obfuscated order id to:
Be only numbers
Consistent length (if possible)
Not cause collisions
Be reversible so I can decode it and get the original ID
How would I acheive this in PHP? It doesn't have to be very complex, just so at first glance it's not obvious.
I think you can use XOR operator to hide "at first glance" for example (MySQL example):
(id*121) ^ 2342323
Where 2342323 and 121 are "magic" numbers - templates for the order number.
To reverse:
(OrderNum ^ 2342323)/121
Additional advantage in this case - you can validate OrderNumber (to avoid spam or something like this in online form) if (OrderNum ^ 2342323) is divided by 121 with no remainder.
SQLFiddle demo
A little bit late, but Optimus (https://github.com/jenssegers/optimus) does exactly what is here asked for.
$encoded = $optimus->encode(20); // 1535832388
$original = $optimus->decode(1535832388); // 20
Only the initial setup is a bit weird (generate primenumbers)
Probably the simplest way is to just generate a long random string and use it instead of the auto-increment ID. Or maybe use it alongside the auto-increment ID. If the string is long enough and random enough, it will be unique for every record (think of GUIDs). Then you can display these to the user and not worry about anything.
Can it help?
echo hexdec(uniqid());
Off course you should store this value at db, at the same row with order id.
Just converting a ID into something like HEX might not give you the result what you like. Moreover its still easy 'guessable'
I would a a extra ID column (i.e. order_id). Set a unqi. index. Then on_creation use one of the following mysql functions:
SHA1(contcat('ORDER', id))
MD5(contcat('ORDER', id))
SHA1(contcat('ORDER', id, customer_id))
MD5(contcat('ORDER', id, customer_id))
UUID()
// try this in your mysql console
SELECT UUID(), SHA(CONCAT('ORDER',10)), SHA1(1);
You could (as in the example), add a simple text prefix like 'order'. Or even combine them. However i think UUID() would be easiest.
Implementation depends a bit on what you prefer you could use a stored procedure) or incorporate it in your model.
i made a small code that generates different type of code, but i'll make it simpler,
i have a registration form submitted while submitting i collect some info about the user and i create for him a random, but i want this random to be unique for this user.
so i have 3 cases :
$code_random = rand(1000,9999);
if($code_random < 0){
$code_random = -$code_random;
}
$random = $fname.$code_random; //case 1
$random = $lname.$code_random; //case 2
$random = $fname.lname.$code_random; //case 3
But i want to create case 1 check if this random exist in the database, if it does use the second case if it does use the third case, before submitting the form and without displaying anything for the user.
Thanks for your help in advance.
Don't reinvent the wheel - SQL databases have two great ways of assigning unique IDs to every row.
1) Auto-incrementing primary key - goes up by one for every new row. Managed by the database, guaranteed to not use the same value for two rows by mistake. Nice and small and simple. http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
2) GUIDs (also known as UUIDs) - The algorithm used to generate GUIDs means that you'll never see the same one twice, ever. Over auto-incrementing integers, they have the advantage of being unpredictable, being generateable outside of the database and being meaningful outside of their database table context. http://www.php.net/manual/en/function.uniqid.php#94959 http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_uuid
If you really want to use a random integer, you can use the MySQL - rand() function for this
insert into users (id, ...) values (FLOOR(1000 + RAND() * (9999 – 1000)), ...)
There is a simple way of making infos unique: Create a unique index on the database column.
Then simply insert what you want and check if the database complains about violating the unique index. If this is the case, use one of your alternative queries and check again.
What if the last query still does not work?
Is there any way I could make my model ID (primary key) generated into random unique 8 digits containing only numbers instead of the default auto increment?
A client requested this specific 8-digits-number-only feature, so I can't argue much about the reasons.
I want to use the PHP uniqid but it's 13 digits and contains alphabets as well.
Any idea?
Thanks.
Update
I forgot to tell that I need the ID randomly generated each new record being saved.
Just want to ask the mechanism on generating the ID and then saving the ID (also the attributes). Do I have to check the database first for the randomly generated ID whether another same key already exists and then save the attributes or what?
Why dont you keep the auto increment but set it to start from 10000000 on your primary key instead of 1?
ALTER TABLE some_table AUTO_INCREMENT=10000000
Yes you can. I assume you are on MySQL, when talking about AI. Just do not set it as auto increment and insert the value as for the other columns. You can create a function or method, which will take up to 8 numbers randomly or in specific order (algorithm).
INSERT INTO model (id, name, value, etc) VALUES (87654321, 'My selected name', 'some price or text', 'etc').
Consider that INT(11) value may accept from -2147483648 to 2147483647. Which will fit you for numbers with 8 digits. If at later time the client request bigger numbers you may need to switch to BIGINT.
I use to set the Primary Keys as unsigned, which allows you to fit numbers between 0 and 4294967295.
For php function - generator of 8 digits:
<?php
mt_srand();
$id = mt_rand(10000000, 99999999);
?>
You can read more about mt_srand() and mt_rand() on php documentation. It is said that they are better than the srand() and rand().
Keep the ID, but pad it.
$id = 6;
$padded_id = sprintf("%013d", $id);
// This will print 0000000000006
That'll pad the $id so that it's 13 digits long.
Every time you need to display the ID use a function to convert it, like this.
function padId($id){
return sprintf("%013d", $id);
}
Or you could make a row in your table called pad-id, then run this function when you create a record (along with mysql_insert_id (to get the ID just inserted)).
The best approach depends on a subtle aspect of your client's randomness requirement --
When they say random do they mean completely unpredictable or just hard to predict? I don't mean to sound like Clinton at the Lewinski trial, but what your client intends when they say random affects whether it will even be possible for you to meet the requirement.
If the client wants to hide user IDs (for some perceived security benefit) and make them virtually impossible to predict or reverse-engineer, then that is very difficult. If the client would be satisfied with just "hard" to predict (which I suspect), then you can do something simple, similar to the md5 approach (#Dotty). But md5 is not collision resistant. And even with the best, provably unique hash algorithms (which md5 is not), you'll have a collision problem if the number of users is large compared to the number of digits you are allowed for user ID's (8). You have about 27 bits to work with in the 8 decimal digits allowed. Which means you're likely to get a collision after 2^N/2 = 2^(27/2) which is about 10K users. So if your client's user list approaches 10K users, then even the best hash algorithm will spend a lot of time filtering out all the collisions.
To solve this without filters and nondeterministic algorithms, just use a simple "Full Cycle" algorithm. Some will produce pseudo-random numbers (PRNs) that are guaranteed to be unique and guaranteed to fully span whatever range you're trying to cover (e.g., the set of all 8-digit positive integers). And if you ever need to reverse engineer the user registration sequence just rerun the full cycle PRN generator again with whatever initial value you used. And you can keep this initial value a secret, like a private key, if your client wants to make it slightly more difficult than easy for a hacker to reverse-engineer your user ID sequence.
Another question for your client is whether leading zeros are allowed in the user id. If so, (and the client's randomness requirements are liberal) then the simple Full Cycle algorithm on Wikipedia will work nicely for you. It could be distilled to 2 lines of PHP.
Whatever algorithm you use, it might be good to actually generate the list of official 8-digit semi-random user IDs in a separate table, and then just "pop" the value from the top of the table (deleting that row) whenever you add a new user. The database memory requirements shouldn't be prohibitive and it will streamline the user experience, eliminating any delays and memory gobbling caused by sophisticated, nondeterministic, random number generators and uniqueness filters. Trying to create the user ID online, live, it's conceivable you could get into a perpetual loop with some hash algorithms stalling your user registration indefinitely. And this stall (due to perpetual collision) might not occur until user 1000 or 10000. In contrast, with the offline lookup table approach, you can easily add additional client-prescribed filters like eliminating IDs with leading zeros; in case the client never wants to see a user with the ID 1 (00000001). And you'd know in advance whether everything is going to always work, without any hangs.
i've got hundreds of people joining my website and create an membership id for them. i just created a new column in the database called user_no.
whats the mysql query for incrementing the membership no.
is it possible to start with AE then numbers ie: AE0001, AE0002, .... and it starts with 4 number not AE1, AE2..
mysql query:
UPDATE user SET user_no=..??
and on PHP side, how do i increment it? if there is a new member join in.
$db->query("INSERT INTO user (user_no) VALUES(AE'$user_no')");
Why not use an autoincrement field and append AE to it? Autoincrement will be carried out by MySQL so you don't have to worry about it in PHP : http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
And to display your user key in the format of AE0001 you can do
$id = 'AE' . str_pad($autoincrementid, 5, "0", STR_PAD_LEFT);
http://php.net/manual/en/function.str-pad.php
I agree with everyone else who is saying that you should simply use the MySQL Auto-increment feature. That's what it's there for.
It is possible to write your own, possibly using MySQL's MAX() function to find the highest value of a field currently in the table. However unless you're using some very robust transactional code, there is always the danger that this method will result in duplicate records being created when two users create accounts at exactly the same time.
The amount of code required to avoid this is not small, and if you're inexperienced enough not to see the benefits of using Auto-increment then you're unlikely to get it right.
The whole point of Auto-increment is to save you from having to implement all that code every time.
In addition, it is highly recommended for performance reasons to use an integer value for your primary key. Sure, you can display it as "AE" . $id, but you should store it as an integer on the database.