How do i make $_GET more secure.? - php

I am using the get method to perform some operation like, approve, markasspam, delete, for commenting system. i know it is highly insecure to go this way but i cannot help it out. because the reason for using $_GET method is to perform the operation within the page itself using PHP_SELF, and FYI i am using the post method using checkbox to perform the operation too.
now for making it bit secure i want to randomize the number or generate the hash or something and then compare it, get the id and perform the operation
my current code is somewhat like this.
<?php
if($approve == 1 )
{
?>
Unapprove
<?php
} else
{
?>
Approve
<?php
}
?>
| Spam
| <a class="edit-comments" href="edit-comments.php?id=<?php echo $id; ?>">Edit</a>
| Delete
and i perform the operation using this code..
if(isset($_GET['approve'])) {
$id = intval($_GET['approve']);
$query = "UPDATE comments SET approve = '0' WHERE id = '$id'";
$result = mysql_query($query);
}
if(isset($_GET['unapprove'])) {
$id = intval($_GET['unapprove']);
$query = "UPDATE comments SET approve = '1' WHERE id = '$id'";
$result = mysql_query($query);
}
if(isset($_GET['delete'])) {
$id = intval($_GET['delete']);
$query = "DELETE FROM comments WHERE id = '$id'";
$result = mysql_query($query);
}
if(isset($_GET['spam'])) {
$id = intval($_GET['spam']);
$query = "UPDATE comments SET spam = '1' WHERE id = '$id'";
$result = mysql_query($query);
}
instead of using approve or unapprove or delete or spam, i want to randomize or hash that words and want it as lengthy as possible and then perform the operation.
how do i do it? what is your take on this?
EDIT: Please Note Only the
Authenticated User i.e Admin will be
able to perform this operation. even
though it pass through authentication
system i want to add more security
even for admin. to avoid experiments
or accident
the code is not exact it is just the sample to make you understand what i want to achieve.

Whether you use GET or POST parameters here doesn't matter much in this context - what the script needs first is some sort of authentication. (After that is done, you can go into security details where GET is slightly less secure than POST - see the comments for details.)
I'd say you have two options:
Protecting the entire script using .htaccess - no changes needed to the script itself
Introducing PHP side user authentication and perform the operations only if a logged in user makes the request. Needs fundamental changes to the script but is most flexible.
Re your edit:
It turns out your script is already protected. In that case I assume you are uncomfortable with incremental ID numbers turning up in the URLs, getting cached in the browser etc. etc. The usual solution to that is to generate a random key for each comment when it is created (in addition to the incremental ID). That key gets stored in a separate column (don't forget to add an index) and you'd match against that.
A step even further would be to create temporary hashes for every action, which is the ultimate protection against a number of outside attacks.
Re your edit about using one-time hashes:
I've never implemented one-time hashes in an admin interface yet so I have no experience with this, but I imagine that a very simple implementation would store action hashes in a separate table with the columns hash, record and action. Whenever your tool lists a number of records and outputs "delete / approve / unapprove" links, it would generate three record in the hash table for each comment: One for delete, one for approve, one for unapprove. The "delete / approve /unapprove" links would then, instead of the record ID and command, get the correct hash as the only parameter.
Add a time-out function for unused hashes (plus delete any hashes that were actually used) and you're done.

You can do it that way, the $_GET is not the unsecure thing in your code. The unsecurity comes from you not checking wether the user is e.g. authorized to delete comments.
In your current code, anyone can delete anything at anytime and as often as they want.
If you have a wrapping code that ensures the if-statements postet by you are not executed if enter good reason here, then it's okay.
But you should try verifying, that the content of the parameters are really integers instead of just int_val'ing them and using them directly on the database.
On your edit
You should check your parameter is really an int. intval("test") will also return an integer, mostly 0.
You might consider regex for that, to verify the string only consists of numbers: preg_match('/[0-9]+/', $_GET['id']);
If so, you can perform the action.

You shouldn't use GET for any operations that change data on server. NEVER. You use it only to get data.
If you can't use forms for operation buttons (because there is another form outside them) you should consider this design:
You use AJAX to perform POST requests to your server
In javascript-disabled environments you use GET links like user.php?action=delete, which shows you very simple form on a separate page. The header in the form asks: "Are you sure you want to delete user X?" and it has two buttons: 1) "Yes" - that submits POST request to operation script, 2) "No" - which sends user back to the page where he has been

Related

Preventing user tampering through changing data values in PHP/AJAX

