I have several forms brought in via jQuery .ajax funciton. In the parent page I start a session like this
<php
session_start();
$_SESSION['authenticated'] = 'yes';
?>
then in the form that is loaded have a check like this:
<?php
session_start();
if($_SESSION['authenticated'] != 'yes') {
header("Location: http://www.google.com");
}
?>
I know its not the best, but it's an attempt to stop people form accessing the forms directly. The problem is that if you go to the parent page, then you can enter the form URL and get there because the session was started when you hit the parent page. How can I destroy the session or remedy this issue?
Effectively, you can't.
To make it more complicated, don't request the form URLs directly. Try to request authorize tokens per request of the main page:
If you generate the main page and you know the form to be requested beforehand, then generate tokens e.g. using md5(time().rnd()), associate each with one you your forms and save the association in your session
Then, your JS code won't request the form URLs, but a factory script using a token injected into the JS code
If you find the token in your saved association in your session, emit the form and delete the token in your session.
This way, each form can only be requested once by one preceding call of the main page.
Note, that this isn't fully safe too: If a user requests the URL of the main page using wget, he can request each form once.
You can check $_SERVER['HTTP_REFERER'] in your form .php code to see where the request is coming from. An AJAX call will set the HTTP_REFERER to the page it is called from.
if (strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false) {
die();
}
It's not a bulletproof solution. Any page that is publicly accessible can be retrieved by an automated script.
Related
I have a PHP site (with CodeIgniter) that includes a registration form. I have a page with some details, which links to the form on a separate page. The form posts to a third URL which does the processing and redirects back to the first page if it's successful (or the form page if not).
Currently I am adding a parameter for success: example.com/page?success=1 which shows a success message. The problem is that some people have been sharing this URL (and clicking the Facebook Like button) so when another user opens that URL they see a message "thanks for registering!" which they obviously haven't done yet.
I thought this was the standard way of doing forms (submitting to one URL and redirecting to another) but is there a better way? I don't want to post back to the same page because then you get the POSTDATA warning when trying to reload the page.
You have three ways to do this
The way you're using
Not actually redirecting but sending request(s) with AJAX
SESSION (or, in edge case, cookies)
If you select to use SESSION, you can just assign a session variable to true
$_SESSION['registered'] = true;
and checking it on the first page
if (isset($_SESSION['registered'])) {
unset($_SESSION['registered']);
// shot the message
}
Typically you would set your flag for success in the session to display this message when the next page loads. This is commonly referred to as a Flash Message. You would then check the value/existence of this session flag and show your message or not accordingly. In most frameworks there is built in functionality for this which includes the clean up of the flag on the next request so that the message is only displayed directly after the action generating it is taken.
From the CI Sessions Documentation:
CodeIgniter supports "flashdata", or session data that will only be
available for the next server request, and are then automatically
cleared. These can be very useful, and are typically used for
informational or status messages (for example: "record 2 deleted").
Note: Flash variables are prefaced with "flash_" so avoid this prefix
in your own session names.
To add flashdata:
$this->session->set_flashdata('item', 'value');
You can also pass an array to set_flashdata(), in the same manner as
set_userdata().
To read a flashdata variable:
$this->session->flashdata('item');
If you find that you need to preserve a flashdata variable through an
additional request, you can do so using the keep_flashdata() function.
$this->session->keep_flashdata('item');
You should have some verification checks in your code that handles the processing of the form data to make sure that the required fields are filled out. Otherwise, you should be redirecting to your first page to have the user fill out the form.
Also, this could be handled via AJAX, but that would be a second step to having the proper verification in your form-processing page
HTML:
<form method="post">
<input type="text">
<input name="submitted" type="submit">
</form>
PHP:
if($_POST['submitted']{
//post was submitted process it
if(/*whatever you're doing to the form succeeds*/){
//show success
}
}
POST will not show variables in the URL.
Several solutions here, one would be to check for the form submission and if it hasn't been submitted redirect to the page with the form on it.
ie:
<?php
if (isset($_POST['submit']))
{
// process the form
}
else
{
//redirect to the form itself
header( 'Location: http://www.yourform.com' ) ;
}
?>
I have a jQuery script using .live() to load it's page content.
$('#content').load("content.php?" + id);
Question: How can I deny a user from accessing the file content.php directly via a URL?
I tried to put this code on top of content.php but Access Denied appear in my #content div
if (!empty($_SERVER['SCRIPT_FILENAME']) && 'content.php' == basename($_SERVER['SCRIPT_FILENAME']))
die('Access Denied');
What is the correct way to make sure users can't access my content.php file using a URL?
You could use some sort of hashing. For example if content.php has the parameter id; you add an additional parameter hash which contains the MD5 hash of "'some random string' + id*15". In content.php you check if the hash & id match; if not access denied.
The computation has to be done in PHP (not ajax) because the user must not know the hashing algprithmus.
With this method the user can look up the source code and access the page directly but you can't disallow that completly because the browser need to access the page to show it. But the user can't access pages he hasn't accessed through ajax before. You could use some headers (HTTP_X_REQUESTED_WITH) to prevent most internet users to access the page directly but experienced users will change the header and access it anyway.
If you want to protect the usage you can use a one time key algorithm. Have the server generate a key that the page will contain in a variable or attribute somewhere. Then on the load command you pass the key to content.php like this:
key = $("{some selector to get the key}")
$('#content').load("content.php?id=" + id + "key=" + key);
Once the page makes the call to server using the key the server will expire the key making it unusable. This way only active page requests will have access to your content.php file.
This method is still not bullet proof but would make it more difficult to access the content.php by the user.
Since you're calling a resource via ajax a possible solution is sending a particular header into the request like HTTP_X_REQUESTED_WITH and then detect the header server side like so:
/* AJAX check */
if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) ||
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
die($content);
}
I suggest reading this: Detecting Ajax Events on the Server.
Is there other content on the mainpage that you could reference from the content.php file to verify that you are loading it as part of the mainpage and not a stand alone page? You could also pass something via a session from the mainpage into the content.php page and then remove the item at the end of loading of the content.php.
If the URL is accessible with AJAX request then it can be accessed directly and there is not much you can do about it. You can try to detect if a request is the AJAX request with:
function isAjaxRequest() {
return array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
}
and check for it in the beginning of the script:
if (!isAjaxRequest())
die('Access Denied');
but you shouldn't rely on this check too much because it's rather easy to bypass.
I think I forgetting something in my code but can't find what.
On my server I have simple logging.php file.
If I pass user/password parameters then a new session is created.
If I pass loggout the session is destroyed.
If I pass report the list of current session variables are reported on screen.
If I test the code writing urls in my browser all works fine. First invoke .../logging.php?user=xxx&password=xxx. The session is started and the session variables are reported to me. And finally I destroy the session passing the logout parameter.
If user request a report or a logout and no session exists a HTTP-401 error code is returned to client.
On the other hand I have a piece of JavaScript code that I can inject on web page using a bookmarklet. Once code is injected I show a toolbar where user can write user/password and send to server.
The logging actions seems to works fine, and server returns me a 200 status code, but later if I make a request to logout the server returns me a 401 error, which mean no session exists.
I was using chrome and looking at HTTP request and responses can see that when I logging the server returns in the response different values for PHPSESSIONID.
That means two different AJAX request are considered different sessions. The server seems to not recognize the second request from AJAX as if it was started by the same client.
Repeat, the PHP code works fine if I execute using browser directly but not with AJAX request, so I think I forgetting something in AJAX.
Any ideas?
Thanks in advance.
Update
To be more concise, my problem is calling php from JavaScript. It seems there are no sessions started.
Imagine a very simple PHP code:
logging.php: given a user/password starts a new session and also stores 'user' names as a session variable.
request.php: which returns the user name stored as session variable.
logout.php: which destroys the session.
My first AJAX request start a PHP session. That seems fine because a PHPSESSIONID cookie is returned from server. Also I store the user name as session variable.
The second AJAX request tries to get the user name (stored in the session) but it gets nothing and in addition a new PHPSESSIONID cookie is returned from server.
I know it seems impossible and more when I'm testing using browser url request and works fine, but it's the truth.
I'm forgetting something on AJAX, expiration times or something similar?
Update again
I made some tests and I found the problem but not the solution.
My JS code is injected through a bookmarklet.
When I inject the code in a HTML page from my server, the AJAX requests works fine. The first (logging) request gets a PHPSESSID which is passed in subsequent request to the server.
On the other hand If I load google.com and inject the code, the first (logging) request gets the PHPSESSID too but later it is not sent with next requests.
Anyone has experienced the same issue? which is the problem?
Thanks in advance.
Update again, again
Ok finally I found my problem. Because my JS is injected from a different domain (current page is from domainA and my JS code comes from domainB) cookies are not cross domain, so PHPSESSID can be shared.
A possible soulution is when I logging I will return the PHP session ID in pice of JSON data and use it for subsequent calls.
If I'm correct, you're trying to log in a user by making an AJAX request to a URL, with the username and password provided in the URL? That's not really a safe construction, the password is very vulnerable this way?!
I would advice you to implement jQuery, and transer the login details using the $.POST command:
http://api.jquery.com/jQuery.post/
Make sure all your files (also those requested by AJAX) contain session_start(); on top of the file.
When every file contains session_start(); and you're using the same $_SESSION variables to check if a user is loggedin, it should work!
Are both of your AJAX requests coming from the same page? The requests are Asynchronous, so it may be that the "logged in?" request is returning its result before the "log in" request goes through.
From what you have asked, I hope your code is (at its beginning more or less) something like:
A file logging.php like this:
<?php # file : loggging.php
if(!ini_set('session.auto_start'))
// more stuff
if(!empty($_REQUEST['user']) && !empty($_REQUEST['passwd'])) {
session_regenerate_sid(); // This is important (1)
$_SESSION['user'] = $_REQUEST['user'];
// Whatever
}
A file request.php like this..
<?php # file : request.php
if(!ini_set('session.auto_start'))
// Whatever stuff to process data
var_dump($_SESSION);
// Or a nice foreach($v as $i => $x) {
// echo("[$i] => $x\n<br />");
// } instead :)
And your logout.php should read something like..
<?php # file : logout.php
if(!ini_set('session.auto_start')) session_start();
session_destroy();
You are probably not calling either session_start() or you are calling it twice.
To check this out try this: change all your session_start() lines for:
session_name('MYCoolNewName');
session_start();
Now your session should not read PHPSESSID, instead it should be MYCoolNewName.
If it is not, then your problem is the aforementioned.
(1) I put as important session_regenerate_sid() because opened authenticated sessions are a threat out there. I'll demonstrate it with an example.
Alice visits coolwebsite.com/login.php, which gives her a SID which I'll call AliceSID.
Alice tells Bob to visit coolwebsite.com/login.php?PHPSESSID=AliceSID, and when Bob does Alice could log in his account unless Bob's session was regenerated.
I have a form in a page .Now I am submitting the form to the same page(using post).This part works well. But when I refresh the page, the form is submitted again.Any clues on how to fix this.
Do a header redirect to either the same page or another page.
Redirect back to the same page after processing the POST request.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
# process the request ...
# ... and redirect
header('Location: page.php');
}
You can work with sessions. If the form name exists in the session, you don't handle the form again.
$_SESSION['form']['name_of_the_form'] = time();
As the others have noted, you can use a HTTP redirect by modifying the headers sent back to the user's browser. The header() function in PHP allows you to do this. Be sure that you execute this function call prior to outputting anything, otherwise it will fail. It could look something like the following.
<?php
// some code to process the form data here
header('Location: page.php'); // redirect user to page.php
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"]);