Salting example in Zend Framework - php

I am pretty new to the Zend framework and looking to build an application with pretty tight password security. I have been trying to follow the user guides in relation to password salting but haven't had any luck so far. I have setup my database and table adapter (As described in the documentation on the Zend Framework site but it didn't seem to finish the example (or I am not following well enough!) I have started with:
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter,
'users',
'username',
'password', "MD5(CONCAT('".Zend_Registry::get('staticSalt')."', ?, password_salt))"
);
But from here, what is done with the password salt? I just need an example and I'll be away! Does anyone have an example or point me in the right direction??
Many thanks!

Excelent example for an secure login with Zend Framework (altough using salts)
Login example with Zend Framework

Authentication method:
/**
* Authenticate user with specified identity and credential
*
* most used case is authenticate user inline in script
*
* #param string $identity
* #param string $credential
* #return Zend_Auth_Result
*/
public function authenticate ($identity, $credential)
{
$auth = Zend_Auth::getInstance();
$adapter = $this->getAdapter();
$adapter->setIdentity($identity)
->setCredential(self::passwordHash($credential));
$config = Singular_Runtime::extract('config');
$isActiveCol = $config->resources->auth->columns->is_active;
$isActiveAllowVal = $config->resources->auth->is_active->allow_value;
/**
* #see APPLICATION_PATH/configs/application.ini -> resources.auth
*/
if (null != $isActiveCol && null != $isActiveAllowVal) {
$adapter->getDbSelect()->where("{$isActiveCol} = ?", $isActiveAllowVal);
}
Singular_Event::dispatch('beforeAuth', array(
'auth' => $auth, 'adapter' => $adapter
));
$result = $auth->authenticate($adapter);
if ($result->isValid()) {
$auth->getStorage()->write($adapter->getResultRowObject());
Singular_Event::dispatch('afterAuth', array(
'auth' => $auth, 'adapter' => $adapter
));
}
return $result;
}
And password hash generation method:
/**
* Password hash generator
*
* #static
* #param string $password
* #return string
*/
public static function passwordHash ($password)
{
$password = strtolower($password);
return md5(
str_repeat(
md5($password) . strrev($password) . sha1($password),
strlen($password)
)
);
}

Related

PHP Unit testing setting JWT token after login test completes

Hi I'm trying to set JWT token after the login test is complete in a global var and then extend all tests off of that class, now I'm not sure if I am even doing it right so any help is appreciated or documentation I could look up
<?php
use Illuminate\Support\Facades\Config;
class AuthenticationTest extends TestCase {
/**
* #var string
*/
public $token = '';
/**
* Register test user
*
* #return void
*/
public function testRegister()
{
$this->json('post', '/auth/register', [
'email' => Config::get('tests.email'),
'password' => Config::get('tests.password'),
'companyName' => Config::get('tests.companyName'),
'firstname' => Config::get('tests.firstname'),
'lastname' => Config::get('tests.lastname')
]);
$this->assertEquals(200, $this->response->status());
$this->seeJsonEquals([
'message' => "Registration successful, please confirm email to login",
'status' => true,
]);
}
/**
* Login test user
*
* #return void
*/
public function testLogin()
{
$email = Config::get('tests.email');
$password = Config::get('tests.password');
$encryptedToken = base64_encode($email . ":" . $password);
$this->json('post', 'auth/login', ['token' => $encryptedToken]);
$this->assertEquals(200, $this->response->status());
$this->seeJsonStructure([
'token',
'refresh',
'status'
]);
$content = json_decode($this->response->getContent());
$this->assertObjectHasAttribute('token', $content);
$this->token = $content->token;
$this->token;
}
}
?>
The login test works as it should but the token isn't being set for other tests that extend off of this test case ad use $this->token to send the JWT token
PHPUnit creates a new instance of AuthenticationTest for every test method to run each test in isolation.
For tests that need an authenticated user, you have to obtain the token somehow. You could make an api request like you did in testLogin. A bit faster and less brittle way would be to create the token programatically using the same service as in your production code.
To reduce boilerplate, create a test case for tests with an authenticated and create the token in the setup method. Tests that extend from this class have the token property. Alternatively, you could also make this functionality a trait.
EDIT:
Here's a simple code example.
abstract class AuthenticatedUserTestCase extends TestCase
{
protected function token(): string
{
$email = Config::get('tests.email');
$password = Config::get('tests.password');
$encryptedToken = base64_encode($email . ':' . $password);
$response = $this->json('post', 'auth/login', ['token' => $encryptedToken]);
$content = json_decode($response->getContent());
if (!isset($content->token)) {
throw new RuntimeException('Token missing in response');
}
return $content->token;
}
}
final class UserProfileTest extends AuthenticatedUserTestCase
{
public function test_that_user_can_access_their_profile_page()
{
$response = $this->json('get', '/user/profile', [], ['token' => $this->token()]);
// ... assert stuff
}
}

