Could this script be improved? - php

Ok, i'm wondering if this code stored in user_login.php could be improved or if i'm doing it wrong. I'm confused because all my script in the application are long about 30-40 lines only and i was wondering if i am missing something.
This script is called with an ajax call like everyone else in my application except for template files.
<?php
# Ignore
if (!defined('APP_ON')) { die('Silence...'); }
# Gets the variables sent
$user_name = post('user_name');
$user_password = extra_crypt(post('user_password'));
# Check if the user exists
if (!user::check($user_name, $user_password)) { template::bad($lang['incorrect_login']); }
# Logging in
$id = user::get_id($user_name, $user_password);
$u = new user($id);
$u->login();
template::good($lang['correct_login']);
?>
I'm going to explain it:
# Ignore
if (!defined('APP_ON')) { die('Silence...'); }
This basically check that the file is not called directly. Each url is redirected to an index.php file that manage the url (es: www.mysite.com/user/view/id/1) and include the right file. In the first lines of this index.php file there is a define('APP_ON', true);. The index file also initialize the application calling some set of functions and some classes.
# Gets the variables sent
$user_name = post('user_name');
$user_password = extra_crypt(post('user_password'));
The function post() manage the recovering of $_POST['user_name'] making some checks.
The function extra_crypt() just crypt the password using sha1 and a custom alghoritm.
I'm using prepared statement for sql queries so don't worry about escaping post variables.
# Check if the user exists
if (!user::check($user_name, $user_password)) { template::bad($lang['incorrect_login']);
The user class has both static and normal methods. The static ones doesn't require an id to start from, normal methods does. For example user::check(); does check if the username and the password exists in the database.
The template class has just two static methods (template::bad() and template::good()) that manage fast dialog box to send to the user without any header or footer. Instead if you instantiate the class $t = new template('user_view'), the template user_view_body.php is called and you can manage that page with $t->assign() that assign static vars to the template, or with $t->loop() that start a loop etc.
Finally $lang is an array having some common strings in the language set by the user.
# Logging in
$id = user::get_id($user_name, $user_password);
$u = new user($id);
$u->login();
template::good($lang['correct_login']);
At the end we have the actual login. The user class is instantiated and an id is recovered. The user with that id is logged in and we return a template::good() message box to the user.
For any clarification write a comment above.

First of all a message digest function like sha1 is not an encryption function. So please remove crypt from the function name to avoid confusion. Further more, this whole idea of a "custom algorithm" for storing passwords scares the hell out of me. sha256 is a better choice than sha1, but sha1 isn't all that bad after all its still a NIST approved function. Unlike md5 no one has been able to generate a collision for sha1 (although this will happen in the new few years). If you do go with sha1 make sure the salt is a prefix, as to thwart the prefixing attack.
Input validation must always be done at the time of use. the post() function should not be responsible for any input validation or escaping. This is just an incarnation of magic_quotes_gpc which is being removed because its a security train wreak.
Parametrized Query libraries like PDO and ADODB are very good and I recommended using it. This is a great example of sanitizing at the time of use.
Your template assign() method can be used to sanitize for XSS. Smarty comes with a htmlspecialchars output filter module which does this.

With so much functionality behind functions that you haven't provided, it's hard to say whether anything needs to be fixed. Are you making sure the input is clean in post()? Are your database calls secure in user::check()? Are you using session cookies to simplify user prefs/info storage? Is your template class well-written? When you create a new user, are you logging all the necessary information (time of login, IP address if necessary, etc)? Are you ensuring that the user isn't doubly logged in, if that's important here?
The only concrete thing I can tell you about what you wrote is that the sha1 algorithm is completely broken, and you should be using something else instead; see the comments below for suggestions.

Related

Can a default user's password be set in SQL using PHP's password_hash?

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 want to make a secure link

i want to make a code generator link,like
www.website.com/register?code=29U3HTR3H219URH923UH419H94RH1298491U2HERUH1?plan_bought=LowReseller
in a functions file on php that is redirecting an user on that link.
$planned = htmlspecialchars($_GET["planbought"]);
// connect to database
$db = mysqli_connect('localhost', 'root', 'pass');
mysqli_select_db($db,"ronp");
function generateRandomString($length = 16)
{
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
}
$code_secure = generateRandomString(17); // OR: generateRandomString(24)
$query = "INSERT INTO codes (code, expired, 'date', plan)
VALUES('$code_secure', '0', date, '$planned')";
mysqli_query($db, $query);
header('Location: register?code=', $code_secure);
?>
Process: After payment,paypal will redirect user on https://website.com/functions_generate-ak9esysgenthos.php?planbought=Low
That link will create a code in database,and will redirect user on https://website.com/register?code_secure=(code)
Now the problem is,i get redirected on "https://website.com/register?code=",not "https://website.com/register?code=(the code created in database,like 'J2498JT9UJ249UTJ293UJ59U123J9RU9U')"
If you look at the documentation for header() you'll see that the second parameter is a boolean value. This parameter specifies if the header should be "forcefully" be replaced. You are incorrectly passing your "secure"* code as that parameter.
What you want to do is concatenate the strings instead of passing your "secure" code as a second parameter. What you want to get is
header('Location: register?code=' . $code_secure);
*The "secure" code you are generating is predictable (as you used this code), if you need a secure code you might want to look into openssl_random_pseudo_bytes before PHP 7.0 and random_bytes() in PHP 7.0 or higher, as demonstrated by this answer.
Furthermore, as mentioned by Raymond Nijland your code is vulnerable to SQL injections. See this excellent answer on how you can prevent SQL injections.
Besides the issues mentioned in Tom Udding's answer, there are at least two other issues:
functions_generate-ak9esysgenthos.php can be accessed without any authentication at all. Moreover, it generates a "secure code" blindly, without determining whether a user is logged in or otherwise authorized to access that page (e.g., without determining whether a payment operation is in progress). This could allow anyone with knowledge of the URL to access functions_generate-ak9esysgenthos.php; depending on how your application is implemented, this could cause orders that weren't paid for or even a denial of service attack (due to the additional order codes clogging your database).
You are generating a random "secure code" without checking whether that code was already used. What if /register?code=... finds two records with the same code? Can your application tolerate the risk of generating the same code for different records? (See also my section on unique random identifiers.)

Wordpress sha 256 login

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.

Using a PHP multidimensional array as a database

I'm building a private CMS for my own use and am at the point where I will start building out the username and password storing features. I am considering the possibility of storing all admin username, password, and user details in a multidimensional array within a PHP file, rather than using SQL to store them in a database.
My reason for wanting to use this non-traditional approach of storing user info is the belief that this will make it harder for attackers to gain unauthorized access to user info (usernames, passwords, IP addresses, etc.), because I will not be connecting to a MySQL database.
Rough Outline of Code:
add_user.php
// set the last referrer session variable to the current page
$_SESSION['last_referrer'] = 'add_user.php';
// set raw credential variables and salt
$raw_user = $_POST['user'];
$raw_pass = $_POST['pass'];
$raw_IP = $_SERVER['REMOTE_ADDR'];
$salt = '&^${QqiO%Ur!W0,.#.*';
// set the username if its clean, else its false
$username = (is_clean($raw_user)) ? $raw_user : false; // is_clean() is a function I will build to check if strings are clean, and can be appended to an array without creating a parsing error.
// set the salted, sanitized, and encrypted password if its clean, else its false
$password = (is_clean($raw_pass)) ? $salt . encrypt($raw_pass) : false; // encrypt() is a function I will build to encrypt passwords in a specific way
// if username and password are both valid and not false
if( $username && $password ) {
// set the users IP address
$IP = sanitize($raw_IP);
// create a temporary key
$temp_key = $_SESSION['temp_key'] = random_key();
// random_key() is a function I will build to create a key that I will store in a session only long enough to use for adding user info to the database.php file
// add user details array to main array of all users
$add_user = append_array_to_file('database.php', array($username, $password, $IP));
// append_array_to_file() is a function I will build to add array's to the existing multidimensional array that holds all user credentials.
// The function will load the database.php file using cURL so that database.php can check if the temp_key session is set, the append_array_to_file() function will stop and return false if the database.php file reports back that the temp_key is not set.
// The function will crawl database.php to read the current array of users into the function, will then add the current user's credentials to the array, then will rewrite the database.php file with the new array.
// destroy the temporary session key
unset($_SESSION['temp_key']);
}
else {
return false;
}
database.php
$users_credentials = array(1 => array('username' => 'jack',
'password' => '&^${QqiO%Ur!W0,.#.*HuiUn34D09Qi!d}Yt$s',
'ip'=> '127.0.0.1'),
2 => array('username' => 'chris',
'password' => '&^${QqiO%Ur!W0,.#.*8YiPosl#87&^4#',
'ip'=> '873.02.34.7')
);
I would then create custom functions to mimic SQL queries like SELECT for use in verifying users trying to log in.
My Questions
Is this a bad idea, and if so, why?
Am I correct in thinking that this will reduce the number of possibilities for hackers trying to gain unauthorized access, sniff/steal passwords, etc., since I'm not connecting to a remote database?
I don't see any advantage: Whether you use a text file, a mysql database or a php file ( === text file), they are all "databases" in the sense that they are files where you store your information. The difference is that an sql database is made for that stuff;
I do see disadvantages as there are more potential holes you would have to think about. Some examples (apart from the stuff mentioned in the comments):
You need to take care that the password file is always out of the web-root in case php dies on you;
You need to avoid passing around your password file in for example source control.
These are not things that are hard to solve, but using a normal database you don't even have to worry about them.
Apart from that are misunderstanding the purpose of the salt: If you just prepend it to the encrypted password, there is really no point in using a salt, you need to send it to your encrypt function to hash it with your text-password so that rainbow tables would have to be generated for each password instead of just one for your whole database. And for that reason you should also not use a single salt for all your users, each should have a different, unique salt.
If you plan to store any kind of config data in a text file of any sort, as opposed to a traditional database, consider using an .ini file. If I'm not mistaken, you can also take advantage of storing it outside of your web root, just like the php.ini file.
Here's a great post explaining exactly how to go about this: Using ini files for PHP application settings
PHP Manual: get_cfg_var()

How to 'encrypt' information passed along in URL when redirecting in PHP?

Sorry if the title's unclear, couldn't think of anything better since I'm still new to this area. :)
Anyway, my question is this: I want to send some information from one page (let's call it 1.php) to another page (let's call it 2.php) using this (don't know the formal name, sorry):
http://localhost/X/2.php?user_id=5&user_type=2&ssn=1234567890&first_name=John&last_name=Doe
As you can see, the information is in plain text, which I dislike. Is there an easy way to encrypt the string after the question mark above in 1.php, and then let the 2.php (that gets the passed-along info) decrypt it? I'd like for it to be something along:
http://localhost/X/2.php?user_id=rj3i15k&user_type=8109fk1JIf&ssn=6893kfj399JFk...
Sorry if this is a stupid question. Many thanks in advance!
If you don't want information to be modified, use a hash string to verify them.
For instance :
$hash = sha1($user_id."haha".$user_type.$ssn.$first_name.$last_name);
The "haha" here, is a salt. Use a random string, it will be use so someone can't reuse your algorithm to inject fake data.
Then put this hash at the end of your url, eg
http://localhost/X/2.php?user_id=5&user_type=2&ssn=1234567890&first_name=John&last_name=Doe&hash=$hash`
When you'll get this information, make the hash again, and compare it to the hash sent : If the information was modified, the hash won't match.
Maybe you're going about it the wrong way.
Thought about storing the data in a serverside session variable?
Or even in a database (if you're passing to another machine), then you just need to send the unique identifier of the database entry.
page2 will then read the session variable, or retrieve it out of the database again.
Basically, keep the data serverside and then you wont need to encrypt/decrypt.
Session Example:
page1
<?
session_start();
$_SESSION['pagedata'] = array(
'user_id'=>5,
'user_type'=>2,
'ssn'=>1234567890,
'first_name'=>'John',
'last_name'=>'Doe'
);
header('Location: page2.php');
?>
page2
<?
session_start();
$user_id = $_SESSION['pagedata']['user_id'];
$user_type = $_SESSION['pagedata']['user_type'];
$user_ssn = $_SESSION['pagedata']['user_ssn'];
$user_first_name = $_SESSION['pagedata']['first_name'];
$user_last_name = $_SESSION['pagedata']['last_name'];
// use variables to do stuff
?>
Its called GET, never relate 100% on 2 Way Decryption but this may help you Best way to use PHP to encrypt and decrypt passwords?
you could use base64_encode on the one side and bas64_decode on the other - just as one possibility - but note that this is only for "better looking" url als you want it (for me, this is ugly). this isn't encrypting your data for being more safe or something like that - to achive this, use https and don't confuse your users by doing such crazy stuff.
You should use $_SESSION.

Categories