The idea is to encrypt a string containing session data (username and token expiry) and store it in a cookie. Whenever an HTTP request comes from the client, the backend checks if it can decrypt the token in the cookie and then queries the DB to check if the username exists and is allowed to access the target resource:
// create token
$token = openssl_encrypt($username . "::" . $expiry, "rc2-40-cbc", $key, 0, "00000000");
// then store in cookie
'::' is just a delimiter.
// on http request, verify token
$decryptedToken = openssl_decrypt($token, "rc2-40-cbc", $key, 0, "00000000");
if($decryptedToken != false) // token is valid
// extract data
$tokenValues = explode("::", $token);
$username = $tokenValues[0];
$tokenExpiry = $tokenValues[1];
// then validate username and expiry
Is this safe?
Your code is absolutely insecure for multiple reasons. Please do not implement crypto without a basic understanding of what you are doing.
Using your method, the session data in the db would be cracked in less than a day with hardware that you have at home.
A few problems:
rc2-40-cbc is very, very weak
it is not an authenticated encryption
the IV is not for fun, you can't just put in zeroes because that worked
the result will not fit in a cookie if there is enough session data
the same session data would be encrypted to the same ciphertext
...
Please note that the solution is not changing the cipher and adding an IV. The solution is to use a well-known and tested library that does this for you.
The concept btw could work, but it has to be correctly implemented.
Related
I want to use authentication token, after various searches I found JWT token and another token generation method with md5.
Off for various reasons I do not want to use the token JWT and md5 seems to me to have a bad reputation.
So I found a method but I would like your opinion:
Generating a Byte String: https://www.php.net/manual/en/function.random-bytes.php
Then I convert that into Hexadecimal.
Finally, I concatenate that with the id of my user and salting.
I send this to my application (Android or web) than during a request to my service I decode the received token, and I will see the token contained in the id hidden in the token.
So it is harder to find the token by brute force?
Note 1: that I would like to refresh the token by the future but for the moment it should be issued for an indefinite period.
Note 2: I do not want to use a library or framework just in php.
Code :
$bytes = random_bytes(32);
$part=bin2hex($bytes));
$shortpart=substr($part,0,17);
$id = #userid
$salt=#customsalt
$token=shortpart.$id.$salt;
Example :
$shortpart =f2e4d1f2a2dfedcf5
$id=1
$salt=4d2ze121
$token=f2e4d1f2a2dfedcf514d2ze121
The user or hacker doesn't know is id is hidden in the token.
After some research I think the better method is:
$token = bin2hex(openssl_random_pseudo_bytes(32));
And custom this string.
Add one column in database table for time and calculate authtoken as:
$time = time();
and store this to database.
Encryption:
$encrypt = base64_encode($time.userid);
Decryption:
Get the time from table:
$decrypt = base64_decode($encrypt);
Check this link.
I'm creating a sort of 'social network'. Now I'm creating the authentication part. Today I learned about JSON Web Tokens.
1) I read that JWT's are safe to use, because the are signed with a secret key. But then I found some tools online like https://jwt.io. I tried to build some JWT tokens with PHP using firebase/php-jwt. The tools like jwt.io can extract the data I put in the JWT (like user ID). How can this be safe? Can't someone just create a new JWT using the old one but with a different user ID?
An example: I created the following token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNeUFQIiwiaWF0IjoxNTE2NTYzMTM0LCJleHAiOjE1MTY1NjQzNDAsImF1ZCI6Ind3dy5leGFtcGxlLmNvbSIsInN1YiI6ImFkbWluQGV4YW1wbGUuY29tIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOiJmYWxzZSJ9.dCtYVpFXhoQmzOdX_dW1yFHEcZ9aZ1I9MC33lJGapvY
If you paste this token in jwt.io, you'll see the payload is "name: John Doe" and "admin: false". Isn't this unsafe? Or is it safe because you NEED the secret key to recreate a JWT.
Off the record: You cannot store senstive information in a JWT I guess...
2) How to "login" a user using PHP and JWT's? I created a quick demo, I'm not sure the code "works" but I guess you'll see what I mean.
if (isset($_POST['submit'])) {
$user = $_POST['user'];
$pass = $_POST['pass'];
if($user = 'my_username' && $password == 'my_password') {
// user is logged in
// create a JWT here
} else {
// wrong credentials!
}
}
Now, the question/problem is: how to store this token? In a cookie? In a session? Using HTML5's localStorage? What is the safest way to do this?
And secondly: how to authenticate the user? I would do it like this:
// my secret key
$secret = 'MY_SECRET_KEY';
// decode the token
$token = JWT::decode($token, $secret, array('HS256'));
// what to do here?
Can you match this token with some data in a database or something?
1) The JWT token is not encrypted, so the data inside it can be easily read. However, it contains a signature that can be validated only with the secret key you have set when creating the token. You can check the signature to see that the token has not been tampered with.
2) The token can be stored anywhere, since it's so small. Just remember that the token can be easily read, but not altered. Don't store any sensitive data in the tokens.
When checking the token, the important things to check are the signature and the exp time to see that the token is still valid. A well-constructed token doesn't need full database validation, but rather just check that the user's privileges haven't been changed since issuing the token, and if they have, recreate the token with the updated data or force the user to log in again.
Im trying to make a SSO validation on thinktecture OpenId connect in PHP
I have written a client and get an access token. BUT i cannot find out how to validate it.
The documentation says:
3.2.2.9. Access Token Validation
To validate an Access Token issued from the Authorization Endpoint with an ID Token, the Client SHOULD do the following:
Hash the octets of the ASCII representation of the access_token with
the hash algorithm specified in JWA [JWA] for the alg Header
Parameter of the ID Token's JOSE Header. For instance, if the alg is
RS256, the hash algorithm used is SHA-256.
Take the left-most half of the hash and base64url encode it.
The value of at_hash in the ID Token MUST match the value produced
in the previous step.
I have no idea how to make step 1.
I got the ALg as RS256 and i have the at_hash from the Id token, i just cant find an exampel in PHP on how to do the validation.
Here is an example in PHP to calculate the at_hash value that should be easy to adapt to your environment:
public function setAccessTokenHash($accessTokenString)
{
// bit : 256/384/512
if(isset($this->_header['alg']) && $this->_header['alg'] != 'none'){
$bit = substr($this->_header['alg'], 2, 3);
}else{
// TODO: Error case. throw exception???
$bit = '256';
}
$len = ((int)$bit)/16;
$this->_payload['at_hash'] = Akita_OpenIDConnect_Util_Base64::urlEncode(substr(hash('sha'.$bit, $accessTokenString, true), 0, $len));
}
I want to use methods and resources from the code of a website which is developed in PHPFox.
Basically, I'll receive request from iPhone/Android, I'll get the request and pass to the respective function from the PHPFox code, take the response from that function and return it back to the device.
For this purpose I've developed REST APIs using Slim framework.
But the major blocker I'm facing currently is in accessing the resources(i.e. functions and data) of PHPFox website.
I'm not understanding how should I authenticate the user using 'Token Based Authentication' in order to access the website's resources.
If someone could guide me in proper direction with some useful working example it would be really helpful for me.
N.B. : The proposed implementation of 'Token Based Authentication' should be very secure and fast in speed. The security should not be compromised in any way.
Following is the code I tried on my own but I don't know whether it's right or wrong. Is my approach correct or wrong. Please someone analyse it and let me know your feedback on it.
To create a token i use this function which takes as parameters, the user's data
define('SECRET_KEY', "fakesecretkey");
function createToken($data)
{
/* Create a part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"]; // It can be 'stronger' of course
/* Encoding token */
$token = hash('sha256', $tokenGeneric.$data);
return array('token' => $token, 'userData' => $data);
}
So a user can authentified himself and receive an array which contains a token (genericPart + his data, encoded), and hisData not encoded :
function auth($login, $password)
{
// we check user. For instance, it's ok, and we get his ID and his role.
$userID = 1;
$userRole = "admin";
// Concatenating data with TIME
$data = time()."_".$userID."-".$userRole;
$token = createToken($data);
echo json_encode($token);
}
Then the user can send me his token + his un-encoded data in order to check :
define('VALIDITY_TIME', 3600);
function checkToken($receivedToken, $receivedData)
{
/* Recreate the generic part of token using secretKey and other stuff */
$tokenGeneric = SECRET_KEY.$_SERVER["SERVER_NAME"];
// We create a token which should match
$token = hash('sha256', $tokenGeneric.$receivedData);
// We check if token is ok !
if ($receivedToken != $token)
{
echo 'wrong Token !';
return false;
}
list($tokenDate, $userData) = explode("_", $receivedData);
// here we compare tokenDate with current time using VALIDITY_TIME to check if the token is expired
// if token expired we return false
// otherwise it's ok and we return a new token
return createToken(time()."#".$userData);
}
$check = checkToken($_GET['token'], $_GET['data']);
if ($check !== false)
echo json_encode(array("secureData" => "Oo")); // And we add the new token for the next request
Am I right?
Thanks.
1st you should understand what's token based authentication. It could be explained as below.
The general concept behind a token-based authentication system is
simple. Allow users to enter their username and password in order to
obtain a token which allows them to fetch a specific resource -
without using their username and password. Once their token has been
obtained, the user can offer the token - which offers access to a
specific resource for a time period - to the remote site.
Read more
Now let's see what are the steps of implementing it in your REST web service.
It will use the following flow of control:
The user provides a username and password in the login form and clicks Log In.
After a request is made, validate the user on the backend by querying in the database. If the request is valid, create a token by
using the user information fetched from the database, and then return
that information in the response header so that we can store the token
browser in local storage.
Provide token information in every request header for accessing restricted endpoints in the application.
If the token fetched from the request header information is valid, let the user access the specified end point, and respond with JSON or
XML.
See the image below for the flow of control
You might be wondering what's a JWT
JWT stands for JSON Web Token and is a token format used in
authorization headers. This token helps you to design communication
between two systems in a secure way. Let's rephrase JWT as the "bearer
token" for the purposes of this tutorial. A bearer token consists of
three parts: header, payload, and signature.
The header is the part of the token that keeps the token type and encryption method, encoded in base64.
The payload includes the information. You can put any kind of data like user info, product info and so on, all of which is also stored in
base64 encoding.
The signature consists of combinations of the header, payload, and secret key. The secret key must be kept securely on the server-side.
You can see the JWT schema and an example token below;
You do not need to implement the bearer token generator as you can use php-jwt.
Hope the above explains your confusion. if you come across any issues implementing token based authentication let me know. I can help you.
i will be providing api keys to my partner sites and they will be using code that i give them to to generate "tokens".
these tokens will be automatically present on forms which the partner sites' users will click on and reach my site. when they reach my site, i will need to validate that they indeed came from a partner site.
how do i validate this? the apikey will be secret, but what is presented in the form will NOT be, so it must not be possible for smart users to reverse engineer my algorithm.
EDITA
Option1: I get teh client page to send across md5($apikey.$time) AND $time (in plaintext). When i get it, i use time and my copy of apikey to generate md5($apikey.$time). if it matches and is within 1 hour (or whatever), i let the request proceed.
Option2: I already have $userid, $requestcommandoption coming in as well. I can do the following:
$input = $userid.'-'.$requestcommandoption.'-'.$time;
$encrypted_data = mcrypt_ecb (MCRYPT_3DES, $apikey, $input, MCRYPT_ENCRYPT);
when i get it at my end, i can do:
$decrypted_data = mcrypt_ecb (MCRYPT_3DES, $apikey, $encrypted_data, MCRYPT_DECRYPT);
and then check the 2 inputs if they are the same, and the 3rd if its within 1 hour?
EDITB
How secure does this sound? (code borrowed from http://onlamp.com/pub/a/php/2001/07/26/encrypt.html?page=3)
// on client
$apikey="test123";
$userid = '577';
$requestcommandoption = 'delete-all';
$time = mktime();
echo "time = $time<p>";
$input = $userid.'-'.$requestcommandoption.'-'.$time;
// Encryption Algorithm
$cipher_alg = MCRYPT_RIJNDAEL_128;
// Create the initialization vector for added security.
$iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher_alg, MCRYPT_MODE_ECB), MCRYPT_RAND);
// Encrypt $string
$encrypted_string = mcrypt_encrypt($cipher_alg, $apikey, $input, MCRYPT_MODE_CBC, $iv);
$transmitted = bin2hex($encrypted_string);
// sent from client to server
print "Encrypted string: ".$transmitted."<p>";
// received on server
$encrypted_string = pack("H*" , $transmitted);
$decrypted_string = mcrypt_decrypt($cipher_alg, $apikey, $encrypted_string, MCRYPT_MODE_CBC, $iv);
print "Decrypted string: $decrypted_string";
Looks as if you're implementing something similar to open authentication - the process twitter/facebook etc use for enabling partner sites.
I'd recommend that you take a look at oAuth - http://oauth.net/ - there are plenty of libraries and php samples.
If you really want to do something simple, then assuming you've got a record of the API keys you've handed out, I would write the client script so that it makes an md5 hash of the key with another bit of information on the form - a username for example (lets call the hashed string the request key and the username the username), and I'd include an identifier for the partner (which we'll call partner_id).
So when the form submits it has the requestkey, username and partner_id.
When your server receives the request you can look up the secret key for the partner using the partner_id, then md5 the secret key you've got, with the supplied user name and see if it matches the md5 key sent with the form.
#frank...
[Adding this as a result of your comment]
In order to make the key that is sent over the wire disposable, you can get the client web page to request a temporary session key - your server generates one (using a combination of date + time + a secret word), saves it as a temp key in the partners table (alongside their permanent key) and sends it back to the client. The client app then MD5's this with the permanent key and submits this with the form. You then look up the permanent and temp keys and hash them together and compare the result with the hash you've been sent.
does that seem ok?
What about GUIDs? Of course, you would have to track the issued GUIDs.
http://php.net/manual/en/function.com-create-guid.php
You should check out OAuth and OAuth 2 standards. They're widely used for authorization and many APIs (Facebook, Twitter, etc.)