views:

40

answers:

2

I am wondering how to best deal with a return url in a login form. I think a good way is probably how it is done here. That is to urlencode the current url and send it as a get parameter to the login controller.

This can then be urldecoded in the login controller. But what then? I looked at the StackOverflow login page, and I couldn't find that url anywhere. Where is it stored? How does it know where to go when the login is done? Is it stored in a cookie? Session variable? Or something else?

I guess things can be done a bit differently than in PHP, but anyways. What is a good way to do this?

A: 

If it's a static URL, then you can include it on the form as a hidden field and then redirect to it in your code, i.e.

<input type='hidden' name='return' value='/thankyou.html' />

Then in your submit function ...

header("Location: $_POST['return']");

In production, you'll obviously want to encrypt the URL in the hidden field, and then decrypt it and validate it as a good URL before invoking the header() function, but that should give you the idea.

Some folks use cookies, others have a lookup table in the DB. Doesn't really matter where you store the URL, it's just important that you scrub it before you header() it.

EDIT: Scrubbing

function isValidURL($url) {
   return preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $url);
}

if ( isValidURL($_POST['return']) )
    header("Location: {$_POST['return']}");

As I said earlier, if you want to be even more careful, you can encrypt/decrypt the actual URL prior to showing on the form and prior to validation. There's a ton of good encrypt/decrypt libraries out there.

The take home lesson is to never actually "do anything" (such as insert values in a database, run a shell command, redirect to a URL, etc) with data that comes in via form. Someone could manipulate that hidden field and inject code into your app or db by being tricky. There's thousands of posts an examples of things that people will do through form injection.

Coach John
How would you scrub it?
Svish
Run it through a simple URL validation function, like this: function isValidURL($url){return preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $url);}
Coach John
Sorry that was ugly, I'll make a new reply and format it.
Coach John
Would that only accept absolute urls?
Svish
Wouldn't it be enough to store it in the server-side session? Then the user can't tamper with it. If you wish you can atleast check if it's in your expected domain before redirecting to it, eg if (strpos($url, $mydomain) === 0) { redirect }
Fanis
That's what I alluded to in my original comment about the URL originating somewhere other than as a field in the form, like something generated server side or pulled out of a DB (or even a session). I was addressing the "how to" of the original question rather than trying to lead him down a different logical path. (New here, don't want to overstep)
Coach John
Use Validate::url($url) and $this->request->redirect(). If there is no `return` key in $_POST, redirect to main page.
biakaveron
@biakaveron: Problem is that the Validate::url method only takes full urls starting with `http://`.
Svish
A: 

If it were me, I would set the return URL in a session. This way you can calculate the appropriate URL and keep things separate from the presentation layer. Upon detecting a return URL is set, and a post object is present, you can set the location header and exit the script. Example...

// You have the URL to return to (could be a constantly updated session variable
// or simply set when a certain page is accessed via HTTP_REFERRER - it's open)
// in a MVC application (like stackoverflow) you could add this to the controller
// for any view with return functionality.
$_SESSION['RETURN_URL'] = $Url;

// Now you have validated and processed a form (from the model). If there is a 
// return url set, we redirect to it. Otherwise, we follow the default action of 
// the form
if ($FormValidatedAndSubmitted)
    returnToURL();

function returnToURL(){
    if (isset($_SESSION['RETURN_URL'])){
     header("Location: " . $_SESSION['RETURN_URL']);
     unset($_SESSION['RETURN_URL']);
     exit();
    }
}
Mahdi.Montgomery