How to make this login/redirect method more secure - javascript

I have an application which sends e-mails to users, each e-mail containing a link to a page the user must access.
The link is an md5 of an unique id + a random number.
The link looks like: www.domain.com/index.php?id_page=<the md5>
On the index page i save the $_GET["id_page"] within a session variable, $_SESSION["id_page"] and I redirect the user to the page which he must see (the user is redirected to that page only if the link contains id_page).
How can I improve this method and make it more secure? How can I guarantee that the users enter the page designated to them - and cannot enter any other page?

What you are concerned with here is a matter of time, rather than a matter of security :). If you allow anyone to guess an infinite number of id_page values, then given enough time, eventually someone will happen upon a random valid id_page value.
Your only real defense against this is to increase the length of your hash, causing it to take (on average) more time to happen upon a random valid id_page (on the order of months or years). This could be accomplished by using sha256 or sha512 rather than md5.
Another approach is to lock someone out for a period of time if they have, for example, 3 consecutive incorrect guesses at an id_page value. This will greatly decrease the number of values they can attempt in a given period of time.
Lastly, If the user is already logged in at the time of redirect then you could also store the hashes you generate in a database table. That way you can map a particular hash to one and only one userid in the table. If a user attempts to visit a hash page to which they don't correspond in the DB, then you could redirect them elsewhere.

You can add their email in the URL. the probability of someone guessing someone else's email and the associated hash is just about 0.

One method that can work quite well for preventing guessing ID numbers is to add some sort of padding to the ID and then convert it to base32. Of course, this doesn't eliminate the ability to guess an ID entirely, but it does make it a little more time consuming for anyone who is snooping around.
If you have the URL www.domain.com/index.php?id_page=1, you could convert the id to something unique in your application, for example:
padded id = id x 9 - 2
7 = 1 x 9 - 2
16 = 2 x 9 -2
25 = 3 x 9 - 2
Then, you can convert the new padded ID to base 32, which would be
7 = G4
16 = GE3A
25 = GI2Q
The new url would then be (for an id of 1):
www.domain.com/index.php?id_page=G4
Using this method, if someone guesses the base 32 of 1-6, it would return a 404, because your ID of 1 is actually being padded out to become 7. Guessing 8-15 wouldn't return a parsed ID because the next id of 2 is padded to 16, and so on.
Not only does this keep the query string smaller in size, but it also doesn't use an obvious MD5 hash which can very easily be sequentially browsed using dictionaries.
If you want the page to be linked to a specific users, well there's no reason why you cannot append more values to the new padded_id (let's call it a hash).
For instance, if you have a user with an ID of 12, and you only want that user to be able to visit a page with an id of 10, you would need to create a hash that comprises of both these values:
page_id(10)-user_id(12), using the above example, this would produce:
(10 x 9 - 2) (12 x 9 - 2)
88-106
HA4A-GEYDM
You now have a nice small hashed link that can be secured to a single user ID. The padding method in the example above is rather simple, but it gives you the overall idea of how to approach the issue.

Don't bother hashing or creating unique ids or anything. Complexity is not going to help you.
Instead, simply generate a random token, and use that. A 256 bit random token should be sufficient for anything you need to do.
So, using mcrypt (a core extension):
$token = strtr(
base64_encode(mcrypt_create_iv(256/8, MCRYPT_DEV_URANDOM)),
'+/',
'-_'
);
That will give you a 44 character result of the alphabet a-zA-Z0-9-_ which contains 256 bits of random entropy.
There's no need to hash the result or anything. The random data is enough.
To understand why, you need to understand The Birthday Problem.
Basically with a 256 bit random value, to have a 1% chance that 2 tokens collide you would need to generate 4.8e37 tokens (that's 48 followed by 36 0's).
To get a 10e-18 chance of collision (which is typically seen as secure) you'd need to generate 4.8e29 tokens.
Since those numbers are WAY more than you'd ever generate, the chance of 2 tokens colliding is infinitesimally small.
The other problem is people guessing the token. Well, MCRYPT_DEV_URANDOM uses the underlying operating system's random pool. Which means that people are way more likely to guess your "unique id and random number" than they are to guess the token generated here.
So, in short, just use a random token and be done :-)

