I'm trying to make a url shortener for a client. I've got a decent algorithm going right now, the only problem is that if the client was to shorten the same URL for a different promotion it would create the same code.
What can I do to prevent that?
$hash = sha1($this->data[$this->alias]['us_url']);
$this->data[$this->alias]['shortid'] = base_convert(hexdec($hash), 10, 32);
I'd like to be able to create multiple shortcodes for the same url to track it differently.
Same procedure as when creating password hashes: Use some salt.
$hash = sha1($randomly_generated_salt . $my_url);
Thus, same input strings will create different hashes.
The salt should have a decent length to provide enough entropy.
(Although you wouldn't use sha1 to hash passwords!)
Add the possibility to give a custom url. In pseudocode:
if(isset($customUrl) && isUniqueLabel($customUrl)) {
createCustomUrl()
} else {
createUsualUrl()
}
Given the custom URL the client has the possibility to define the url manually (as is.gd and many other services are doing) and this will increase the possibility of customization.
Related
I'm raising the security levels of a software I'm working on.
The goal, as stated in the title, is to prevent CSRF attacks.
In a scenario that involves the use of the backend by a user who has to manage a control panel, let's take the example that the user must initialize a command that deletes a data from the database, obviously if it were used:
Delete Post
it would be an announced suicide.
By reading the PHP documentation (https://www.php.net/manual/en/function.random-bytes.php)
I found this that interested me a lot but it also made me ask questions.
If you implement the code in it:
function RandomToken($length = 32){
if(!isset($length) || intval($length) <= 8 ){
$length = 32;
}
if (function_exists('random_bytes')) {
return bin2hex(random_bytes($length));
}
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));
}
if (function_exists('openssl_random_pseudo_bytes')) {
return bin2hex(openssl_random_pseudo_bytes($length));
}
}
function Salt(){
return substr(strtr(base64_encode(hex2bin(RandomToken(32))), '+', '.'), 0, 44);
}
$token = (RandomToken())."\n".Salt()."\n";
we will get such a thing that every time the page is refreshed it will change:
13356ac7fc5e058b61bbad693d84ca2e1d9ae584db356dfa928098800d46ed6d F0ToG948CsaUF2wGDSdt.DuyUMKY1VC/liEAyjTB6ME=
Which is good ...
but:
If you choose the way of storing codes in the db and check that the generated code is new and never used then I validate the query statements for the elimination, who guarantees me that a user with bad intentions cannot generate the same 108-character code that maybe it has never been used?
So a solution of a unique time code would be better?
How to solve this?
Regarding your questions:
If you choose the way of storing codes in the db (...)
Why store the codes in the DB? Store them in the users session. You can have one anti-CSRF token for the whole session and it makes handling of the token easier.
who guarantees me that a user with bad intentions cannot generate the same 108-character code that maybe it has never been used?
Math. 32 byte random variable has an entropy of 256 bits. 128 bits would be sufficient to prevent a successful brute force attack and this is way above this.
So a solution of a unique time code would be better?
No. You already have all you need. And you don't need salt for the purpose of token creation too.
My old PHP app has a default admin user and md5 encrypted password created by the SQL that creates the database: insert into users values ( 1, 'admin', MD5('changeMe'), 2 );
Is there a simple way to include a default user and encrypted password using PHP's passowrd_hash function on creating the tables? I ask because I understand that password_hash is a native PHP function and I assume it won't be understood in SQL.
The solution to my problem came in three parts. My OP sought a simple way to create a hashed password for the admin user for insertion in the MySQL database on the installation of the application, using the native PHP password_hash() function.
(1) Based on a suggestion by #Nick and #Tadman, I decided to incorporate setting the hash in an installer script that would set not only the hash but other defined site/application variables.
Rather than inserting user values when the database table is created, it was deferred until immediately after, with the admin user entering their credentials in the form that inserts the hash and writes other definitions to a file:
$userpass = $_POST['userpass'];
echo password_hash($userpass, PASSWORD_BCRYPT);
(2) The second part of my problem was replacing all instances of md5()`` withpassword_hash()` and I achieved that by using a neat PHP script I found online to recursively search and replace the occurrences on the server.
Having replaced the md5() occurrences, I needed to change the hash comparison method and again by searching the relevant files I was able to replace instances of:
if ($p != $theUser->pwd ) {
return( false ); }
with:
if(password_verify($p, $theUser->pwd)) {
// Success!
}
else {
// Invalid credentials
echo "Uh oh!";
}
(3) The third step in resolving the problem was discovering that adding $1$ to the opening of the md5 hash could make it readable by password_hash(); so I just needed to make a couple of adjustments in the installed database to the admin user's old password.
Thanks to those who helped shine the light so I could find my way. I'm off now to invent the wheel and sliced bread.
you can do something like this in php:
$hash = password_hash('changeMe');
//echo $hash;
then use this hash in the Database.
I've been asked to enable SHA256 for storing wordpress passwords.
I've searched for plugins with no luck (not working), so I started to develop my own.
I first thoug.. well if I replace the wp_hash_password with my own function, It would encrypt when saving password and loging. But I wasn't that lucky. I'm able to run hash(sha256) though in a basic php file. I'm aware that users wont' be able to login as the stored key would be md5 and the comparation would be SHA, but it isn't a problem.
Code:
if(!function_exists('wp_hash_password')):
function wp_hash_password($password){
return hash('sha256', $password);
}
endif;
So I guess I'll have to make my own "check login" function.
Did someone did something like this?¿
Seems to me that your approach should work if you override the wp_check_password function as well. That'll have to be done in a plugin, I think, as the functions are loaded before the theme's functions.php. Something like this:
<?php
/*
Plugin Name: sh256pass
Version: 1.0
*/
if(!function_exists('wp_hash_password')):
function wp_hash_password($password){
return hash('sha256', $password);
}
endif;
if(!function_exists('wp_check_password')):
function wp_check_password($password, $hash, $user_id = '') {
// You might want to apply the check_password filter here
return wp_hash_password($password) == $hash;
}
endif;
Note that you'll either have to have your users reset their password on their next login (you won't be able to convert the existing passwords automatically), or you'll have to follow WordPress's approach in wp_check_password and compare the password to the old encrypted value (in their case md5), and if that matches, update to the new value.
Keep in mind that the wp_users.user_pass field is only 64 characters long. While that's (just) long enough to store the sha256 value, it isn't long enough to store the sha256 value and a salt. If you don't salt, and two users choose the same password, the wp_users.user_pass field will contain the same value, making it obvious to anyone with access to the database that the passwords are the same. My gut feel is that that is a greater security risk than using the current algorithm. You might be able to get around that by (say) concatenating the user ID and the password before hashing, but there might be edge cases where you don't know the user ID (such as when a user is created).
Personally, I'd question the requirement.
I have read about users being able to manipulate website cookie and use it to exploits security loopholes. I did a search and came across an idea posted online. Here is the code below that is, after the username and password of the user are authenticated;
$Separator = '--';
$uniqueID = 'jhlhgjh12u0#345';
$Data = $userID.' '.md5('65748');
$expire=time()+60*24;
setcookie('verify-user', $Data.$Separator.md5($Data.$uniqueID), $expire);
The code above will set the cookie using a uniqueID, the userID, a MD5 hash numbers and a separator. The uniqueID, md5 hash numbers and separator are set by the developer. The idea is that a user won't be able to manipulate the cookie because the don't know the UniqueID, and the md5 hash numbers. The code below is used to test each cookie if they are manipulated or not
if ($_COOKIE) {
$Separator="--";
$uniqueID = 'jhlhgjh12u0#345';
$Cut = explode($Separator, $_COOKIE['verify-user']);
if (md5($Cut[0].$uniqueID) === $Cut[1]) {
$_COOKIE['verify-user'] = $Cut[0];
} else {
echo "fake cookie";
}
}
else {
echo "fake cookie";
}
I am wondering if this method is security tight or if there are loopholes too. criticism and corrections are welcomed
This is known as message signing. You hash the message together with a secret and attach that "signature" to the message itself. This allows the recipient to verify that the creator/signer of the message is in possession of the secret, without revealing the secret itself.
The problem with your particular implementation is that
the secret is too small
the hashing algorithm is unsuitable for the task
the cookies never change and never expire; if a cookie is stolen there's no recourse
You should use a longer secret, the longer the better. You should also use a hashing algorithm that is suited for the task, namely something like HMAC (hash-based message authentication). E.g.:
hash_hmac('sha512', $data, $secret)
You can see an implementation of a similar thing, including expiration of values, here.
The most important thing though: think thrice about whether a signed plain text message is the best way to go here in the first place. Perhaps you want a session-like system, in which an entirely meaningless random string is used as an id for data that is stored on the server. This completely eliminates the problem of users manipulating the cookie.
I'm toying with the idea of creating automatic electronic certificates. It's pretty easy to create custom certificates using the fpdf PHP class. The way I have it set up is that given a URL
http://www.example.com/makepdf.php?name=myname&class=classname
you get a PDF certificate with the student name and the class they took taken from the $_GET variable. Of course, this means that anyone could manipulate the URL to very easily create a custom certificate. (They could do this in Photoshop anyway, but the idea is to make manipulating the certificate not totally trivial.) Once a class is over, I want to send a mail merge to everyone with a unique URL for their certificate.
How would you approach this problem? Should I just create a set of random numbers and associate these with the student/workshop pairs in a database? Are there standard ways of approaching this problem?
Couple solutions stand out:
Store the names & classes in a database, and reference them with a numeric ID instead of passing the data in the request
Keep the information in the request, but add a secure hash that will prevent tampering with the data
The hash mechanism would be something like this:
When generating the link for the certificate, you have $name and $class. You'll create a third GET variable that is a hash of $name, $class, and a secret string that only your program knows. Something like this:
$salt = "this is my secret";
$hash = md5($name . $class . $salt);
$url = "http://www.mysite.com/certificate.php?name=" . urlencode($name) . "&class=" . urlencode($class) . "&hash=" . $hash;
Now when a user hits your certificate generation page, you must verify the hash:
$salt = "this is my secret";
$expected = md5($_GET['name'] . $_GET['class'] . $salt);
if ($expected != $_GET['hash']) {
die("You are not authorized");
} else {
// User is OK; generate the certificate
}
Yes, if you want to limit your inputs to a fixed pool, then creating a database full of random keys is the way I would go.
If you want a quicker and dirtier way to do it, just generate the keys into a text file, use a script to pull the file apart to send them to the recipients, and have your PHP certificate generator read from a copy of the file on the server.
Assuming you are generating these URLs yourself on the server, you could join all your parameter values together into a string:
hash_string = "myname:classname";
Then append a final parameter that's a hash of that string along with some secret seed:
query_string .= "&h=" . md5("my_secret_key:" . hash_string)
Then, when you get the query back, just check to make sure that the hash matches:
hash_string = params['name'] . ':' . params['class'];
if (params['h'] == md5("my_secret_key:" . hash_string)) ...
I don't really know PHP syntax, but you get the idea.
Your best bet would be to have a list of students/classes (some kind of database) and only allow generation of allowed certificates. That way you don't need to obfuscate the name of the student or class, because only valid certificates can be generated.
If that's too much to ask - you could generate a MD5 hash based on the combination and some salt, then add that hash to the URL. That way the salt would need to be know to forge a URL.
http://www.example.com/makepdf.php?name=Tim&class=PHP&hash=c2c455ce438112b44499561131321126
Then the generation script just does this:
$hash = md5($_GET['name'] . $_GET['class'] . $salt);
if($hash != $_GET['hash']){
//invalid request
}
Of course you'll need to generate the URL's with the same salt.
Should I just create a set of random numbers and associate these with the student/workshop pairs in a database?
Yes.