I was wondering if what I'm using is safe:
Header(“Location:”);
I want to use it like this:
$user would be the user's session and 1 would mean admin. (Just an example.)
if($user != 1){
header("location: index.php"); }
So would this stop users from using that page, including downloading files, looking at the source of that page?
Because my adminpanel is going to have downloads and also inserts for the homepage..
If it's not safe to use, or the way I'm using it. What should I use instead?
Thanks.
header() is safe to use, but it means you have to include that call in every page you don’t want a non-admin to view.
A better way would be to have an authenticated user class that handles this. The benefit is, it DRYs your code.
<?php
class User
{
public function __construct()
{
// create your user however, i.e. fetch a record from your database
}
public function isAdmin()
{
return ($this->user->role == 'admin');
}
public function redirect()
{
header('Location: index.php');
exit;
}
}
And then use it in your code:
<?php
$user = new User();
if (!$user->isAdmin()) {
$user->redirect();
}
// continue with your page as normal; user here is an admin
Don't forget to die() after setting a Location header. That's what really stops people from using pages they're not supposed to.
you need put exit; after header
I feel like a better way would be to exit the page and prevent any other code execution. I'm not sure but a user might be able to ignore header redirect requests if they wanted to.
I've always just added a little snippet like "Sorry, this is only available to Administrators" and then just return; or exit;
edit: great googly moogly you guys are quick to the draw.
So what happens here is that by setting the Location tag, you are in fact giving back a HTTP 30x request, pointing the users browser to the new page. But it's in the hands of the browser to respect it. If the browser/user is malicious, he might just ignore it.
So you need to stop your output right after sending the Location header with die(). Than it's safe - the browser doesn't get any data it shouldn't get
use die(header("Location:...")); kill the page
Related
I have a framework and I think I'm following something like the MVC pattern: A framework (the model) an index page that controls the input (the controller) and the views pages (that are included inside main.php/the main html)
I read a lot about structure and logics, to write a good application. I read many comments like "Why are you outputting anything if all you are going to do is try and redirect the user to another page?". Well the answer is, the most common case: redirect after the user successfully logged in. Do I need to print something? Of course, the whole main page with a login form/post. How I'm supposed to do that redirection??
So I'm a bit confused about logics and structure of the application. How do you store all the output and do the header redirection without printing anything?
I was thinking about using javascript to do the redirection but I also read comments saying; "if you write good code (following a good logic/structre), you won't need to use hacks like javascript redirection". How is that even possible?
Because the php output_buffering should not be enabled.
I have the output_buffering enabled, and I can use header (after output) without any problem. If I use the javascript redirection the whole page reloads, but using header it just loads the content (the views content that are included in main.php).
So how do you do this without output_buffering?
If you want to redirect to a success page AND pass messages - say, after a successful login - an easy solution is to use "flash" sessions, where you store a message in a SESSION and then, as soon as it's used, you discard it. You don't need to sore anything in the output buffer for this.
This is a very basic example, but should give you the gist of it.
login.php
if($login_successful) {
// put your message in the session
$_SESSION['message'] = 'Login Successful';
// redirect to the success page
header('location: success.php');
}
success.php
<?php
session_start();
// check if $_SESSION['message'] exists
if(isset($_SESSION['message'])) {
// print the message
echo $_SESSION['message'];
// clear the session
$_SESSION['message'] = null;
}
Looks like you are mixing up some things here. What you are talking about are actually two different requests. Either the user wants to view the main page, or he wants to log in using that form on your main page. In your index.php you would have something like this (pseudocode):
if (isLoginRequest) {
// user wants to log in
if( validateLogin($loginFormData) ) {
redirect('successful');
} else {
displayLoginError();
}
} else {
// user wants to view main page
echo main.html
}
Update to answer the question in the comments: The better alternative would be to leave your form validation stuff in login.php and refer to that in your login form <form action="login.php" .... Then in your login.php you would have something like this:
if (loginSuccessful) {
redirect('success.php');
// no need to call die() or whatever
} else {
setFlashMessage('Login failed'); // set a flash message like timgavin described
redirect('index.php')
// also no die() or whatever
}
index.php then is responsible to display your main page and, if set, rendering the flash message from a failed login attempt.
Simple solution: Move the login post script from login.php to another file (login_post.php). The same for other scripts using header() after dom output. (no need to change the form action="")
In index.php:
$url = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
//some more security checks like esc_url() (non-php function)
if ($url == '/login') {
include('header_pages/login_post.php');
}
// all these includes before including main.php
// where views pages are included and the DOM output starts
Since header() is inside the post script, no more headers already sent errors (And output_buffering off, of course).
Same for logout page that is currently being included inside main.php
Thanks to the other answers, they helped me finding this solution.
I'm using the below function to redirect a person after specific task (eg.: after login, after logout, after searching etc.)
code is below:
<?php
class common {
/* Redirect to another page
* $url= Url to go
*/
function redirection($url){
header("location: $url");
exit();
}
// Some other function below
?>
But now I'm dealing this class with many project of different host (MLM project). I have a problem now. With some server it works as i expected, but in some other server, it's not redirecting. If i enable error_reporting(E_ALL); i found a notice that headers are already send. So I'm in confusion that what can I do now instead of header() function. Also i tried the below code
<?php
function redirection($url){
echo "<div align='center'><a href='$url' target='_top'><img src='../img/proceed.jpg' alt='Proceed>>' align='absmiddle' border='0'></a></div>";
exit();
}
?>
But it is not desirable as everybody wants automatic redirection. My servers are windows and linux both. Please help me anyone
well, this situation is very common, then you can simple turn on output buffering (the output will be stored in an internal buffer).
Use ob_start(); in the very first line of your application
<?php
class common {
/* Redirect to another page
* $url= Url to go
*/
function redirection($url)
{
header("location: $url");
exit();
}
// Some other function below
}
?>
<?php
ob_start("redirection");
// Your Common Class Page
include("Common.php");
// some code
ob_end_flush(); // turn off output buffering
?>
One way to deal with this is to test if the header has already been sent before calling header(location). You could use mix both solutions:
<?php
class common {
/* Redirect to another page
* $url= Url to go
*/
function redirection($url){
if (!headers_sent()) {
header("location: $url");
} else {
echo "<div align='center'><a href='$url' target='_top'><img src='../img/proceed.jpg' alt='Proceed>>' align='absmiddle' border='0'></a></div>";
}
exit();
}
// Some other function below
?>
This way if the headers haven't been sent, you redirect automatically. If they have, you ask the client to click.
This is the reason why when you see a redirection notice in most websites, it also includes a sentence stating - if you are not redirected automatically, please click here...
Hope this helps.
Good luck!
If headers have already been sent, it is likely because content has already been written out to the screen (via an echo, print, or similar). Since your class has no control over what came before it was instantiated and the function was called, it seems unlikely that you can do much to avoid your client PHP (what calls your class) from writing anything out before. Either use Javascript or use Apache redirects.
I would try using:
header("Location: ".$url, TRUE, 302);
If you want to use a different method, or called "refresh" method,
header("Refresh:0;url=".$url);
Both would work in every case. The problem with your header is, you need to let them know it's a 302 redirect, as well as set TRUE to replace the existing headers. If header is already set, you need to replace it using TRUE boolean.
302 is also the common HTTP response code for redirection, which needs to be specified when you are trying to redirect using header.
The Refresh method works fine as well, though it has compatibility issues with older browsers.
http://en.wikipedia.org/wiki/HTTP_302
http://php.net/manual/en/function.header.php
the easiest way is to do it through client side. javascript...
window.location= url
I have several pages inside an AJAX directory. I don't want these pages accessible directly so you cannot just type in the URL of the page within the AJAX directory and access it. I "solved" this by using a PHP session on the page that calls it as follows:
Main page:
<?php
session_start();
$_SESSION['download']='ok';
?>
and on the ajax page I have this:
<?php
session_start();
if($_SESSION['download']!=='ok'){
$redirect='/index.php'; //URL of the page where you want to redirect.
header("Location: $redirect");
exit;}
?>
The only problem is that if a user goes through the correct process once, the cookie is stored and they can now access the page directly. How do I kill the session once they leave the parent page?
thx
why use session ?
if i understood what you want:
<?php /// Is ajax request var ?
if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
if (strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])=="xmlhttprequest") {
// do your ajax code
} else {
// redirect user to index.php since we do not allow direct script access, unless its ajax called
$redirect='/index.php'; //URL of the page where you want to redirect.
header("Location: $redirect");
exit();
}
} ?>
A really simple solution is to open up each of the files you want to protect from direct URL entry & add the following to the top:
<?php if (isset($_GET['ajax']) != true) die();?>
Now get rid of your redirect script since it's useless now. You don't need to use sessions for this. Every time you request a page, use it's direct URL, just add ?ajax=1 to the end of it.
By adding the ?ajax=1, PHP will set a key of 'ajax' to the $_GET global variable with the value of 1. If ?ajax=1 is omitted from the URL then PHP will not set a key of 'ajax' in $_GET and thus when you check if it's set with isset() it will return false, thus the script will die and not output anything. Essentially the page will only output data if ?ajax=1 is at the end of the URL.
Someone could still "spoof" the URL and add '?ajax=1' themselves, but that is not the default behavior for people or web browsers. If you absolutely need to prevent this then it will be much more complicated, e.g. using templates outside of a publicly available folder. Most other "simple" solutions will have the same "spoofing" potential.
There's really no way to accomplish this with a 100% certainty - the problem is, both AJAX and regular web browser calls to your web site are using the same underlying protocol: HTTP. If the integrity and security of your site depends on keeping HTTP clients from requesting a specific URL then your design is wrong.
so how do you prevent people from directly accessing files inside certain directories while still letting the site use them??
Create a controller file. Send all AJAX requests to this controller.
ajax-control.php
<?php
$is_ajax = true;
include "ajaxincludes/test.php";
// ... use the ajax classes/functions ...
ajaxincludes/test.php
<?php
if (!isset($is_ajax) || !$is_ajax)) {
exit("Hey you're not AJAX!");
}
// ... continue with internal ajax logic ...
If clients try to access the file directly at http://mysite/ajaxincludes/test.php they'll get the error message. Accessing http://mysite/ajax-control.php will include the desired file.
I don't think there is a surefire way to do what you are asking, since HTTP request headers can be faked. However, you can use $_SERVER['HTTP_REFERER'] to see if the request appears to be coming from another page on your site.
If the rest of the security on your site is good, the failure of this method would not grant the user access to anything they were not already able to access.
I've never tried this but maybe you could do something with jQuery's .unload() and then call a PHP page to unset() the session.
Why not (on Ajax page):
session_start();
if($_SESSION['download']!=='ok'){
$redirect='/index.php'; //URL of the page where you want to redirect.
header("Location: $redirect");
exit;
}
// do whatever you want with "access granted" user
// remove the download flag for this session
unset($_SESSION["download"]);
I have a few pages that require login, so all controllers that link to these pages start with
$this->checkSession();
//...rest of the code
CheckSession should verify the session is still live, otherwise display a message and stop the execution of the rest of the code in the controller:
function checkSession()
{
if (!$this->session->userdata('is_logged_in'))
{
//the session has expired!
$data['main'] = 'confirmation_message';
$data['title'] = "Session expired";
$this->load->vars($data);
$this->load->view('template');
exit();
}
}
.
I was expecting these instructions to happen in sequence, but I only get a blank page.
How can I make sure exit() gets executed only after all views are loaded?
In this case Pedro is correct. If they are not logged in just redirect them, it's even better if you can use Public/Admin named base controllers to stop you having to do this in each separate protected file.
Generally speaking though, if you use exit() it will stop the Output library for running. If you just want to stop the current controller from executing but allow output of the controller you can use return in exactly the same way.
function checkSession()
{
return (bool) $this->session->userdata('is_logged_in');
}
Then simply:
if(!$this->checkSession())
{
//the session has expired!
$data['main'] = 'confirmation_message';
$data['title'] = "Session expired";
$this->load->vars($data);
$this->load->view('template');
return;
}
exit() should only ever be used if you really want instant death of your application's execution for debugging, error reporting, etc.
In this case you should not use exit, what you should do if the session is not valid is redirect your app using example:
redirect('/init/login/','refresh');
I had a similar problem. Where I wanted to stop the user to due to no login. But I wanted to offer a list of links for them not simply redirect them to a login page. I am using CI version 1.7.2 and the $this->_output() $this->display->_output() and $this->output->_display() solutions did not work for me. I was however to get my results using the $this->output->get_output() function.
$this->load->vars($data);
$this->load->view('template');
die($this->output->get_output());
$this->output->_display();
exit();
Is the correct answer! Thanks to Sam Sehnert... It's hidden in the comments so thought I'd re-post.
I don't know enough about codeigniter's workflow but it seems to me that you want to redirect to the login page instead of trying to render it. Evidently, none of the code you supplied sends the template to the browser by the time exit() is called.
exit() cuts your scrip there and the actual _output() function of the controller is never called. What you need to do is add action in one of your controllers for example the user login screen and redirect there. You can use the flashdata function from the Session - http://codeigniter.com/user_guide/libraries/sessions.html to pass your message and then catch it inside your view and display it.
Another way which is not very smart but should work is to forcefully call the output function.
function checkSession()
{
if (!$this->session->userdata('is_logged_in'))
{
//the session has expired!
$data['main'] = 'confirmation_message';
$data['title'] = "Session expired";
$this->load->vars($data);
$this->load->view('template');
$this->_output();
exit();
}
}
Actually in the newest CI function to manually call output class is
$this->display->_output();
and don't be worried - it handles caching, content will also be properly gzipped if you set so in config
I usually add and extended controller with login logic that handles login functions, so that if a normal controller is one that is needing an auth then the login method is called automatically and the original content is not displayed. It's a good solution if you would like to stay on the page the user tried to access without redirecting (and then posting him back to the same page)
Put the code in a variable and write it.
$html = $this->load->view('template',null,true);
echo $html;
exit();
I just read this article on tdwtf.com. Generally, it describes an archiving bot destroying things because it ignores headers. I then realized that I don't know how to do security in a page WITHOUT headers. Therefore my question is:
What security measures can i take besides using headers?
I develop mostly in php, so I'm familiar with header("Location: ") function. But what else is out there?
Ideally I'm looking to replace the logic of
if (!$something_important) header("Location: somehereharmless.php");
with something else (more) secure?
This one works pretty well
if (!$something_important) {
header("Location: somehereharmless.php");
exit();
}
Even if it's bot, so it doesn't respect Location, you will call an exit so the execution flow is halted anyway, so no harm
header: location is fine, as long as you include an exit at the end.
You might also want to include a link or something.
I usually use something like this:
<?php
function redirect($url)
{
header('Location: ' . $url);
exit('Redirecting you to: ' . $url . '');
}
redirect('somepage.php');
?>
This way people can't bypass the redirect, and know that they should be redirected.
[Edit]
Also, always use POST when deleting stuff. It is very easy to create a fake GET (for example <img src="http://www.example.org/action.php?do=SetAsAdmin&userid=MyUserId" />).
Make sure all your gets are idempotent
Idempotent means that doing same request many times has the same effect as doing it once.
I'd say that if you have a PHP script that performs some action which only, say, logged-in users should be able to perform, you must put the check for being logged in right there in the very same script, so you can look at it at a glance and see that the code is secured. My rule is that there are only two valid patterns for protecting secured code:
if (user_is_authorized()) {
// restricted code here
}
or Alekc's
if (!user_is_authorized()) {
// send headers or whatever if you want
exit();
}
// restricted code here
To be perfectly honest, I was rather shocked... or at least disappointed... when I read that article - I can't understand how someone came to the conclusion that a website could be secured with HTTP headers. A header is nothing more than some text that your server sends out. It's an instruction that may be followed or ignored by the client at will (at least, you have to think about it that way for security purposes). As far as I'm concerned, outgoing (response) HTTP headers are pretty much useless for security. (This is not counting things like HTTP authentication, where you send a header and get one back in response... but in that case it's the contents of that reply header that you base your security on, not the header you sent out.)
The reason for the incident reported in the link you provided was the missing exit; statement after the header();. The bot can't do any harm if the script stops.-
if (!$something_important) {
header("Location: somehereharmless.php");
//close all your db connections and other stuff you need to end..parhaps calling a function?
die("If the redirect doesnt start in 3 seconds, please click here");
}
Your solution is
<?php
die($errormessage);
Die will just halt your script, not go through start, don't collect any data that you shouldn't.
Addition to Alekc's answer. If you have many header("Location:") statements and the person qualifies for them all. The last one will fire.
if($foo && $bar)
{
header("Location: somehereharmless.php");
}
if($foo && $baz)
{
header("Location: someotherplace.php");
}
So if that user has all 3 variables set, he will get redirected to someotherplace.php. Unless you have an exit(); or a die(); after the header();