Related

Why does a number get its last 2 digits turned to 0?

Use
Hi, so I was working on my discord bot with some user ids that I stored in a database, which then I will get back to ping them or give them roles.
Problem
Though here's the problem, in the database they get turned from ex: 533692905387196429 to 533692905387196400. I tried setting that to a String, and it worked, in the database, it's stored fully how it's supposed to be. But, when I get them back from the database and turn them into a number or an integer they get turned back to ex: 533692905387196400.
Tried using
I tried using parseInt(), parseFloat() and Number() but all of them give the same result.
More info
Also if I remove 2 digits for example: 533692905387196429I remove 19 from there, it will give back 533692905387(19)6429 instead of 533692905387196429 and instead of 533692905387196400.
Any help is appreciated!
So, Number.MAX_SAFE_INTEGER is 9007199254740991 (which is 253 - 1). Your number is too large to hold full precision.
If you want a number with that level of digits and precision, you can use BigInt and then you will have to be very careful how you use that BigInt as it cannot be directly used in place of a regular Number type, but it can hold infinite precision and you can do math between two BigInt values.
Whether it's best used as a BigInt or as a String really depends upon what you're doing with it. If these are just some sort of ID that you aren't actually doing numeric operations with, then you can just keep it as a string.
The number is too big, so Javascript doesn't keep full precision!
> 533692905387196429
533692905387196400
You can resolve this by storing them as strings:
> '533692905387196429'
'533692905387196429'
You shouldn't need to do any mathematical operations with Discord IDs so there shouldn't be any issue storing and treating them as strings everywhere.

How do I randomly generate non-repeating numbers even after page reload?

Question: How do I generate non-repeating numbers even after page reload? (Example: 0 -> 100, I would get 10 once, and never again until seeing every other number.)
Preferred Answer: Code block or explanation showing how to generate non-repeating numbers that won't repeat after reloading the page.
The easiest way is to save the 'seen' numbers in a form of an object in a cookie or localStorage, for example like that:
const seenNumbers = {
51: true,
64: true
}
Then every time you load the page, you load this array and try to generate a new number. Before using it, you check whether it is in seenNumbers and if it is, you try to generate a new one until you get a new number that was not used before. After that, you add it to the seenNumbers and save the cookie.
Don't forget to have the logic in case of seenNumbers have all the numbers, then your code will try to generate new items forever. To avoid that, first check the number of items in seenNumbers, and if it equals the number of possible numbers, you do not generate any numbers.

What is the best way to make a prompt into an integer in JS? [duplicate]

This question already has answers here:
How to get numeric value from a prompt box? [duplicate]
(6 answers)
Closed 9 months ago.
I am writing a program to perform "Russian Math" (using the numberphile youtube video on it as my basis for the algorithm). It works. But, to "prove" that it works, I'm giving the user the ability to try using their own numbers as input.
When I assign numbers to the variables myself, it works without fail. However, when I use prompt var numberOne = prompt('What is the first number you want to multiply?'); on one variable (with the other being assigned myself)it works. But as soon as I prompt the user for both numbers it won't work. Presumably because a string can be converted to an integer when an operation is performed on it (multiplied by an integer), but it does not seem to work when both are strings.
Adding another line to reset the prompt variable to an integer using parse seems like too much extra.
var numberOne = prompt('What is the first number you want to multiply?');
var numberTwo = prompt('What is the second number you want to multiply?');
var numberOneInt = parseInt(numberOne);
var numberTwoInt = parseInt(numberTwo);
Is this really the best way to do it?
For a prompt, yes, that is pretty much what you want to do. Prompt returns a string, so you need to convert it. There are other ways, i.e. Number(numberOne), but parseInt is fine. They have slightly different behaviors, but for your case they're mostly the same. (parseInt stops parsing at the first non-number, while Number type-casting attempts to convert the whole thing).
And kudos for figuring out the edge behavior of having one string and one int multiplied together.
In general, most developers prefer using inputs on the page rather than prompts. The problem with prompts is that they interrupt the user's control of the page. As a bonus, with inputs you can set type=number to give the users number controls on some devices and limit the input to actual numbers.
Edit
I don't ever use prompt, so I was reading up on them. One thing to look out for is if the user hits escape, it returns null, which may break your code. You could prevent that by just checking for it first, i.e. if(numberOne){ ... }

