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.]
Related
Guys I have an ISSUE..
I've Created 2 PHP files..
CreateAdmin.php ----------> Has a Form to Fill
Save.php ----------------> Called through jquery Ajax to insert in database.
the codes are working fine. but the problem is i can directly access Save.php file.. when i access each of access an empty row is created in database admin table.
how to prevent it.
actually I've got an idea. when ajax called a session variable should create. the top of the Save.php there should be code if there is not a session variable page should redirect..
is it possible.. if it's okay will it affect the accessing speed.
I would implement some fundamental concepts here. I try to describe them here as simple as possible:
1) In your GET request (calling your CreateAdmin.php) start a session (if not already done) and create a random string that you store to your session:
$_SESSION["token"] = sha256(uniqid(mt_rand(), true));
2) Add the token to your form as a hidden field
<input type="hidden" name="token" value="<?php echo $_SESSION["token"]; ?>">
3) Now do a http POST request with jQuery Ajax call (should not be a GET request). Important include your hidden token from the form. You could also include a special value in your JS that indicate that this is coming form a Ajax call, but this dose not make your Save.php more secure (see below).
4) In you Save.php check first if the call is a POST request, if not do not continue. Than check if the hidden token is included and matching the value in the session (you have to start it), if not do not continue.
if (!isset($_POST['token']) || $_POST['token'] != $_SESSION['token']) die('invalid');
5) If both checks pass you can continue to do your DB stuff but first I would do some additional checks about your data quality that means if the rest of the input is valid e.g.:
Field is not empty if required or the field values has a minimum or maximum length or check if only allow characters included, etc.
6) In all cases I would delete the token from your session.
Generate a new one for the next request if required.
You maybe also want to limit the time how long a token is valid, or check if the user from this session has the right to do this action.
There will be no speed issue with this. There are more things that could be considered but this should be the minimum.
For example you ask that this request should be only possible by Ajax. To be honest I would not take to much effort to check this. You could try to check if the "HTTP_X_REQUESTED_WITH" with "XmlHttpRequest" was included, but this dose not give you much more security. You can simulate a Ajax/POST request very easy in any modern Bowser with the Developer Tools. But with the described method it is not enough just putting the path to your Save.php file in the Browser.
Being a novice with PHP, I may not be taking the correct route with forms but this way works for me, up to a point. Below is an example of my setup/
I have a form at www.foo.com/add.php, which needs an admin to be logged in to the session. The form inserts data into a database. Once it is submitted, the actions is set to action="scripts/add.php" and then that is redirected using a PHP header function to www.foo.com/done.php.
What I want to know is, can you deny access to the script file directly, e.g. if you go to the script file in a web browser it could enter an empty row into the database or possibly cause some other security issues?
If the form is submitted using POST method (with attribute method="post" in <form>), you can still execute your script only on POST requests, by adding this at the top:
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
exit;
}
There are a few options available to you:
Validate the form post data before inserting into the database
Include a nonce or other generated value that is only present in the form prior to submission
The problem you're trying to solve really sounds like you want to prohibit blank records from being inserted into the database- not necessarily that you want to prevent access to add.php. This is where option #1 comes into play.
In your current add.php, it sounds like there needs to be some input validation. Basically, you'd check the values that are received by the script to make sure they exist. For example, if add.php accepts a first name, as part of a phonebook app, you'd have code similar to the below:
$firstName = '';
if(isset($_GET['firstName']))
$firstName = $isset($_GET['firstName']);
// ...
if(trim($firstName) == '')
//do something to handle an error, either set an error flag or die() with an appropriate message
This is a basic example of input validation, PHP has a validation library you may want to become familiar with: http://www.php.net/manual/en/intro.filter.php
With this input validation in place, someone can navigate to add.php and should receive an error. It will also detect if someone submits a blank form as well.
#2 requires that your form receive a unique value when it's generated called a nonce. The nonce is a unique value that's specific to that instance of the form. The subsequent call to add.php will only accept the request if the nonce is valid. An approach might be to store the nonce in the user's session.
Another note outside the scope of the question, since you're inserting data into a database, you should make sure you have proper escaping of inserted data. If you're using MySQL, see here: http://www.php.net/manual/en/mysqli.real-escape-string.php. If using another database engine, you'll want to lookup the specific library to see how to escape the string.
Yuu can try this to check whether request send by post or not
if(isset($_POST)){
continue.......
}
in order to secure such pages i have applied the code below.
Except request method, it also checks that the request comes only from specific domain.
$live_site_regex = '/http:\/\/(w{3}|w*).?yourdomain.ext/';
if($_POST && preg_match($live_site_regex,$_SERVER['HTTP_REFERER']) == 1){
//everything is ok
}
Is there a way to ensure the $_POST data my code received came from my form and not an outside influence. Basically I don't want someone to be able to spoof a $_POST to a universally available page such as account creation. The account creation page is accessible by any user, but I want to ensure only the data submitted by my account_creation form is what gets processed.
The only thing I could think of was initiating a $_SESSION, and then supplying the session_id to the form using a hidden input. Upon $_POST the value of the hidden input would then be matched against the current session_id.
If there is a better method to achieve this result? If there is I look forward to hearing it.
You cannot ensure that data came from a form. A POST request is just a POST request, it can be generated in any number of ways. An HTML form is just one of those ways that's very user friendly. Your server needs to validate whether the data received via the POST request is valid or not and whether to act on it or not.
Having said that, there are things that can help you to restrict and validate the data that is being submitted. First of all, require that a user is logged in using (session) cookies. That eliminates random requests by anonymous users. Secondly, you can embed a token as a hidden field into the form which you also save into the user's session. The POST request needs to contain that token in order to be valid. The token is simply a pseudo-random string.
You can enhance this by preparing a hash of the form fields that you expect the user to submit. If the form value should be read-only, you can include the value into the hash as well. E.g.:
$rand = md5(mt_rand());
$hash = sha1('lastname:firstname:email:' . $rand);
$_SESSION['rand'] = $rand;
$_SESSION['hash'] = $hash;
// on form submit:
$keys = array_keys($_POST);
$checkHash = sha1(join(':', $keys) . ':' . $_SESSION['rand']);
if ($checkHash != $_SESSION['hash']) {
die('Form submission failed token validation');
}
That's just a quick example, you'll probably want to sort the keys alphabetically to make sure you'll get the same hash etc. It demonstrates the concept of the user needing to have a unique token for each request though which prevents tempering with forms and submitting more or less data than wanted.
This still does not mean that a user actually used your form to submit the data though.
$ref = $_SERVER['HTTP_REFERER'];
if($ref !== 'some site path/index.php')
{
die("Access Denied!");
}
This should prevent most people from posting data to your database from an outside influence.
Slightly better is to add additional validation such as user_agent, user_ip and some other $_SERVER vars - those are the two I use.
So, create the unique ID (or Session ID) as you describe, but add a little extra validation that the agent and ip also match. Not fool proof, but adds another little layer of security.
Edit: I should add that you don't send the user agent back; keep that server side and silently validate against the returned session id.
Also, if a submission fails validation, never reveal that back to the user as to why - that way a cheat doesn't know how your tracking them. You can also add "5 invalids and you're out" tracking, but you need to sort of login for that.
Using the session ID is certainly one way of doing it. But there are other options most (if not all) of which involve adding some data as a hidden field.
Use a CAPCHA. That will always be unique to each page load and therefore make it mandatory to use your form.
Generate random data and store it in the DB (or just the $_SESSION variable) and check it once the form is submitted.
Option one is the one I recommend for a user creation form as it pulls double duty. It stops automated submission of your own form, while ensuring that the $_POST data is coming from your own form.
This is a standard pattern pattern to prevent XSRF. Essentially it is the similar to what you mentioned. Server creates a random token when form is rendered for the user. It is tied to a browser cookie for the user. On form submission it is posted back to the server. Server then compares the token with what was issued and form action is performed only after a successful match.
There's a lot of good mentions of putting a unique value in the form and matching to the stored value in the server side session. Do that, but also think about what happens when a user uses the back button and possibly tries to submit the form twice, or they open a second browser window(same session!), or they use multiple forms on your site.
Don't create crazy bugs by not thinking your system through.
I'm developing a PHP-MySQL app that enables registered users to enter text comments. Problem is:
User sign-in into the web site - OKAY
User presented with a form to submit text comment - OKAY
User enters text comment and submits - OKAY
I have a routine that sanitize the comment & save it into MySQL (with userid, textcomment, datetime stamp) & present back the user that his/her comment is entered - OKAY
User decides to refresh browser - a duplicate comment is entered - BAD!
I was thinking 3 options:
OPTION 1: Routine that checks: last time user posted comment, and if so, check if this is a duplicate. If duplicate then display error message.
OPTION 2: Routine that does not allow a user to post too quickly. So basically do not allow postings of comments within 1 minute or so. So if browser is refreshed the comment will be ignored.
OPTION 3: Manipulate the browser cache to clear out its contents so when refreshed no duplicate will be entered.
Now in the context of my application, my concerns with OPTION 1 and OPTION 2 is performance PHP-MySQL since I already have various queries within the same page that push/get data from databases. So OPTION 3 may target the issue differently.
Questions is: If I go for OPTION 3 can this be considered a Best Practice? meaning clearing the browser cache is the best most effective solution? I have read that there are consequences too? your thoughts are appreciated!
Just do a redirect after submitting data to the database. It's a common practise.
An http redirect instructs the browser to issue an http GET for the url specified (as opposed to the http POST that is used to submit the form) . If you do this right after you have inserted data into the database, when the user refreshes his browser nothing will happen other than him seeing the same page again.
This question on SO tells how you redirect with php.
Just unset the posted variable after you have inserted it in the database
<?php
if(isset($_POST["comment"])){
//store in the database
//on successful storage
unset($_POST["comment"]);
}
?>
Thus the value won't be posted back when user refreshes the page...
You need to implement the Post/Redirect/Get pattern.
I would use Option 4: Use a one-time form token that authenticates the form request. If the request was successful, invalidate the token.
When doing this, even returning to the form after a redirection won’t allow it to send the form again. Additionally, this will also make CSRF attacks harder.
After entering data into database redirect the page using header or location.href
i have found new way try this.
function PreventResendData($invalidpage='index.php'){
if( $_POST && !is_array( $_SESSION['post_var'] ) ) {
$_SESSION['post_var'] = $_POST;
header('location:'.$_SERVER['PHP_SELF']);
}
else if($_SESSION['post_var'])
{
$_POST = $_SESSION['post_var'];
$_SESSION['post_var'] = '';
}
else
{
header("location:".$invalidpage);
}
}
What is the best way to stop a form from being reprocessed when a user hits a Back button?
I'm following the Post/Redirect/Get pattern, so I don't have a problem if F5 is pushed, but the back button still provides the opportunity to resubmit the form. If this form is a credit card processing page, this is bad.
This question has been asked somewhat here, but the phrasing and answers were of poor quality and not specific to this problem exactly.
I have form.php which submits to itself. If there were no errors in input data upon submission, the user is redirected to form_thanks.php. Hitting back (and "Send" or "Resubmit") once resubmits form.php (BAD!) and then brings them back to form_thanks.php.
Please also include solutions that do not involve using Sessions, if possible.
I would do it a different way. Put up a hidden input with a random string as the value, and when it's submitted store that random string in a session. Set up a simple check to see if they've already posted it and if they have don't accept the input.
This should be done with a single-use token, or a nonce. The php/server should include this token in a hidden form field.
It should store a list of all the recent tokens and each time a form is submitted, it should check to see if the token is in the list of recent valid tokens.
If it's not in the list, then don't reprocess the form.
If it is in the list, then process the form and remove the token from the list.
The tokens can be handled within sessions or just a simple database table without sessions. The tokens should be user-specific though.
This is also the recommended way to avoid CSRF attacks.
Just thinking out loud here. But what about a variation on post/redirect/get where the final get is not actually the final get ;) But rather, it in turn always automatically forwards to the truly final page, so that should the user hit the back button, they return right back whence they came?
EDIT:
Ok, taking into consideration the OP's comment, here's another idea. The URL for the form submission could be made to require a parameter that is good for only one use. That token would be generated (using MD5 or some such) before the form was submitted and could be stored in a database (in response to somebody else's suggestion you requested a solution without using sessions). After the form is processed, this token would then be flagged in the database as having already been used. So that when the page is returned to with the same token, steps can be taken to prevent the resubmission of the form data to the backend.
Late answer, but one could avoid the processing altogether by using an AJAX-based solution; there wouldn't be an issue with including a nonce with this processing scheme, but by using an asynchronous query which, on success, redirects the user, the requests are not repeated by refreshing, pressing back, or anything other than clicking the button.
It is also easy to implement a mechanism that prevents the button from either being pressed twice or being "locked up" if something happened during the request by embedding into the handler for the request (whether high level with PrototypeJS or jQuery or low level with your handrolled function) the mechanisms to enable and disable the button when the request completes and first fires, respectively.
I find that back will bring the form to the state it was before the page was redirected, if that is the case, have a hidden input/variable or something which starts with value say true, then once the form is submitted, and if the value is true, change it to false and then submit, else return false
Try this :
<SCRIPT type="text/javascript">
window.history.forward();
</SCRIPT>