is it okay to overwrite the php superglobal arrays? - php

Here's essentially what I'm trying to accomplish. I have an HTML form which is processed by PHP. A user is required to be authenticated to be able to submit this form. However, I do not want the user to loose their work if the session times out while they are working on filling it out.
My thought process is, when I perform my authentication check, if it fails, the authentication module can store the $_POST data in the $_SESSION array, and redirect the user to the login page. Once the user logs in, the login page can redirect the user back to the submission page, and the authentication module would then see that there is saved $_POST data in the $_SESSION array, and set the $_POST array back to the values that were stored in $_SESSION. Then the submission page can process the form data as normal.
I have done testing and verified that it is, in fact (at least in the version of PHP I'm using), possible to overwrite the value of the $_POST superglobal in PHP. And, in this particular situation, it seems to make a lot of sense to do so. Using this method, no other site code anywhere, other than in the authentication module, would have to be modified for every form on the whole site to take advantage of the "saved post data" feature.
So, I've asked myself if I could do this, and the answer is yes. But should I? Or are there potential problems with using this method? Part of me says it make a lot of sense, but another part of me worries it might be bad code design. If I shouldn't do this, what would be the proper way?

Thanks to all the comments. I ended up using the following code placed in a common library file used by all pages. The only downside is you do have to remember to use global $post in any function or method using the special POST data. But it has the advantage of not being hackish like my previous idea.
if (isset($_SESSION['authSavedPost'])){
$post = $_SESSION['authSavedPost'];
unset($_SESSION['authSavedPost']); // So we don't try to re-post the same data twice
}
else{
$post = $_POST;
}
The authentication check function used on this and other forms, if it fails, will save the current POST data as $_SESSION['authSavedPost'] = $_POST so it can later be restored by the above code.

Related

Hiding $_GET variables by a re-direct

I'm using a 3rd party app that I need to integrate with my own app. In the 3rd party app, information is posted via a form and then re-directed to my site for further processing. The re-direct to my site will contain variables that I'll need from the form within the re-direct URL. However, I don't want the user who published the form to be able to view those variables.
If the re-direct link is hidden on the 3rd party app (i.e. it's not in the form), then one method that I thought which could work would be to direct the 3rd party app to a "pre-processing" script which does the following:
session_start();
$_SESSION['some_variable_to_save'] = $_GET['some_variable_to_save']; //properly sanitized!!
header('Location: where_i_really_want_to_process.php');
exit;
Then, in where_i_really_want_to_process.php I can process the session variables. Is this a secure method to ensure that the user never sees the $_GET variables?
Your suggestion of using $_SESSION seems to be the only solution.
However to make life a little easier and to cope with any changes that may occur just put the whole $_GET array onto a Session variable
session_start();
// dont sanitization here, do it in the where_i_really_want_to_process.php
$_SESSION['previous_GET'] = $_GET;
header('Location: where_i_really_want_to_process.php');
exit;
It is physically impossible to "ensure" the user never "sees" some form of the data being passed if you have to have the user forward the data to you. They must see some form of the data, otherwise they can't turn around and tell your server what the data was.
If you could encrypt the data, that would effectively hide the data from the user (assuming you use good encryption). But you lack control of the third party, so this may not be viable.
Another option would be to find a third party you can trust to give limited db access, and have them contact your server directly instead of using the client as a middleman. Without knowing exactly what you're doing, I have no idea if this is viable.
If all you're doing is trying to protect "normies" from bookmarking the GET values, the shove-into-session-then-redirect trick is plenty. Only other option would be to write something js/ajax/whatever that does it client side- however that's less transparent to the user than doing it serverside, as well as depends on the user not blocking your method of hand-waving. Very very few people disable internal redirects.
I do endorse Riggs's method (shove all of $_GET into session instead of just the current key you want) over the solution in-question, however, as it lets you pretty much ignore this helper script for the life of the application.
Try to use an associative array of variables $_POST:
$_POST = $_GET;
$_GET = [];
header('Location: where_i_really_want_to_process.php');

How can I protect a form processor script file?

I have set up a rather complicated HTML form that uses the JQuery Validate plugin with several required fields and various rules. Form is working great. It POSTs to a separate PHP processor file that does a number of things such as send a couple of emails and eventually sends the user to Paypal. (It is a club membership application.) It appears that it only took about a week for some type of "bot" to find the processor file and start running it directly over and over. About 500 emails & apps were generated before I caught it and stopped it by renaming the files temporarily. At the time it was happening I wasn't quite sure exactly what was going on, but after evaluating it for most of the day I came to realize that it couldn't be as a result of the main form being executed, but by just running the processor file directly.
So...my question is this: How can I keep this from happening? There must be some type of coding to include that will ensure that the processor can't run unless it is really coming from the real HTML form...or is there a better way? I followed all of the "examples" on the 'Net in regards to forms and POSTing but nowhere did I see anything that relates to this type of problem.
Generally this can be reduced by adding a CSRF token to the form.
Set a random sha/md5 value to your session, and set that value in the form also as a hidden input, upon a legit user sending the form that value will be passed along too, validate and check the passed value with the one in session. if all is good process.
If its a bot, the bot would need to parse the form for the CSRF token first. Or you could step up and make that security key an image and make the user type it (captcha).
How to properly add CSRF token using PHP
Its something you should also add to your login forms ect, else your have bots brute forcing there way in.
Maybe you could add a $_SESSION[] global variable on the form page. Then check it on your processing page and unset it after execution. Sounds like the simplest way to me, but you should hear out what others suggest. You can fin documentation on $_SESSION[] variables here PHP $_SESSION
Add a token to the form when generating the page, and save it into the session.
When you got the post data, check the token with the one in the session.
And you probably want to use CAPTCHA code to protect yourself from the bots.

Data from the exact form

I have been googling a lot but i am still without an answer so i decided to ask this question here:
Is there a way in PHP how to check that the data i am receiving in some script are from the specific form on the page? I am asking because everyone can see the name of the script i am using for saving the data to my database, so if somebody is able to find out the whole URL, he is also able to send some fake data to the script and i need a condition, that the saving process is triggered only when the data comes from the proper form on my page.
I am using jQuery to call AJAX function, so basically if i click on the button "send", the $.POST() method is triggered to call the script for saving the data.
Thanks,
Tomas
Use tokens to check if request is valid
You could always add some kind of security token when submitting data:
Tokens can be easily extended for many different uses and covers wide area when it comes to checking if some request is valid, for example you could let your non critical forms open for public, ask users to get their secret keys from some page (forcing them to open that page) and then use those keys to identify them when submitting data.
Of course all of this can be completely transparent to user as you could give keys from front page via cookies (or session cookies, it does not matter here, no more or less security as server keys should change after use and invalidate within specified time or when user's identity changes).In this example of use, only user that opened front page can submit data to server.
Another case is when cookies is given away at same page which contains form for submitting data to server. Every user that open page will have their keys to submit data straight away, however if someone tries to make request from outside it will fail.
See OWASP Cross Site Request Forgery
and codinghorror.com Blog CSRF and You
Only with AJAX?
Here is my answer to another question, this answer covers different methods for inserting additional data to ajax request: Liftweb: create a form that can be submitted both traditionally and with AJAX (take a closer look at
$.ajax({
...
data: /* here */
...
Currently I am using tokens this way:
Form used to submit
This hidden input can be added to form, it is not requirement as you can use methods described earlier at another answer.
<input type="hidden" name="formid" value="<?php echo generateFormId(); ?>" />
Function generateFormId()
Simply generate random string and save it to session storage
function generateFormId() {
// Insert some random string: base64_encode is not really needed here
$_SESSION['FORM_ID'] = 'FormID'.base64_encode( uniqid() );
// If you want longer random string mixed with some other method just add them:
//$_SESSION['FORM_ID'] = 'FormID'.base64_encode( crypt(uniqid()).uniqid('',true) );
return $_SESSION['FORM_ID'];
}
Processing submitted form data
if (!isset($_SESSION['FORM_ID']) || $_SESSION['FORM_ID'] != $_POST['formid']) {
// You can use these if you want to redirect user back to form, preserving values:
//$_SESSION['RELOAD_POST'] = $_POST;
//$_SESSION['RELOAD_ID'] = uniqid('re');
echo 'Form expired, cannot submit values.';
//echo 'Go back and try again';
exit(1); // <== Stop processing in case of error.
}
If you need to check which form is submitting data
Then you could just add prefix when generating id's and check for that prefix when processing form data.
This is case when one php script deals with many different forms.
Remember that only ultimate answer to prevent evil users is to pull off all wires from your server...
This is an interesting topic, but you are missing an important point: A spam robot / bad user could also bomb your database by using that specific form page (!).
So, the question is not how to check if the request comes from that page or not, the question is how to check if he's a regular user or a robot/spammer/bot.
Do it with a captcha, like http://www.recaptcha.net
In case i slightly misunderstood the question: If you want to be sure that the request comes from a "real" user you will have to work with a login system / user system and check for users id / password (via db or sessions) every time you want to do a db request.
You can verify that the page is being requested via AJAX with checking against this:
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest'
You could also check the HTTP_REFERER.
But it sounds like you're trying to protect a page that processes vulnerable data. This requires more than just the two things above. I'd recommend googling 'CSRF Forgery' to get more information on this.
These are something that you should take a look at.
Capthcas.
Referer check.
Use POST than GET. [Still Curl can automate it.]

Smart way to generate a FORM key with PHP

I have a form, in whmcs that I want user to only be able to submit once, so they cant submit it and refresh to resubmit... I was thinking about unsetting $_POST or redirecting, but neither would work in this situation, how would I generate a key and make it so its only usable once? Can't use mysql.
Why not store a random key in the session? That's how most CRSF token systems work: When loading the form, generate the key and save it in the session and include it in the form. When submitting, compare the keys and delete the saved key.
If you just don't want the user to accidentally resubmit a successfully submitted form, the link from #zerkms' comment is what you want: http://en.wikipedia.org/wiki/Post/Redirect/Get
The most common way to avoid double-posting is to do
header('location: /'.$your_url_here);
after you complete your actions. So you just redirect to the same page, but without $_POST.
Set a session or cookie when the form has been submitted and check if it exists beforehand.
You could also store information in a database such as their IP and browser if you wanted a permanent check, but this has it's own problems so your never going to stop someone 100% of the time.
I realize this is an old question but I recently had the same problem. None of the Post/Redirect/Get solutions appear to work on WHMCS if you want to stay on the productdetails page (for example) even if you are switching to another smarty template file after POST. Probably because it needs $_POST[id'] and that goes away after a refresh. So the closest I could get was having it go back to the products list page which is not what I want and probably not what the original poster wants.
The solution I finally came up with was to add a $_SESSION[submitted] variable after the form was submitted. You will have to figure out the logic yourself depending on what you are doing.
My Logic goes something like:
if ($_SESSION['submitted'] == 1 && !isset($_POST['somecustomkey'])) {
unset($_SESSION['submitted']);
}
That is at the top and resets the "submitted" session key if your POST form data does not exist.
Then add a check before you write the info to your database or whatever.
if ($_SESSION['submitted'] != 1) {
//Do some stuff with $_POST form data
$_SESSION['submitted'] = 1;
}
I think this fits in well with the intended purpose of $_SESSION and easy to implement.

Will bad things happen if I control the content of $_POST?

In my days of writing web applications, I'm missing a simple way for a PHP script to direct to another PHP script while passing along data without using the URL
Browsers can pass data invisibly to a PHP script using the $_POST array. So I'm devising a script that will dump the contents of $_SESSION["POST"] into the $_POST array to mimic the passing of post data, then clears $_SESSION["POST"].
For instance, a page X contains a login form. Page X directs to page Y to validate the data. Page Y discovers that the login information is incorrect, and redirects back to page X - which now displays the error message "Login information incorrect" from $_POST.
Am I crazy for missing this feature? Is there some other method of doing this easily that I'm missing?
Please respond with anything that can be helpful.
You could use the session. On page X, you'd put the data into the session. On page Y, you'd validate the data and handle the redirect. You can put your error message into the session too.
The session persists between requests, so it's the perfect place to store that kind of data.
EDIT
OK, I'd do things with session variables, but if you want to avoid that you do have a few other options I can think of besides posting:
You can use files on the server (such as temporary files). Use the user's session key to identify which file is theirs, and you can read and write whatever data you care to it.
You can put the data in a database. That would work too.
Of course neither of those is a true post. If you want a true post, you have another two options.
First, you can return the data to the page in a hidden form, and use JavaScript to trigger a POST. This is simple to do, but it requires the data to pass through the browser. This means you'd have to take care that a user didn't change the data, and the user would have to have JavaScript on. You can checksum the data to ensure it isn't modified, but the JavaScript problem is unsolvable.
Another way to do it would be to take the user out of the equation entirely. When the user submits to page A, page A could make a POST to page B, check the response, and then redirect the user to the proper place. This would be just like if you had to make a JSON or SOAP call to a 3rd party service, except you control that service. It's been a little while, but I believe that HttpRequest is the class to use to do this.
It would be ideal if that URL you check with returns a simple answer ("true", "false", "yes", "no", "good", "bad", whatever), but as long as you can tell by the response whether they were successful or not, you can do it.
Now I should note that I'd agree with mvds, this sounds like a function that should be included in page A so that it can do all the work but the code can be shared with other pages. Having page A post to page B and then redirect to A or C seems unnecessarily complex. Page A can easily accomplish all this. I read your comment on his answer, but it seems there should be another way to accomplish this.
Abstract the performed actions to functions and/or classes, and the need for such kludges will vanish. Concrete: both page X and Y could call the same function to validate the posted login data.
Page X redirecting to page Y to do validation means handing back control to the client, while you could (and should) validate the data right away.

Categories