Regex for finding all the numbers between any two numeric values (range) in JavaScript [duplicate]

This question already has answers here:
How to match numbers between X and Y with regexp?
(7 answers)
Closed 7 years ago.
First of all, i know Regular expressions isn't the best tool to achieve what I want here. I have done enough research to know that bit. Still, The problem I am stuck in requires me to make up a regex to find the values between some lower and upper bound values.
So here is the problem, I have a large set of data, let's say ranging between 1 and 1000000. That data is not under my direct control, I cannot manipulate the data directly. Only way of finding out (searching) some values from that data is regex.. Now, the user can give two values, a minimum value and a maximum value and I need to construct a regex based on these two values and then query the large data set using the regex to get all the values lying between the set range. So, if my data contains [1,5,7,9,15,30,45,87] and user sets the range min:10, max:40. The regex should filter out values 15, 30.
From whatever I have searched, I know it is very much possible to build a regex for finding out values between fixed values (if we know them beforehand) for example, values between 1 to 100 can be found by:
^(100|[1-9][0-9]?)$
But what gets so tricky about my problem is that the input range can be anything from pretty much 1 digit values to up to 10 digit values. 10000-550000 can be an example user input for a large data set.
I know this will require some complex logic and loops involved on the basis of number of digits in the lower bound and number of digits in the upper bound of the range and then some recursive or other magical logic to build a regex that covers all the number lying in that range.
I've been filling up pages to come up with a logic but I'm afraid it surpasses my knowledge of regex. If anyone has ever done something like this before or try to point me in the right direction or attempt it him/herself - it'll be quite helpful. Thanks.
The language I will be using this in is JavaScript and I read somewhere that JS doesn't support conditional regex, keeping that in mind, solution doesn't have to be in specific to a language.
If your task is to get numbers between min and max value from the dataset, you can try filter method.
Var filteredResults = Dataset.filter(function(item){
If(item < max && item > min)
Return item
}
)

Select random combinations of list elements such that no list element is present in more than one selection (SQL)

I'm running a bit out of ideas how to realize a small project.
What I have:
- a list of users including their ID and name
What I want to achieve:
- I want to combine each user on this list with another user such that no user is assigned to more than one user and no user is assigned to herself.
- The combination has to be random and has to take past combinations into account
My idea so far:
- I have this information:
User (A,B,C,D) (the actual number of users ranges between 50 and 400)
Possible combinations: (A-B,A-C,A-D,B-C,B-D,C-D)
Random draw(1): (A-B, C-D)
Random draw(2): (A-D, B-C)
Random draw(3): (A-C, B-D)
I was able to get all possible combinations using a join of the user table with itself.
I guess I can take previous draws into account by storing the draws in a separate table and limit the possible combinations to those that are not in this special table.
What I can't do:
- I don't know how to randomly draw from the list of possible combinations such that every user is part of only one combination per draw (e.g. A-B,A-D in the same draw is not allowed)
- I try to use sql or a bit php for this (maybe javascript)
Thanks for any help.
An easy solution:
Create a temporary table with a row for each pairing. Loop over the list of users skipping a random number of empty rows from 1 to the number of empty rows -- insert user.
Easy solution number 2:
Given N users. Assign each user a unique random number from 1 to N (remove randomly from the set of all numbers from 1 to N). Pair each user with from 1-N/2 with user from N/2+1 to N.
The solution is the problem "Bergr (s) table", see Wikipedia: http://en.wikipedia.org/wiki/Round-robin_tournament.
The latest (best) solution is from Professor FronĨeka (Dalibor Froncek, a professor at the University of Minnesota in the US).
For custom solutions, look in the table of solutions n ^ 2.

Categories

Resources