Is it possible to check that the $_GET or $_POST values are submitted from specific page?
For example, there is an ajax in page1 submitted value to page2.php?q=abc, and the page2 only accept the q when it is submitted from page1.
If I directly browse to the page page2.php?q=abc, the php will not run unless I submitted the value from page1.
Is it possible to do that?
Edit 1:
Because I can access the page2 and get the result. Don't mention about the session, because I can validate the session to match my needs and the values submitted to php is valid or not.
What I want is to check if the request is sent from specific page or not. If true, then accept the values and process it, else, redirect to homepage or something else.
Edit 2:
My question is, not only values submitted through Ajax, but also direct access, such as href="page2.php?q=abc". I guess token will be the best way to do that, and the query part will validate again.
There are two security checks you could perform while dealing with AJAX:
1) Check if the request it sent through AJAX:
if ( !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' )
{
//AJAX Request Detected
}
2) Hashed tokens:
On the page that's holding the AJAX Request, create a token:
session_start();
$hashed='';
$_SESSION['token'] = microtime();
if (defined("CRYPT_BLOWFISH") && CRYPT_BLOWFISH) {
$salt = '$2y$11$' . substr(md5(uniqid(mt_rand(), true)), 0, 22);
$hashed = crypt($_SESSION['token'], $salt);
}
This is using the blowfish algorithm with the crypt() to create hashed string.
Your AJAX function would be like:
$.ajax({
type: "POST",
url: 'page2.php',
data: {
action: '<?php echo $hashed;?>', //pasted the hashed string created in PHP
q: 'test'
},
success: function (data) {}
});
Upto you whether you want to use $_GET or $_POST method.
And then on the second page which is receiving the AJAX request, you do:
session_start();
if(crypt($_SESSION['token'], $_POST['action']) == $_POST['action']){
//Hashed string matches. Request has come from page1.
echo $_POST['q'];
}
in your form you can just add a hidden field and add a page id. On the page that should send post or get request you can do something like
<form action='phpscript.php'>
<input type='hidden' name='page' value='valid_page'>
<input name='your_other_info'>
</form>
In the phpscript.php you can do something like
<?php
//If you have a request, it can be either post or get method
if(isset($_SERVER['REQUEST_METHOD']) && (isset($_POST['page']) || isset($_GET['page']))){
}else{
//Post or get is not from the valid page
}
?>
You cannot restrict the "origin" of the request, because there's no such thing per se. Your "page" isn't sending a request, it's the browser that does it. And the browser may have any number of reasons why it's sending you a request; be that because one of your pages has instructed it to do so or because a user is fiddling around with the Javascript console manually.
All you get on your end is an HTTP request. Go ahead, inspect it. In the browser, look at the network tab and inspect the raw request being sent. It's just a bunch of HTTP headers, nothing more. Anyone can send an HTTP request with arbitrary HTTP headers any time from anywhere and make it look like anything they want. Even the Referer HTTP header is not going to "protect" you.
If you need any sort of protection, you can use session tokens or user authentication to restrict the set of potential entities that can send queries somewhat. But what you have is still by definition a public URL endpoint which answers to arbitrary HTTP queries. You cannot restrict their "origin page". The best you can do is obfuscate it by requiring certain headers to be set (like Referer or X-Requested-With).
Related
Page one contains an HTML form. Page two - the code that handles the submitted data.
The form in page one gets submitted. The browser gets redirected to page two. Page two handles the submitted data.
At this point, if page two gets refreshed, a "Confirm Form Resubmission" alert pops up.
Can this be prevented?
There are 2 approaches people used to take here:
Method 1: Use AJAX + Redirect
This way you post your form in the background using JQuery or something similar to Page2, while the user still sees page1 displayed. Upon successful posting, you redirect the browser to Page2.
Method 2: Post + Redirect to self
This is a common technique on forums. Form on Page1 posts the data to Page2, Page2 processes the data and does what needs to be done, and then it does a HTTP redirect on itself. This way the last "action" the browser remembers is a simple GET on page2, so the form is not being resubmitted upon F5.
You need to use PRG - Post/Redirect/Get pattern and you have just implemented the P of PRG. You need to Redirect. (Now days you do not need redirection at all. See this)
PRG is a web development design pattern that prevents some duplicate form submissions which means, Submit form (Post Request 1) -> Redirect -> Get (Request 2)
Under the hood
Redirect status code - HTTP 1.0 with HTTP 302 or HTTP 1.1 with HTTP 303
An HTTP response with redirect status code will additionally provide a URL in the location header field. The user agent (e.g. a web browser) is invited by a response with this code to make a second, otherwise identical, request to the new URL specified in the location field.
The redirect status code is to ensure that in this situation, the web user's browser can safely refresh the server response without causing the initial HTTP POST request to be resubmitted.
Double Submit Problem
Post/Redirect/Get Solution
Source
Directly, you can't, and that's a good thing. The browser's alert is there for a reason. This thread should answer your question:
Prevent Back button from showing POST confirmation alert
Two key workarounds suggested were the PRG pattern, and an AJAX submit followed by a scripting relocation.
Note that if your method allows for a GET and not a POST submission method, then that would both solve the problem and better fit with convention. Those solutions are provided on the assumption you want/need to POST data.
The only way to be 100% sure the same form never gets submitted twice is to embed a unique identifier in each one you issue and track which ones have been submitted at the server. The pitfall there is that if the user backs up to the page where the form was and enters new data, the same form won't work.
There are two parts to the answer:
Ensure duplicate posts don't mess with your data on the server side. To do this, embed a unique identifier in the post so that you can reject subsequent requests server side. This pattern is called Idempotent Receiver in messaging terms.
Ensure the user isn't bothered by the possibility of duplicate submits by both
redirecting to a GET after the POST (POST redirect GET pattern)
disabling the button using javascript
Nothing you do under 2. will totally prevent duplicate submits. People can click very fast and hackers can post anyway. You always need 1. if you want to be absolutely sure there are no duplicates.
You can use replaceState method of JQuery:
<script>
$(document).ready(function(){
window.history.replaceState('','',window.location.href)
});
</script>
This is the most elegant way to prevent data again after submission due to post back.
Hope this helps.
If you refresh a page with POST data, the browser will confirm your resubmission. If you use GET data, the message will not be displayed. You could also have the second page, after saving the submission, redirect to a third page with no data.
Well I found nobody mentioned this trick.
Without redirection, you can still prevent the form confirmation when refresh.
By default, form code is like this:
<form method="post" action="test.php">
now, change it to
<form method="post" action="test.php?nonsense=1">
You will see the magic.
I guess its because browsers won't trigger the confirmation alert popup if it gets a GET method (query string) in the url.
The PRG pattern can only prevent the resubmission caused by page refreshing. This is not a 100% safe measure.
Usually, I will take actions below to prevent resubmission:
Client Side - Use javascript to prevent duplicate clicks on a button which will trigger form submission. You can just disable the button after the first click.
Server Side - I will calculate a hash on the submitted parameters and save that hash in session or database, so when the duplicated submission was received we can detect the duplication then proper response to the client. However, you can manage to generate a hash at the client side.
In most of the occasions, these measures can help to prevent resubmission.
I really like #Angelin's answer. But if you're dealing with some legacy code where this is not practical, this technique might work for you.
At the top of the file
// Protect against resubmits
if (empty($_POST)) {
$_POST['last_pos_sub'] = time();
} else {
if (isset($_POST['last_pos_sub'])){
if ($_POST['last_pos_sub'] == $_SESSION['curr_pos_sub']) {
redirect back to the file so POST data is not preserved
}
$_SESSION['curr_pos_sub'] = $_POST['last_pos_sub'];
}
}
Then at the end of the form, stick in last_pos_sub as follows:
<input type="hidden" name="last_pos_sub" value=<?php echo $_POST['last_pos_sub']; ?>>
Try tris:
function prevent_multi_submit($excl = "validator") {
$string = "";
foreach ($_POST as $key => $val) {
// this test is to exclude a single variable, f.e. a captcha value
if ($key != $excl) {
$string .= $key . $val;
}
}
if (isset($_SESSION['last'])) {
if ($_SESSION['last'] === md5($string)) {
return false;
} else {
$_SESSION['last'] = md5($string);
return true;
}
} else {
$_SESSION['last'] = md5($string);
return true;
}
}
How to use / example:
if (isset($_POST)) {
if ($_POST['field'] != "") { // place here the form validation and other controls
if (prevent_multi_submit()) { // use the function before you call the database or etc
mysql_query("INSERT INTO table..."); // or send a mail like...
mail($mailto, $sub, $body); // etc
} else {
echo "The form is already processed";
}
} else {
// your error about invalid fields
}
}
Font: https://www.tutdepot.com/prevent-multiple-form-submission/
use js to prevent add data:
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
I made an ajax call to the php script, which would indicate whether the information input by the user were valid and he would be authorized to sign in.
The thing is I'm not really sure about the session variable. Can that php script save session id variable and pass it onto the next php page, where jquery would redirect me upon successful ajax call?
Or would php itself redirect me using header location?
I hope I made myself clear. It is a theoretical question.
You would set the userid in the session and return a "success" condition back to ajax. the ajax success callback would inspect the returned data to determine if the login was successful and act accordingly (for example, redirect to a logged-in homepage)
You cant not redirect with PHP in this case because the headers are not returned in the http response.
you can set a variable in the response that returns the state of your php proccess.
something like
data = array(
"connect"=>true
);
or
data = array(
"connect"=>false,
"errors"=>"There is an error in your request"
);
then in the success: function you can check if the variable is true or false and redirect if you need to.
success: function(response){
if(response.connect){
//succeded
}else{
//not succeded
}
hope that helps. cheers!
I disagree from #Mijail when he says
You cant not redirect with PHP in this case because the headers are not returned in the http response.
With jQuery (or plain XHR), you can verify if you got a redirection with the response status (one of 3xx) and then use getResponseHeader method from xhr object to get the 'location' header to see where you should be redirected :
success: function(data, textStatus, xhr) {
if(xhr.status > 300 && xhr.status < 400) {
document.location.href = xhr.getResponseHeader('location') || '/';
}
}
This is the right way of doing it when you don't want to replicate your redirection rules on the client side too.
In theory you could do it either way, but without seeing your actual code, it's hard to give a definitive answer.
If you are using jQuery to call your auth PHP script and setting your session in that PHP script, you would likely then just use your JS (Ajax) to redirect to the "protected" area on success (returned from your PHP script with session started).
When my page does a post, I store all the $_POST data in a separate $_SESSION var. I know that a back button to this same page is by design to show the Document Expired message. My hope is to fool the browser into thinking that there was really never any $_POST data and so not to display the Document Expired message when coming back to it. I am forcing a complete refresh of the page so I am not worried about receiving old data, since I have it stored in the session.
I have tried to unset($_POST) hoping this will stay with the page. but the $_POST data must be cached / stored and returns with a refresh or back button. Is what I am trying to do possible? Any ideas?
* UPDATE *
My solution / answer is posted below. It posts to a separate form, which redirects back to the original form for processing. Not sure why the down vote. It has been working great for months and I no longer receive Document Expired messages. It also prevents duplicate posting.
You can achieve this using the post - redirect - get design pattern.
http://en.wikipedia.org/wiki/Post/Redirect/Get
A more standard way to implement this pattern is to display, validate and save the form on one page.
The following solution has these advantages :
1. All form related code is in one place.
2. Server side validation is simple
I have two pages, form.php and after.php.
form.php :
if(isPosted()){
if(dataIsValid($postedData)){
// dataIsValid should set $message to show to the user
saveData($postedData);
redirect('after.php');
die();
}
} else {
$postedData = $defaultValues;
}
showForm($postedData, $message);
You can add the following to the beginning of your script to solve this issue.
header("Cache-Control: max-age=300, must-revalidate");
Simplest solution that comes to mind? Don't directly do the post, catch the event and submit the form through AJAX. Then on success, redirect.
An example using jQuery:
$('#some_form').submit(function() {
$.post($(this).attr('action'), $(this).serialize(), function() {
window.location = "/some/success/url.php";
});
return false; // Prevent the form submission & any other events triggered on submit
});
Because the POST never got added to the browser history, you won't have that issue.
However, note that the POST url is now different than the one you load; you can make it the same by checking whether a POST or a GET was done server-side, but either way, you have to do a bit of extra work to "remember" the results of the POST.
* UPDATE *
Here's my solution.
I am aware that with post-redirect-get the POST is usually back to the same page for processing before being redirected away from the form with a GET to another destination. However, I need to be able to return to the original page for re-editing (as in a document model where the user can SAVE work in progress). Therefore doing the POST to a second page and redirecting back to the original was my idea of getting rid of the "EXPIRED" message since the editing form would not have post data associated with it. I have extended this (not shown) to include $_FILE and other situations (e.g. using it with a href as well). Not sure why the downvote. This has been working great for months now and accomplishes the task. I no longer receive "Document Expired" messages. In addition, all $_POST processing is accomplished in the original file.
testform.php
<?php
session_start();
if (isset($_GET) && count($_GET)>0){
// process get
var_dump($_GET);
}
if (isset($_SESSION['post-copy'])){
// return post vars to $_POST variable so can process as normal
$_POST = $_SESSION['post-copy'];
// unset the session var - with refresh can't double process
unset($_SESSION['post-copy']);
// process post vars
var_dump($_POST);
}
?>
<form method='post' action='__b.php?redirect=<?php echo $_SERVER['PHP_SELF'] ?>&help=me' enctype='multipart/form-data'>
<textarea name='descr' id='descr'>ABCD</textarea>
<input type='submit' value='Go'>
</form>
redirect.php
<?php
if (!isset($_SESSION)){
session_start();
}
if (isset($_POST)){
$_SESSION['post-copy'] = $_POST;
}
// retrieve the url to return to
if (isset($_GET['redirect'])){
$url = $_GET['redirect'];
}
// if multiple query string parameters passed in get, isolate the redirect so can build querystring with the rest
if (isset($_GET) && count($_GET) > 1){
$get = $_GET;
foreach ($get as $key => $val){
if ($key == 'redirect'){
// remove from rest of passed get query string
unset($get[$key]);
}
}
if (count($get) > 0){
$url .= (strpos($url,"?")===false ? "?" : "&") . http_build_query($get);
}
}
if ($url!==""){
header("Location: " . $url);
}
?>
I have some pages that return a JSON request that are used in jQuery via AJAX. What I need to do is somehow block direct access to the file. So only allow JSON (AJAX) requests but not direct access to the file.
So if I have the following jQuery code:
$.getJSON("ajax/returnDate.php", {
id: $(this).val()
},function (data) {
//more code
}
)
An unauthorized user can see this code. This will allow them to go to the following url and obtain the data they need.
domain.com/ajax/returnDate.php
So I need to write code to not allow direct access to returnDate.php but allow json request to be made.
How can I handle this?
Thanks
Get Operations are never secure and are subject to "get replay attacks",CSRF, and XSS.
HTTP headers can be easily spoofed if your concern is unauthorized access.
I suggest the following:
changing to a "POST" operation from a "GET" operation
When you serve the page, place an antiforgery token into the URL for the AJAX POST operation
Check the antiforgery token on every ajax request POST operation
Generate a new Antiforgery token on every page.
Ask yourself : would Sabu code it that way?
Ajax requests should have an extra header HTTP_X_REQUESTED_WITH and the value would be xmlhttprequest, so you can add a check
$_SERVER['HTTP_X_REQUESTED_WITH'] == 'xmlhttprequest'
Please note that this can still be imitated by an curl library, It's just an extra line of security
When ever you use jQuery to make ajax calls it sets the a header HTTP_X-Requested-With. You can check for that and redirect the user if its false using the header(Location: anything.php) function
$isXhr = isset($_SERVER["HTTP_X_REQUESTED_WITH"])
AND strotlower($_SERVER["HTTP_X_REQUESTED_WITH"]) == "xmlhttprequest";
please note to ensure proper security becaause this header can be injected manually in the header and access the page by some mallciuos user.
I have a ajax form that goes to a php file, i dont want users to be able to directly enter the php file into the URL bar so how would i be able to redirect them if they enter that php file, i cant simply add a header("location:index.html") or the form wouldnt work.
<?php
$Reg['E'] = 'Test';
echo json_encode($Reg);
?>
There's no way to be 100% certain because there's nothing technically special about an ajax request vs. any other request. If you're using jQuery, you can do a check on the X-Requested-With header:
if (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' )
...otherwise redirect. You can also put a token in the form such as a random md5 hash and check whether that token is submitted along with the request (compared to a value stored session). This at least requires that the form page be visited first to generate the token, and "normal" users will not be able to get the token.