I have several forms where a user can make changes to events tied to their account but I'm not sure how to take care of security involving the user changing the value set in data-employeeID for example.
I initially set up a security check script that would look at an identifying piece of data coming in through ajax requests and before it goes on to actually use it. It would first do a series of mysql joins to work it's way back to the Users table and then do a final check to make sure the ID_NO of the users table can bej oined using the session stored userID.
I assume this would work and be secure. But the site is rather large, and often times 5 or 6 values are being passed though ajax calls. So on every public facing function I would need to repeat the process for each variable (Unless they can be tied together along the way; not always possible) before continuing on.
After googling around though I've only found results related to php/ajax processing errors, or security surrounding the user who is making the request.
If the user is legit though they can still make changes on the page, and keep all of the correct information except for changing something like the meeting ID to something else. If I only check the users CompanyID, or DepartmentID though my callback chain and skip meeting ID they will be able to get data inserted into the database that is not accurate, with a meeting possibly being manipulated because I didn't create a callback chain for every single variable being passed between ajax and PHP.
I feel there must be a better way and that this is a solved problem so I'm hoping someone can point me in the right direction before I dive too deeply into making an over-complicated process. Thanks.
Requested Sample Code:
$.ajax({
type: "POST",
url: "<?php echo base_url().'meetings/addstarttime/'?>",
data: {
validDaysID: this.dataset.validdaysid,
departmentID: this.dataset.departmentID,
companyID: this.dataset.companyID,
employees: employeesArray,
start: this.dataset.start
},
success: function(data){
//update html to show updated values
$validDaysID = $_POST['validDaysID'];//unencoded
$securityArray[] = array("validDays", $validDaysID)//Check if access to this value is OK
$tieItBack[] = array("Buildings", "BuildingID");
$tieItBack[] = array("Location", "LocationID");
$tieItBack[] = array("Department", "DepartmentID");
$tieItBack[] = array("Companies", "CompanyID");
From there I make a query that selects the COUNT(*) of rows for the initially requested table, validDays, INNER JOINS the tables specified in the tieItBack chain and then finally checks if Companies.ID_NO == $this->session->userdata('CompanyID');
If there is a hit then they have access, if there is no rows then they do not.
For this example three of the variables can be knocked off in a single query but that is not always the case. Also this is using replaced table/col names cause I'm paranoid I guess but the structure is the same.
You have no reliable way of guaranteeing that the client-side code is doing what you want it to do.
Your server-side software should distrust all input, regardless of whatever client-side validation you've already written. (Users might not even run JavaScript, for example.)
This isn't a problem that can magically be solved. It's just a reality about networked computers and software development.

Procedure for restricting user from typing in php url

I've got a database where you can load details of a candidate through the following get request url: database/candidates/?loadcandidate=n where n is the id of the candidate to load.
Is there a common practice where you can add something to the get request which would prevent my users from being able to type in any id to access the details of candidates? I want them to only be able to access the candidate page from my search results or by bookmarking the candidate page and revisiting it later.
I could probably figure out my own eccentric way of doing this, but I'm wondering if there is a common procedure?
Get requests are get requests and you can't prevent users from changing get request variables.
But reading between the lines, I think the solution you're looking for is obfuscating (hiding/cloaking the intended meaning of) the userid so that it can't just be plainly accessible via changing the id incrementally, (e.g., ?loadcandidate=1/2/3/4/5/6...).
In this case, try assigning a user code so that it would be harder to pull up candidates just by guessing the ID.
Just as a simple example (which by no means should be used for production) try simple MD5 hashing and get the first 6 digit substring.
Example:
1 = c4ca42
2 = c81e72
3 = eccbc8
...
database/candidates/?loadcandidate=c4ca42 will pull up candidate with user id = 1.
So that when you access database/candidates/?loadcandidate=n, n will be something much more difficult to guess, thereby decreasing the number of random lookups exponentially. Of course this will all depend on how random the obfuscation/code actually is.
Hope this helps!

how to get the old session?

I have a post form with captcha based on session for example the code know is Ujkd9 after refreshing the code become Hi8P4 .
And this is the problem. If someone post the form the captcha code of the input become old, how to get the old session ?
Well you can't, but you could save the older session into another session key creating a copy of that session, by example :
$_SESSION['session_copy'] = $_SESSION['current'];
I'm assuming your captcha code is being made on the fly and output via some image making in PHP. Your problem is that once you make the image and output it, you have no way to figure out what the CAPTCHA value actually is?
Since I have absolutely no knowledge of what your application structure, or database looks like, I can give you recommendations based on what I would do:
If I can assume that your PHP script looks something like this:
$captcha_string = make_hash(); // This function could just be the consolidation of your step in making the hash. I don't know, so it's going to be generic.
make_captcha_image($captcha_string); // Again, I don't know exactly how this goes, so I have to assume.
In layman's terms, you are going to need to store that somewhere. But Where? Now, I want it kept in mind that I've never particularly worked with Captcha directly, so I'm not 100% on what the "best practices" are, and I also don't know what you're doing, so these are probably going to be vague answers for a vague question. There are roughly 3 options, the way I see it.
1. Database Storage
Realistically, I would probably use a database as a first go-to. In my database I would have a table named captcha, with the following definition:
CREATE TABLE captcha (captcha_id int unique not null auto_increment, session_id VARCHAR(100) NOT NULL, captcha_string VARCHAR(6) NOT NULL)
Note: I don't know what length the session_id() function will return, and I am currently unable to check. If you know how long the session id is, change the length on session_id to match. Similarly for captcha_string I don't know how many letters your captcha will have. Change the definition to fit your uses.
What you are going to do, in words is:
Query the database with the current session_id to see if a captcha image exists.
set the information in a variable, I am using $captcha_data
If $captcha_data is populated with information, use its captcha string, otherwise, create a new on and store it.
Note that for this example, I assume that your database connection, whatever type it is, sits in an object named $db I am using no particular type of database, just methods I use in my own database class. The definitions would look similar for yours, but probably not exact.
$session_id = session_id();
$sql = "SELECT * FROM captcha WHERE session_id='".mysqli_real_escape_string($session_id)."'";
$result = $db->query($sql);
if($result!==false){
$captcha_data = $db->fetchrow($result);
}
$db->free_result($result);
$captcha_string = (isset($captcha_data['captcha_string'])) ? $captcha_data['captcha_string'] : make_hash(); // This function could just be the consolidation of your step in making the hash. I don't know, so it's going to be generic.
if(!isset($captcha_data['captcha_id'])){
$sql = "INSERT INTO captcha (session_id, captcha_string) VALUES('".mysqli_real_escape_string($session_id)."','".mysqli_real_escape_string($captcha_string)."')";
$db->query($sql);//You should check that this query is successful in your code, but for brevity I am skipping it.
}
make_captcha_image($captcha_string); // Again, I don't know exactly how this goes, so I have to assume.
Now keep in mind, I would probably add a date or time column to the captcha data so that I could delete values from accounts that haven't been used in some months, just to keep size down, but that's something I leave to you.
2. $_SESSION Storage
I can't say I recommend this option. It's effectively the same as my previous answer, just without the database interactions.
In words, we are doing the following:
Checking if the $_SESSION['captcha_id'] value is set, if so, use it. If not, create it.
This code is going to look very odd, so I recommend looking up Ternary Operators
//Assuming your session_start() is at the top of the page
$captcha_id = $_SESSION['captcha_id'] = (isset($_SESSION['captcha_id'])) ? $_SESSION['captcha_id'] : make_hash();
make_captcha_image($captcha_string);
3. File System Storage
I'd like to say that if this is the option you're choosing you've got some life decisions to make. This should not be used. Ever. Seriously.
That being said, the basic premise here is
See if a file with the current session_id exists.
if it does, read it's contents and use them as the hash for the image
else, create a new hash, create and populate the data in the file.
How that looks in Code:
$session_id = session_id();
if(file_exists('temp/'.$session_id.'.php')){
$captcha_string = $captcha_data = file_get_contents('temp/'.$session_id.'.php');
}else{
$captcha_string = hash();
file_put_contents('temp/'.$session_id.'.php',$captcha_string);
}
make_captcha_image($captcha_string);
Just to reiterate: don't use this method. I am supplying this answer simply and solely because your question asked HOW to keep the captcha from changing.

PHP safely receiving user input

I would like to be nice to my users but don't want to get malicious code by mistake.
Looking in SO many questions I decided to go whitelisting, so I get only characters I approve.
How is it possible to be kind to the user, so I let them enter many characters and still keep my inputs safe from malicious code?
I am using Apache with PHP 5.3.8, I haven't incorporated PDO because the time I heard of it it I was deep into the project and feared it might be too big of a change.
Any ideas would help!
If it can be output to a user, you must prevent the potentially malicious user from including HTML tags in his code. For example, if, in that post, I could include a script tag, that could be very dangerous for any user reading my post. To prevent this, you want to use htmlentities:
$clean_data = htmlentities($_POST['data']);
That way, my <script> tag will be translated as <script> so it won't harm users when displayed by their browsers.
Now, if you want to save my post in your database, you must beware of SQL injections. Let's say you're saving my post with that query (yes, you shouldn't use mysql_* functions as they are deprecated, but that's just to explain the idea) :
mysql_query($db, "INSERT INTO posts(data) values('$clean_data');");
Sounds good ? Well, if I'm nasty, I'll try to include that post :
test'); DELETE FROM posts; SELECT * FROM posts WHERE data = '
What your MySQL gets is then
INSERT INTO posts(data) values('test'); DELETE FROM posts; SELECT * FROM posts WHERE data = '');
Ouch. So, you must basically prevent your user from including quotes and double quotes in his post, or, more precisely, you should escape them. That really depends on the library you are using, but in the obsolete one I used, that would be written :
$really_clean_data = mysql_real_escape_string($db, $clean_data);
mysql_query($db, "INSERT INTO posts(data) values('$really_clean_data');");
So, with the above malicious post, MySQL would now receive
INSERT INTO posts(data) values('test\'); DELETE FROM posts; SELECT * FROM posts WHERE data = \'');
Now, to MySQL, the whole INSERT INTO posts(data) values('test'); DELETE FROM posts; SELECT * FROM posts WHERE data = ''); part is a correct string, so what happens is what you want to happen.
Basically, that's all you need in almost all the cases. Just remember that, when you feed user data to an interpreter (it can be a web browser, a SQL engine or many other things), you should clean that data, and the best way to do it is to use the dedicated functions that should come with the library you are using.
Just to add to the answer that Fabien already gave, text data types are not the only item you need to be concerned with. Numbers are just as important. Type casting your variables is one way to handle that. For example,
$really_clean_data = mysql_real_escape_string($db, $clean_data);
$query = "UPDATE posts SET post = '".$really_clean_data."' WHERE id = '".(int)$_POST['id']."'";
All data submitted, including the id number of the post you'll be looking to update, is just as suspect and just as open to malicious fiddling as a textarea or text input entry. When you know it should be an integer (number) you can cast it as such. If it's not numeric, that (int) will cast it as 0. You can't update row 0 so the SQL query will fail.
To check for that failure prior to executing the query, you can check the posted data to see if it is a number using the is_numeric() function.
if(!is_numeric($_POST['id'])){
die("Post id is not a number.");
}
For input fields, most notably username and password fields, you can set a max length and check. For example,
if(strlen($_POST['username']) > 24){
$error = "username is too long";
}
If you can get creative about it, preventing SQl injection is actually fun! Keep in mind that a year from now everything on this page could be completely outdated and irrelevant. Security is a process, not a step, so keep on top of it.

Storing users 'following' during a PHP session

I have a simple followers/following system set up within my app.
What would be the best way to store a complete array of 'users following' during a session so that I don't have to query the database multiple times throughout.
To clarify: When a user logs in once they have been authorized I query the database, get a full list of users that the logged in user is following and ultimately store it as a JavaScript variable. This way throughout the site, as the user navigates around I can constantly check the following status against any other user they may come across.
This query is only performed once when the user loads the full page, while navigating around it is not repeated.
This works well for javascript generated content i.e user hovercards etc...
However I would like to store this array (of only userid's) in PHP itself so that I can prevent further queries to the following table while generating content server side that requires a 'follower/following' check to be performed.
What would be the best way to do this?
Please bare in mind that the results are cached in memcached but if I can avoid any lookups at all and have a consistent array to check against that would be great.
Thanks in advance
So let me get this right, you want to store this list so it isn't queried on each page?
First I would recommend you only perform this query on pages that need this data and second I would say that on these pages you don't try this.
Imagine a user stops following or starts following. Some complex cache calculator or event bubbler programmed into your own PHP app to purge the foreign session of data (which is bad, the user has access to data that is not their own, a session infact) could be tiedious with security and performance.
The better thing to do is to perform the query again for each new page. This would be much easier, probably more performant and wouldn't be micro-optimising.
Edit
If you are picking out 10k rows on one page then you might be doing something wrong. You might be looking at it from the wrong perspective.
Store it in the session variable.
http://php.about.com/od/advancedphp/ss/php_sessions.htm
https://www.google.se/search?q=learning+sessions+php
UPDATE
Store the current user in a session and add it to the queries you are already doing to fetch the popup information and add that it tests if the popups person follows the current user.
The two ways that come to mind would be to just write joins with well placed indexes each time, which you've said you don't want to do, and just a simple array in the session, which would probably look something like this:
if (!isset($_SESSION['following']) {
$following = $user->getFollowingIds(); //some query in here to get an array of ids
$_SESSION['following'] = $following;
$_SESSION['following_str'] = implode(',', $following);
}
Then whenever you want to pull all of the statuses for the people that $user is following you can write a query, something like this:
$query('SELECT status.* FROM status WHERE user_id IN (' . $_SESSION['following_str'] . ')'); //not sanitized or safe but you get the idea

Categories