Using CakePHP 1.3 we have a booking system for hotel rooms. A check-availability form should bring the user to a secure payment page (https://secure.domain.com/bookings/payment). After making the payment, the user gets a confirmation page (secured is also ok), but from here, any links in our header/footer should take the user back to the non-secured domain (http://domain.com).
Currently we have our SSL UCC Cert set up for the domains https://secure.domain.com and https://domain.com. We have also hard coded the check-availability form to run the action https://secure.domain.com/bookings/payment. Thus, we can get the user to get in to the HTTPS secured area, but not back out unless we hard code all our links in that section.
Cake's security component is quite confusing and thus I am looking for the best solution to make this happen.
Can Cake's Security component be used for HTTPS payment pages, make life easier, and keep the code more CakePHP standardized? Any other suggestions?
this is a pretty good way to go: http://techno-geeks.org/2009/03/using-the-security-component-in-cakephp-for-ssl/ so you won't even have to hard code anything.
I used the example from http://techno-geeks.org/2009/03/using-the-security-component-in-cakephp-for-ssl/ but found it problematic. I ended up adding the following to my app_controller.php.
The code below redirects HTTPS to www.example.com and HTTP to example.com. If a user is logged in (see $loggedUser), it forces HTTPS for every connection.
// Pages requiring a secure connection.
$secureItems = array();
// beforeFilter
function beforeFilter() {
// Your logic...
$this->__checkSSL();
}
/**
* Check SSL connection.
*/
function __checkSSL() {
/** Make sure we are secure when we need to be! **/
if (empty($this->loggedUser)) {
if (in_array($this->action, $this->secureItems) && !env('HTTPS')) {
$this->__forceSSL();
}
if (!in_array($this->action, $this->secureItems) && env('HTTPS')) {
$this->__unforceSSL();
}
} else {
// Always force HTTPS if user is logged in.
if (!env('HTTPS')) {
$this->__forceSSL();
}
}
}
/**
* Redirect to a secure connection
* #return unknown_type
*/
function __forceSSL() {
if (strstr(env('SERVER_NAME'), 'www.')) {
$this->redirect('https://' . env('SERVER_NAME') . $this->here);
} else {
$this->redirect('https://www.' . env('SERVER_NAME') . $this->here);
}
}
/**
* Redirect to an unsecure connection
* #return unknown_type
*/
function __unforceSSL() {
if (strstr(env('SERVER_NAME'), 'www.')) {
$server = substr(env('SERVER_NAME'), 4);
$this->redirect('http://' . $server . $this->here);
} else {
$this->redirect('http://' . env('SERVER_NAME') . $this->here);
}
}
Related
Form redirect for confirmation can be currently managed using one of these two options:
1/ Flash message: using flashbag on the form page or another page like this:
$this->addFlash('success', 'Thank you');
return $this->redirectToRoute('confirmation_page');
2/ Confirmation page: using a dedicated confirmation like this:
return $this->redirectToRoute('confirmation_page');
BUT using option 2 makes the confirmation_page directly accessible from the browser without having submitted the form before. I am currently using flashbag mechanism to fix it by adding a $this->addFlash('success', true); before the redirection in the form and then checking the flashbag content in the confirmation page so that the route is accessible only once after being successfully redirected from the form.
Is there any best practice or more appropriate way to manage it?
/**
* #Route("/confirmation", methods="GET", name="confirmation_page")
*/
public function confirmation(): Response
{
$flashbag = $this->get('session')->getFlashBag();
$success = $flashbag->get("success");
if (!$success) {
return $this->redirectToRoute('app_home');
}
return $this->render('templates/confirmation.html.twig');
}
Flash Message is designed to display messages. Instead, use sessions in your application.
When submitting the confirmation form, create a variable in the session before the redirect
$this->requestStack->getSession()->set('verifyed',true);
return $this->redirectToRoute('confirmation_page');
Use the created variable in your method
public function confirmation(): Response
{
if (!$this->requestStack->getSession()->get('verifyed')) {
return $this->redirectToRoute('app_home');
}
return $this->render('templates/confirmation.html.twig');
}
Don't forget to inject the RequestStack into your controller
private RequestStack $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function actionDone($id)
{
if ($model = $this->findModel($id)) {
$model["status"] = 3;
if ($model->save()) {
return $this->redirect(['test/index']);
}
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
It works only for the first time for each link. After that its just redirects to the 'test/index' without doing anything. Seems like browser (or smth else) remember, that if we open, for example, page site.com/?r=test/done&id=2 it should redirect to 'test/index' anyway.
Why is that? How can I fix it?
I even tried put die(); in the beginning of the method - anyway it redirects to 'test/index' until I use different link with another ID.
Thanks!
I am trying to build a server-to-server auth flow using the Facebook PHP SDK and no Javascript, as outlined here. So far, I have successfully created a LoginUrl that lets the User sign in with Facebook, then redirect back to my App and check the state parameter for CSFR protection.
My Problem is, that I can't seem to get the API-call working that should swap my Auth Code for an access token. I pillaged every similar problem anyone else that Google was able to find had encountered for possible solutions.
Yet the end result was always the same: no access token, no error message that I could evaluate.
Researching the topic yielded the following advice, which I tested:
The URL specified in the App Settings must be a parent folder of $appUrl.
use curl to make the request instead of the SDK function api()
I've been at this for 2 days straight now and really could use some help.
<?php
require '../inc/php-sdk/src/facebook.php';
// Setting some config vars
$appId = 'MY_APP_ID';
$secret = 'MY_APP_SECRET';
$appUrl = 'https://MY_DOMAIN/appFolder';
$fbconfig = array('appId'=>$appId, 'secret'=>$secret);
$facebook = new Facebook($fbconfig);
// Log User in with Facebook and come back with Auth Code if not yet done
if(!(isset($_SESSION['login']))){
$_SESSION['login']=1;
header('Location: '.$facebook->getLoginUrl());
}
// process Callback from Facebook User Login
if($_SESSION['login']===1) {
/* CSFR Protection: getLoginUrl() generates a state string and stores it
in "$_SESSION['fb_'.$fbconfig['appId'].'_state']". This checks if it matches the state
obtained via $_GET['state']*/
if (isset($_SESSION['fb_'.$fbconfig['appId'].'_state'])&&isset($_GET['state'])){
// Good Case
if ($_SESSION['fb_'.$fbconfig['appId'].'_state']===$_GET['state']) {
$_SESSION['login']=2;
}
else {
unset($_SESSION['login']);
echo 'You may be a victim of CSFR Attacks. Try logging in again.';
}
}
}
// State check O.K., swap Code for Token now
if($_SESSION['login']===2) {
$path = '/oauth/access_token';
$api_params = array (
'client_id'=>$appId,
'redirect_uri'=>$appUrl,
'client_secret'=>$secret,
'code'=>$_GET['code']
);
$access_token = $facebook->api($path, 'GET', $api_params);
var_dump($access_token);
}
The easiest way I found to do this is to extend the Facebook class and expose the protected getAccessTokenFromCode() method:
<?php
class MyFacebook extends Facebook {
/** If you simply want to get the token, use this method */
public function getAccessTokenFromCode($code, $redirectUri = null)
{
return parent::getAccessTokenFromCode($code, $redirectUri);
}
/** If you would like to get and set (and extend), use this method instead */
public function setAccessTokenFromCode($code)
{
$token = parent::getAccessTokenFromCode($code);
if (empty($token)) {
return false;
}
$this->setAccessToken($token);
if (!$this->setExtendedAccessToken()) {
return false;
}
return $this->getAccessToken();
}
}
I also included a variation on the convenience method I use to set the access token, since I don't actually need a public "get" method in my own code.
I made a controller plugin to handle authentication. If a user tries to access a page without being logged in, it saves the route of the page he was trying to access, forwards to the login page, and then when the user logs in, it redirects him to where he was trying to go.
But if the user tries to access a nonexistent page while logged out, then it still forwards to the sign-in form, but when the user signs in, it brings up an error.
How do I bring up a 404 error before the user signs in? I think I need to detect whether the route is valid within dispatchLoopStartup(). How do I do that? Or is there some other way of doing this?
class Chronos_Controller_Plugin_Auth extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$request->setParam('userName', $auth->getIdentity());
} else {
$request->setParam('origModule', $request->getModuleName())
->setParam('origController', $request->getControllerName())
->setParam('origAction', $request->getActionName())
->setModuleName('default')
->setControllerName('sign')
->setActionName('in');
}
}
}
Try something like this:
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$dispatcher = Zend_Controller_Front::getInstance()->getDispatcher();
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$request->setParam('userName', $auth->getIdentity());
} else if ($dispatcher->isDispatchable($request)) {
$request->setParam('origModule', $request->getModuleName())
->setParam('origController', $request->getControllerName())
->setParam('origAction', $request->getActionName())
->setModuleName('default')
->setControllerName('sign')
->setActionName('in');
}
}
I have a Drupal site and a Zend application. The main thing is the Drupal site, where the users are stored & everything.
I want my users to be automatically logged in to the Zend app when they log in on Drupal. The problem is that Drupal changes the session cookie to SESS* where * is some random (EDIT: not random, but based on protocol and domain) string.
Is there any way I can tell Zend to use this cookie as a session identifier and to log the user automatically?
You have to write your own authentication adapter:
class YourApp_Auth_Adapter_DrupalBridge implements Zend_Auth_Adapter_Interface
{
/**
* #return Zend_Auth_Result
*/
public function authenticate()
{
// Check if the Drupal session is set by reading the cookie.
// ...
// Read the current user's login into $username.
// ...
// Create the authentication result object.
// Failure
if (null === $username) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND, null);
}
// Success
return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $username);
}
}
Then process your authentication:
$adapter = new YourApp_Auth_Adapter_DrupalBridge();
$result = Zend_Auth::getInstance()->authenticate($adapter);
if ($result->isValid()) {
// User is logged in
}