Send reset link with Laravel Auth and encrypted user table

I have a laravel 5.4 installation and I always used the default Laravel Authentication guard to handle user authentication and, mainly, the password restore process.
Now I had to encrypt the email in the users table using the Elocryptfive library, so I also added email_hash field where the hash of the mail is stored in the db in order to easily retrieve users by their email.
I can easily authenticate users using the hash:
Auth::attempt([
'email_hash' => hash('sha256', $request->get('email')),
'password' => $request->get('password')]
, $remember);
What I can't get working is the password reset process. Is there a class to override in order to retrieve users by email_hash, then access the decrypted email and send the mail, without rewriting the whole password forgotten process?
I found a way to achieve this. I will answer my own question to provide a useful solution if someone else needs some help on the topic:
In your ForgotPasswordController.php, override the sendResetLinkEmail function:
/**
* Send a reset link to the given user.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
$hashed = hash('sha256', $request->get('email'));
$user = User::where('email_hash', $hashed)->first();
if (!is_null($user)) {
$response = Password::sendResetLink(
['email_hash' => $hashed]
);
} else {
$response = Password::INVALID_USER;
}
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($response)
: $this->sendResetLinkFailedResponse($request, $response);
}
In your ResetPasswordController.php, override the credentials function:
/**
* Get the password reset credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return [
'email_hash' => hash('sha256', $request->get('email')),
'password' => $request->get('password'),
'password_confirmation' => $request->get('password_confirmation'),
'token' => $request->get('token')
];
}
Thanks to Mike Rodham for pointing out the right direction, I hope it helps someone.

Symfony Custom UserProvider Auth + Parse SDK

Heyo!
I know it's a common problem people having problems with custom providers and web service authentication. I'm spending hours trying to figure out how to do that but I'm almost freaking out.
So, the thing is: I'm using the Symfony Firewalls with a custom UserProvider and a AbstractGuardAuthenticator as well. The problem is in the loadUserByUsername($username) function inside the UserProvider implementation.
For security reasons I can't retrieve the user password from Parse (my web service), and the loadUserByUsername($username) function asks for that. Even in the documentation about how to create a custom user provider using web services they are retrieving the user password from the database.
So what's the solution in that case? What can I do when I don't have access to the user password?
My current code is something like that:
$app['app.authenticator'] = function () {
return new Authenticator($app);
};
$app['security.firewalls'] = array(
'login' => array(
'pattern' => '^/login/$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login/', 'check_path' => '/login/auth/'),
'logout' => array('logout_path' => '/logout/', 'invalidate_session' => true),
'guard' => array(
'authenticators' => array(
'app.authenticator'
),
),
'users' => function () use ($app) {
return new UserProvider($app);
},
)
);
The Authenticator.php is quite big code because extends the AbstractGuardAuthenticator class. But I'm basically using this one from Symfony docs. The only thing Is that I'm sending to the UserProvider class the username AND the password as well, because that way I can check if the user and password are right. Like this:
public function getUser($credentials, UserProviderInterface $userProvider) {
return $userProvider->loadUserByUsername($credentials);
}
And my UserProvider class is the default one, I'm just checking inside the loadUserByUsername function if the credentials comming from my Authenticator are right. Something like this:
public function loadUserByUsername($credentials) {
$encoder = new BCryptPasswordEncoder(13);
try {
$user = ParseUser::logIn($credentials['username'], $credentials['password']);
} catch (ParseException $error) {
throw new UsernameNotFoundException(sprintf('Invalid Credentials.'));
}
return new User($credentials['username'], $encoder->encodePassword($credentials['password'], ''), explode(',', 'ROLE_USER'), true, true, true, true);
}
The problem is: after the login (everything with the login is working fine), Silex calls the loadUserByUsername function in every page which needs to be secured, but just sending the username parameter.
So basically, I don't know what to do guys. I'm really trying to figure out how to get this thing working.
Thanks for your help!
I have a similar implementation and this issue is well known. In my user provider I have the methods loadUserByUsername($username) and refreshUser(UserInterface $user). Since I have the same issue like you, I don't check the user in loadUserByUsername but simple return a new Object with only the username in it to not disturb the flow. loadUserByUsername doesn't make sense for external APIs, so I simply jump over it. The method refreshUser is either beeing called on every request, this is usefull.
In your AbstractGuardAuthenticator you have the method createAuthenticatedToken, which returns an token. There you should have the full authentificated user:
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
{
/**
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
* care about which authenticated token you're using.
*
* #param UserInterface $user
* #param string $providerKey
*
* #return PostAuthenticationGuardToken
*/
public function createAuthenticatedToken(UserInterface $user, $providerKey)
{
//do login stuff
//save password in user
return new PostAuthenticationGuardToken(
$user,
$providerKey,
$user->getRoles()
);
}
}
Then, I would't use loadUserByUsername but refreshUser instead. Both are called on every request:
/**
* Don't use
* #codeCoverageIgnore
* #param string $username
* #return User
*/
public function loadUserByUsername($username)
{
return new User($username, null, '', ['ROLE_USER'], '');
}
/**
* Refresh user on every subrequest after login
* #param UserInterface $user
* #return User
*/
public function refreshUser(UserInterface $user)
{
$password = $user->getPassword();
$username = $user->getUsername();
//login check
}

