Unset $_POST after request has been addressed - php

I am implementing a login controller, as it follows :
public function __invoke(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
try {
session_start();
$datas = [];
$rqst = $request->getParsedBody();
if(!isset($_SESSION["login"]) ) {
if ((isset($rqst["email"]) && isset($rqst["pwd"]))) {
$loginRequest = LoginRequestFactory::from($request);
$loginId = $this->sessionService->connect($loginRequest);
if ($loginId != -1) {
$_SESSION["login"] = $loginId;
}
unset($_POST['email']);
unset($_POST['pwd']);
}
} else {
if (isset($rqst["logout"])) {
unset($_SESSION["login"]);
unset($rqst["logout"]);
}
}
$response = $response->withHeader('parsedBody', []);
$response->getBody()->write($this->rend($datas));
} catch (Exception $exception) {
$datas = [
'error' => $exception->getMessage(),
];
$response->getBody()->write($this->rend($datas));
}
return $response;
}
Everything works well concerning the login and the logout.
The problem is that, when the login request failed (from a login service, for example because login or password is invalid), an error is thrown from the service. This error is shown above the login form.
I want this error to disappear when I refresh the page. For that, I need to unset $_POST['email'] and $_POST['pwd'].
It doesn't work so I tried to do something like that, before returning the rendered response :
$response = $response->withHeader('emaill, '');
Or:
$reponse = $response->withoutHeader('email');
or:
$response = $response->withoutHeader('parsedBody[\"email\"]');
But nothing work, so when I refresh the page, the page ask me to repost the form and the error appears again.
I don't know if the problem is a PHP problem or if it can be solved with Slim functionalities, so has somebody an idea of how to unset posted datas when I do not need them anymore ?
Thanks a lot

Related

Laravel Redirect from Different Function

I'm trying to make an authorization code validator that can be used throughout multiple instances of my website... I want to make it so if the validation fails, it will redirect the user from within the validation function (but this is not working) (similar to how laravels native validation system works for form data). Here are snippets of my code showing the issue.
// From a custom controller (AccountController.php)
public function requestValidate(Request $request, $token)
{
$user = Auth::user();
// This call should redirect to the homepage if the verification fails...
// but instead... it does not redirect and executes return view('verified');
$user->verifyAuthToken($request, $token);
return view('verified');
}
// From User.php
public function verifyAuthToken(Request $request, $token)
{
if (trim($this->auth_code) === '' || trim($token) === '' || $this->auth_code !== $token) {
$request->session()->flash('error', 'Sorry, an error occurred. Please try again.');
// THIS DOES NOT REDIRECT
return redirect()->route('profile');
}
$this->auth_code = "";
$this->save();
}
in your code
return redirect()->route('profile');
this only break the process in your verifyAuthToken function, but not your requestValidate. So basically after the page is redirected to profile, it will redirected again to verified page.
So, maybe you can give a return True in authentication success, then in requestValidate you can do like this:
if($user->verifyAuthToken($request, $token)){
return view('verified');
}

Store value of session key then unset?

I'm trying to imitate the behavior of flash messages in native PHP, for one-time display of error messages.
Displaying the Login page:
public function showLoginAndRegistrationPage()
{
$session = new Session();
$data['errors']['login']['account'] = $session->getFormErrorFlashData('login', 'account');
$this->viewPresenter->display('basic', 'customer/login-registration', $data, 'Login/Register');
}
Verifying the login details:
public function processLogin()
{
// Some code
$session = new Session();
if($this->formInputFilter->isValid()) {
// Some code
if(true) {
// Some code
} else {
$errors = array(
'account' => 'Account does not exist.'
);
$session->setFormErrorFlashData('login', $errors);
header('Location: /login');
}
} else {
header('Location: /login');
}
}
For setting the error messages:
public function setFormErrorFlashData($form, $errors = array())
{
foreach($errors As $field => $message) {
$_SESSION['errors']["{$form}"]["{$field}"] = $message;
}
}
For getting the error messages stored in the session:
public function getFormErrorFlashData($form, $field)
{
if(isset($_SESSION['errors']["{$form}"]["{$field}"])) {
$message = $_SESSION['errors']["{$form}"]["{$field}"];
unset($_SESSION['errors']["{$form}"]["{$field}"]);
return $message;
}
}
Basically for an invalid attempt, after redirect, it should now display the 'Account does not exist' message, and then when the user refreshes the page, it should no longer be there.
What happens is when I comment out the unset() line in getFormErrorFlashData(), the $_SESSION contains the errors, but of course as expected they do persist even after countless page refreshes.
But when it's not commented out, I get a NULL. It seems that $message is also unset, even after attempting to store in it the value of that session key.
I have a bootstrap file that has the session_start() line, it's loaded for every page so I doubt that's the cause?
UPDATE:
index.php (bootstrap file)
<?php
session_start();
date_default_timezone_set('Asia/Taipei');
require_once 'core/Autoloader.php';
use core\Autoloader As Autoloader;
use core\Router As Router;
use core\Dispatcher As Dispatcher;
spl_autoload_register('core\Autoloader::loadClass');
$request_uri = trim($_SERVER['REQUEST_URI']);
$router = new Router();
$route = $router->handleRequest($request_uri);
if (!$route) {
require_once ('./views/errors/404.php');
} else {
$dispatcher = new Dispatcher($route);
$isDispatched = $dispatcher->dispatch();
if (!$isDispatched) {
echo '<div>' .$route['class'] .'/' . $route['action'] . ' not found </div>';
require_once ('./views/errors/404.php');
}
}
I've found the culprit.
When I traced the request logs, showLoginAndRegistrationPage() was being called twice because I didn't realize I also had a .js file attached in the html file with a submit event handler that gets fired too, thus the double page requests.
I removed the file and now it's working.
Thanks for the help!

Angular and Slim framework for Mysql database security

I am new to Angular and Data Api with Slim.
I create data Api with Slim used by Angular. My api code is below:
include 'dbConfig.php';
require 'Slim/Slim.php';
\Slim\Slim::registerAutoloader();
$slim_app = new \Slim\Slim();
$slim_app->get('/getUser','getUser');
$slim_app->post('/updateUser','updateUser');
$slim_app->post('/newUser','newUser');
$slim_app->run();
function getUser(){
global $mysqli;
$result = $mysqli->query("select * from users");
if(!$result){ echo "not connect";}
$rows = array();
while($row = $result->fetch_assoc()){
$rows[] = $row;
}
//echo "<pre>";
print json_encode($rows);
}
My Controller
myApp.controller('Ctrl',function ($scope, $http) {
$http.get('api/getUser').
success(function(data, status, headers, config){
$scope.obj = data;
});
});
When i go to localhost/api/getUser i can see all my json data so that mean anyone could see. how can i only allow my Angular app to use my api so it is secure?
Right,
This is a complicated subject but in it's simplest form you need a login route that returns an API key to your client like so....
NOTE this is very basic code that I use for testing purposes, it's missing proper hashing of passwords and checking for access token expiry plus a fair bit of other security. It's simply to illustrate an easy example.
app->get('/login/:username/:password', function ($username, $password) use ($app, $db) {
//check the login details against the db...
// TODO Include hashing FIX BEFORE PRODUCTION
$db->where("userName", $username);
$db->where("password", $password);
$result = $db->getOne('Users');
if ($db->count === 0) {
setResponseCode(401, array("Error" => "Incorrect login details."));
return;
}
// The way this key is generated needs updating, only for testing purposes.
$key = date("Y-m-d");
$key = $key . $username;
$key = md5($key);
// Need to add expiry time to access tokens....
$expire = date("Y-m-d H:i:s", strtotime('+15 minutes'));
$data = Array ('accessToken' => $key);
$db->where('id', $result['id']);
if (!$db->update('Users', $data)) {
// Internal error - this shouldn't happen!
setResponseCode(500, array("Error" => "Database error."));
return;
}
// Slim by default returns 200 so no need to call setResponseCode
echo json_encode(array("Authorisation" => $key));
});
function setResponseCode($code, $arrayJson) {
global $app;
/*
Function to save repetition of response headers - params are:
Int Code - HTTP Response Code
Array array_json - an array to be serialized into a JSON response ie array("Key" => "Item")
*/
$app->response->setStatus($code);
$app->response->SetBody(json_encode($arrayJson));
$app->response->headers->set('Content-Type', 'application/json');
}
function authorise($key) {
// Middleware for routes that require auth!
//global $key;
global $userId;
global $app;
global $db;
// need to add check for token expiry here!
$db->where('accessToken', $key);
$results = $db->getOne('Users', null, array('id'));
if ($db->count === 0) {
setResponseCode(401, array("Error" => "Authorisation Failure"));
$app->stop();
//return false;
}
$userId = $results['id'];
return true;
}
Your client needs to store the key and add it to the parameters to any request you can either use headers or just simple parameters. Again I'd google slim, API, access token, tutorial as the terms because this is a very basic answer to a complicated subject.
Now you just simply add authorise($key) to the top of any routes that need it, if it's false it'll return 401 and stop any further execution. I would recommend doing some more reading though as this is a very, very basic answer.

Laravel redirect from private method with errors

I have the following code:
public function store(Request $request)
{
$this->validateData($request->all());
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
private function validateData($requestParams)
{
try
{
$validator->validate( $requestParams );
}
catch ( ValidationException $e )
{
redirect()->action('controller#create')->withInput()->withErrors( $e->get_errors() )->send();
exit(); // this causes the withErrors to not be there
}
}
If I remove the exit();, the error messages will appear, but also the store function will be executed (see // store something). I know I can rewrite my code like:
if($this->validateData($request->all()))
{
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
But I don't want the ugly if statement here. There must be a way to redirect with the flash messages without it.
tl;dr
Update your private method code like this to make the redirection work with $errors variable visible:
private function validateData($requestParams)
{
try
{
$validator->validate( $requestParams );
}
catch ( ValidationException $e )
{
$resp = redirect()->action('WelcomeController#index')->withInput()->withErrors($e->get_errors());
\Session::driver()->save();
$resp->send();
exit();
}
}
explaination
When exiting in the middle of your controller, there are some job which is executed in the application termination will not be execute anymore. In your case, the Session middleware terminate method will not be called. Let see its content (ref):
public function terminate($request, $response)
{
if ($this->sessionHandled && $this->sessionConfigured() && ! $this->usingCookieSessions())
{
$this->manager->driver()->save();
}
}
Now, look at the save method of our Session driver (ref)
public function save()
{
$this->addBagDataToSession();
$this->ageFlashData();
$this->handler->write($this->getId(), $this->prepareForStorage(serialize($this->attributes)));
$this->started = false;
}
As you can see, your flash data is only be saved when the Session middleware terminates successfully. With your old code, the flash data will be lost!
What I do with my code is calling the save method manually before sending our response to the browser. However, I still recommend you bring the redirection to the public controller method.
Well I don't see any problem using the if statement there. Basically you do not stop the code execution, so thats why the store function is executed, even if your validation fails. The redirect function just sends a header with the redirect location, it does not abort the code after it to be executed. It works with exit(), because it sends the redirect headers and stop the rest of the code to be exeuted.
This is not ugly, it is clean and clear and I suggest you to use this. This is a good example of right if statement usage - if one conditions i met then do this. In your case if your validation passes, just store the object. (Just remember to modify your validate function to return true or false)
if($this->validateData($request->all()))
{
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
}
The other possible solution is to use try .. catch block like this
public function store(Request $request)
{
try {
$this->validateData($request->all());
// store something
return redirect()->action('controller#index')->withMessage( 'Saved Successfully' );
} catch ( ValidationException $e ) {
return redirect()->action('controller#create')->withInput()->withErrors( $e->get_errors() );
}
}
private function validateData($requestParams)
{
// Your validation logic here
$validator->validate( $requestParams );
}
you just forgot to 'return' after the validation exception :D, then you would not have to 'exit;'

email contact form - can reach success page manually even if form wasn't submitted - how to stop

Here's my setup for an email contact form:
www.example.com/includes/contact_form.php
www.example.com/includes/contact_submit.php
www.example.com/contact/
/contact/ includes contact_form.php, and the form points to contact_submit.php to run.
When contact_submit.php successfully sends the mail, it does a redirect back to /contact/ but includes a $_GET variable.
header('Location: /contact/index.php?success=yup');
Then in contact_form.php I have:
if (isset($_GET['success'])) { echo 'Your message has been received etc'; exit(); }
Everything works fine. I made it this way so that the form couldn't be F5/refresh resubmitted, and it is successful in that.
However, anyone can access the success page at any time by manually entering the url, even if they don't submit the form. Is there any way around that?
Ofcourse.
Use sessions for that:
class ResponseLog {
private function __construct(){}
public static function hasMessages(){
return (isset($_SESSION['response']['messages']) && !empty($_SESSION['response']['messages'])) ? true : false;
}
public static function setResponse(array $response){
$_SESSION['response'] = $response;
}
public static function getLastResponse(){
$response = isset($_SESSION['response'])) ? $_SESSION['response'] : null;
#unset($_SESSION['response']);
return $response;
}
}
And use it like this:
if(isset($_POST['form'])){
//validation and all to proccess request goes here
if(!$valid){
$response = array('request' => $_POST,'messages' => array('Incorrect email','Please enter forename'),'url' => '/my/form/where/it/happen/');
}
else {
$response = array('messages' => array('Success'),'url' => '/my/form/where/it/happen/');
}
ResponseLog::setResponse($response);
//redirect to contact form
}
In success page or fail:
if(ResponseLog::hasMessages()){
$response = ResponseLog::getLastResponse();
foreach($response['messages'] as $message){
echo $message;
}
}
With this you can store everything user does and can work with data as you need.
I hope this help :)
Warn: I wrote it from mind, so it's untested code and it can be implemented better it's just for view how to work with user session and responses.
PS: But is a lot of ways how to do it, for example see flash messages in some framework and you will be see how it works with sessions etc.
You can use the referrer page if you want, and throw an error or redirect if it is not the form page, but it is not so good. You can also check whether the fields and form name have been posted.
$_SERVER['HTTP_REFERER']

Categories