Dynamic Laravel Socialite Configurations

I need to configure my providers dynamically.
$config = [
'client_id' = 'xxxxxxx',
'client_token' = 'xxxxxxx',
'redirect' = 'http://example.com/'
];
return Socialite::with($provider)->setConfig($config)->redirect();
But unfortunately there is no function setConfig.
I need to set provider, client_id, client_secret and redirect dynamically
Is there any ideas?
Thank you!
You could use the Socialite buildProvider method like:
$config = [
'client_id' = 'xxxxxxx',
'client_token' = 'xxxxxxx',
'redirect' = 'http://example.com/'
];
return Socialite::buildProvider(\Laravel\Socialite\Two\FacebookProvider::class, $config);
Where \Laravel\Socialite\Two\FacebookProvider::class would be swapped with your service (if different) as provided in either folder One/Two in https://github.com/laravel/socialite/tree/2.0/src
I use the following service provider in order to automatically fill in the redirect for each provider where it's empty.
It could be modified to update your configuration on the fly. It depends exactly what you're trying to do I suppose.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class SocialServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
collect(config('services'))
->only(config('social.providers'))
->reject(function($config) {
return array_get($config, 'redirect', false);
})
->each(function($config, $key) {
$url = url("login/{$key}/callback", [], true);
config(["services.{$key}.redirect" => $url]);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
}
This could help if anyone still faces the problem
you can set the Redirect Url manually
$driver = Socialite::driver('google');
$driver->redirectUrl('your-custom-url');

Laravel 4 create an user programatically with Zizaco/confide

I'm trying to create a user with the Zizaco/confide library, the user data comes from Facebook and not from a form.
I try this :
$user_profile = Facebook::api('/me','GET');
$new_user = new User;
$new_user->username = $user_profile['name'];
$new_user->password = Hash::make('secret');
$new_user->email = $user_profile['email'];
$new_user->confirmation_code = '456456';
$new_user->confirmed = true;
$new_user->save();
but it doesn't save the user. Any help ?
I found the problem, the confide library sets some default rules to create the user, you need to pass this rules to save a user:
public static $rules = array(
'username' => 'required|alpha_dash|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|between:4,11|confirmed',
'password_confirmation' => 'between:4,11',
);
with this example it works:
$user = new User();
$user->username = 'asdasd';
$user->email = 'angorusadf#gmail.com';
$user->password = '12312312';
$user->password_confirmation = '12312312';
$user->save();
Mabe the method should give you some information when you don't pass the rules.
Maybe it's a pretty late answer, but I'm posting this for future reference because I was myself looking for an answer to the same question, How to create a user programatically in zizaco/confide? or more generally, how to bypass zizaco/confide's requirement for setting a password when saving a user for the first time?
well, the answer is pretty simple, thanks to the new architecture which Zizaco implemented in the 4.* branch, it's now possible to register a new user validator class see more at the package's readme in short all you need in this very case though, is just to extend the current User validator and override validatePassword() to make it accept empty passwords for new users.
Below is an example implementation:
In routes.php
// we need to register our validator so that it gets used instead of the default one
// Register custom Validator for ConfideUsers
App::bind('confide.user_validator', 'MyUserValidator');
In app/models/MyUserValidator.php (that's basically a copy of the function in the class, simply just added a check whether this is a old user or not (if the user has an ID then this is an update operation) if this is a new user, the method always returns true!
/**
* Class MyUserValidator
* Custom Validation for Confide User
*/
class MyUserValidator extends \Zizaco\Confide\UserValidator //implements \Zizaco\Confide\UserValidatorInterface
{
/**
* Validates the password and password_confirmation of the given
* user
* #param ConfideUserInterface $user
* #return boolean True if password is valid
*/
public function validatePassword(\Zizaco\Confide\ConfideUserInterface $user)
{
$hash = App::make('hash');
if($user->getOriginal('password') != $user->password && $user->getOriginal('id')) {
if ($user->password == $user->password_confirmation) {
// Hashes password and unset password_confirmation field
$user->password = $hash->make($user->password);
unset($user->password_confirmation);
return true;
} else {
$this->attachErrorMsg(
$user,
'validation.confirmed::confide.alerts.wrong_confirmation',
'password_confirmation'
);
return false;
}
}
unset($user->password_confirmation);
return true;
}
}

Categories