I am just starting PHP and have a form where users can submit data. Before I display or send the data, it is sanitized and validated (trim, stripslashes, and htmlspecialchars) and saved as new variables which are then used instead of the directly submitted values.
My question is, is it safe to do anything at all with the unsanitized values? Do the security implications only become apparent when the values are displayed?
Specifically, would there be any problems with doing code such as
if(empty($_POST["theirname"]){code;}
if they tried some kind of attack or placed code into that box while submitting?
Currently I sanitize all input before checking if they are empty, but I want to avoid errors/warnings in this case if a user submits a blank box for example (as the sanitizing function could be called on POST values that don't exist)
PHP's filter_* functions. They're functions that do sanitizing for every variable you have, be it server variables (like $_SERVER, $_GET, $_POST, $_REQUEST) or your own variables.
Since you want to sanitize $_POST, here's what you should use:
$theirname = filter_input(INPUT_POST, "theirname");
if (!$theirname) {
echo "theirname is invalid!";
} else {
// your code
}
filter_input can check if the variable exists, if it's empty, and if it has anything that can make your code/website vulnerable (HTML tag injection, JS code injection, PHP code injection by evaluation, etc.). It's way better than checking it by yourself.
Of course, you can always just check it by yourself if you decide not to trust the filter_* functions, in that case you need to:
Check if the variable exists by using is_null and/or checking against NULL;
Check if the variable is empty;
Check if the variable has special characters (and escape them properly);
Check if the variable has HTML or XML tags (and escape/delete them);
Check if the variable has JS code or script tags (and escape/delete them);
Check if the variable has PHP code and if it's trying to execute it via eval;
As you can see, it's an extensive list, and if you don't want to rely on PHP's built-in functions, that's what you need to do.
Sources:
PHP: Filter Functions - Official PHP docs
PHP 5 Filter functions - W3Schools
always check for undefined variables first.
if(!is_null($_POST["theirname"])){
if(empty($_POST["theirname"]){code;}
}
It is absolutely required to check for existence for any variable.
$bingo = isset($variable);
Normally you want to first check if a variable is defined at all. You can do this by if(!is_null($_POST["theirname"]))
If it is defined, then you maybe want to check if it is empty, and if not, do some stuff. (for example sanitizing and / or validating)
if(!is_null($_POST["theirname"])){
if(!empty($_POST["theirname"])
{
//Do some stuff here
}
else
{
//send notification that the user didn't input any data
}
}
The error in your example is you have missing ) in your if judgment.
//Your code
if ( empty($_POST["theirname"]) { code; }
^^
//Code updated
if (empty($_POST["theirname"])) { code; }
Before checking if your inputs are empty, you could check if your form is defined and is not NULL.
Example:
//Reset.
$msg = $theirname = NULL;
//Check if the form is defined.
if (isset($_POST)) {
//Check input empty.
if(empty($_POST['theirname'])) {
$msg = 'Inputs are required';
} else {
$theirname = data_filter($_POST['theirname']);
}
//check $theirname.
if ($theirname) {
//Do something with $theirname...
}
}
//Filter function.
function data_filter($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
//Message.
echo $msg;
Checkout filter_var and filter_input.
Related
I have been trying to create a little script that dies when it detects base64 encoded information being posted to our server.
For some reason it is not entering the loop ...
if (in_array('base64_decode', $_POST, true)) { ... }
When I test it. What am I missing?
Edit: Sorry for this misunderstanding I wasn't clear enough. I am having things like ...
[gkwdwjfvzjpj] => eval(base64_decode($_POST....
Posted to the server and I want to know how can I just detect this string.
What your code searches for is whether or not the string "base64_encode" is one of the POSTed values.
If you want to check if base64_decode is in a substring of the POSTed data:
function spam_in_post_values () {
foreach ($_POST as $postval) {
if (strpos($postval, 'base64_decode') !== false) {
return true;
}
}
return false;
}
However, it seems that you are inserting POSTed data into the HTML, which is a bad idea.
There is a principle in programming called Don't trust user input. You should:
never ever directly insert user input into the HTML
<p><?php echo $_POST['userdata']; ?></p>
when the user posts something like
"</p><script>location.href='http://otherwebsite';</script>"
your users will be kidnapped!
The same is true for attributes, never use unescaped userdata in attributes:
<a onclick="alert('Hello <?php echo $_POST['username']; ?>!')">
When the user posts "'); location.href='http://spamsite.com';('"
users of your website will get kidnapped!
never ever directly eval user input in PHP:
$x = $_POST['x']; // we expect "5"
$y = $_POST['y']; // we expect "3"
$operator = $_POST['operator']; // we expect "*", "+", "-", "/"
$result = eval($x . $operator . $y);
When the user sends malicious data, he can do everything you can do with your privileges
in PHP. Delete files, send emails, download and install malware to your server, and so on.
never ever run eval on user input in JavaScript (even better, never use eval!)
For the same reasons outlined above, malicious input can run arbitrary code in your client.
If you expect to get JSON data, use JSON.parse(jsondata) to get them as an object (or jQuery.parseJSON(...), or angular.parseJSON(...), or whatever your library provides).
This also extends to "hidden" uses of eval, like new Function("arg", userSuppliedString), event handlers element.onclick = "alert('<user supplied value>')", setTimeout/setInterval calls setTimeout("element.textContent = " + userSuppliedValue, 3000), etc.
Instead of testing for data that you do not want, validate that you received data you do want.
My form has several types of inputs including text, checkbox and radio. I'd like to make sure the form is secure. I used the Prestashop functions isGenericname and isCleanHTML to check the text and comment fields by ensuring the fields are valid.
Prestashop Validate.php
public static function isGenericName($name)
{
return empty($name) || preg_match('/^[^<>={}]*$/u', $name);
}
public static function isCleanHtml($html, $allow_iframe = false)
{
$events = 'onmousedown|onmousemove|onmmouseup|onmouseover|onmouseout|onload|onunload|onfocus|onblur|onchange';
if (preg_match('/<[\s]*script/ims', $html) || preg_match('/('.$events.')[\s]*=/ims', $html) || preg_match('/.*script\:/ims', $html))
return false;
if (!$allow_iframe && preg_match('/<[\s]*(i?frame|form|input|embed|object)/ims', $html))
return false;
return true;
}
This is how the function is called in the form PHP file.
if (!Validate::isCleanHtml($message))
$this->errors[] = Tools::displayError('Invalid message');
elseif (!Validate::isGenericName($fname))
$this->errors[] = Tools::displayError('Invalid First Name.');
So my question are. Is it ok to not produce an error message for inputs such as check boxes and radio box that are not valid? The only reason they'd be invalid was if someone hacked he code before sending. Or is there a better way to strip and secure the inputs?
$checkbox = Tools::getValue('checkbox ');
if (!Validate::isGenericName($checkbox ))
$validCheckbox = $checkbox;
I have 68 inputs I want to make sure are secure. Is there a good PHP function that can strip out and stop any sort of SQL injection? Prestashop documents state "getValue() does not protect your code from hacking attempts (SQL injections, XSS flaws and CRSF breaches). You still have to secure your data yourself." I'm thinking I'll need to scrub it all through trim(), stripslashes(), htmlspecialchars() but I didn't know of the most efficient way.
To prevent first order SQL injection you can use PDO with mysql prepared statement.
And when you want to display it to the html page use
htmlspecialchars(trim($value), ENT_QUOTES, "UTF-8")`
Make sure you set the appropriate character encoding in your response header correctly and use the meta tag to indicate character encoding of your HTML.
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
If you ever need to update back the html output into the database. Use
htmlspecialchars_decode(trim($value))
This should give you some protection.
I have a bit of code which checks 2 $_GET variables with preg_match. It also looks up one variable value in the database. The problem is that the email address which is url encoded and the # symbol is replaced with %40 is not turned back into readable text when I call the variable.
So if I call $_GET['email'] the value displayed is someone%40example.com while it should be someone#example.com
I understand $_GET variables get decoded automatically but it is not working for me. This problem came with the installation of SSL on this domain. Could it have something to do with that?
Here's my code:
if (isset($_GET['Email']) && preg_match('/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*#([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/', $_GET['Email'])) {
$Email = $_GET['Email'];
}
U need to put urldecode()
$_GET variable doesnot get url decoded automatically. You have to do it manually.
Do something like this
if (isset($_GET['Email']) && preg_match('/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*#([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/', urldecode($_GET['Email'])))
{
$Email = urldecode($_GET['Email']);
}
Also, this is not the proper way of validating email
Check your content-type header you are sending. If you are submitting a form, then I you should probably be using application/x-www-form-urlencoded type in your form to tell PHP that the data is URL-encoded and should be automatically decoded. That is unless you are submitting a file upload, in which case multipart/form-data is appropriate and may require manual decoding of content (using urldecode() depending on how it is actually sent. You can inspect $_SERVER['CONTENT_TYPE'] to help you programatically determine whether you need to manually decode.
A few other pointers:
You should probably consider using POST here instead of GET unless your expectation is that this would be a navigable page/or endpoint tied to that email address (i.e. something someone could bookmark). Think for the GET action is reading something from a location specified by the query string and POST as being related to making some specific action related to the POSTed data.
You should consider using filter_var() or filter_input() along with the email validation filter instead of regex.
Suggested usage would be:
$email = filter_var($_GET['email'], FILTER_VALIDATE_EMAIL);
if(false === $email) {
// validation failed
}
// or
$email = filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL);
if(is_null($email) {
// key was not present in GET params
} else if (false === $email) {
// validation failed
}
I am developing a basic API for some simple functionality. I am capturing inputs like below:
if ($action == 'delete' && isset($_POST['targetId']) && isset($_POST['userId'])) {
//The isset causes "Do not access Superglobal _POST array directly" warning in Netbeans
$userId = filter_input(INPUT_POST, 'userId');
$beamId = filter_input(INPUT_POST, 'targetId');
}
Should I use filter_input even for checking whether the value is set? Like:
if ($action == 'delete' && filter_input(INPUT_POST, 'targetId') && filter_input(INPUT_POST, 'userId')) {
}
I am not looking at options here rather I would be happy with the most correct solution which is secure and hack resistant.
EDIT: Yes, the above information will be used as inputs for SQL
Another solution could be use filter_has_var().
if (filter_has_var(INPUT_POST, "userId")) {
//occurs when $_POST['userId'] is set, even when empty
}
More info in: Official documentation
filter_input is used to sanitize or check the data type of the input. It is used to make sure that the data you are expecting is in the required format and you can sanitize it using required filters. isset in your case will check if the required variable is set or not (or is not NULL). So both have different usecases. I don't see using isset directly on a POST item as bad but I would recommend using filter_input so that the data can be validated as well.
The main problem with unfiltered input is essentially code injection. That's a problem where you use the input as part of a SQL statement or your output back to the user (JavaScript).
As such, it is not necessary to do it just to check if a value was entered. It is good to rather be consistent and use filter_input first to populate variables and then use those variables to check if the fields were populated if you will be using the value as above later on in your script.
I'm fairly new to PHP and am creating a website for my company. I am using some existing code that I have obtained but can't seem to make it work properly - any help would be much appreciated!
I have a variable, $id, which identifies the product category type to display on the page. I first need to check that the id variable has been set and if not, default the variable to category 0.
The code I have is as follows:
setdefault($id, 0);
function setdefault(&$var, $default="")
{
if (!isset($var))
{
$var = $default;
}
}
So with the address www.website.com/browse.php, I would expect it to default to $id=0; with the address www.website.com/browse.php?id=3, I would expect it to set $id to 3 and display the relevant products. However, despite setting $id, it still defaults to 0. Is there something obviously incorrect with my code?
You are probably expecting PHP to use the $_POST and $_GET as global variables. PHP used to be setup this way, back in the day, but newer versions require you to explicitly reference these variables.
You could try this:
setdefault($_GET['id'], 0);
function setdefault(&$var, $default="")
{
if (!isset($var))
{
$var = $default;
}
}
or even more simply (using the ternary operator):
$id = array_key_exists('id', $_GET) ? $_GET['id'] : 0;
First off, if this is PHP 5.X I highly recommend you do not pass variables by reference using the &. That being said. the isset function call will always be true withing the function. But you will receive an undefined variable warning on the setdefault($id, 0);
Try this instead.
$id = isset($id) ? $id : 0;
If $id is not set, the call to setdefault($id,0) will generate a warning. A function like setdefault does not work in PHP. Use this instead.
if (!isset($id)) $id = 0;
If you are doing this for array variables, like $_GET and $_POST, you can do this:
function getuservar($A, $index, $default = '') {
if (!isset($A[$index])) return $default;
if (get_magic_quote_gpc()) return stripslashes($A[$index]);
return $A[$index];
}
$clean_id = getuservar($_GET, 'id', 0);
This return form is better because you stop using the $_GET array immediately and only use the variables that are cleaned in the rest of the code. You clean your external variables once and never touch the external variables again in your code. $A can be $_GET, $_POST, $_REQUEST or $_COOKIE.
It also handles the annoying magic_quote stuff for you so you know the data in the variable is the text sent by the user. Just remember to clean it again when sending it back to the user or to the database.
How is $id being set? If register_globals is off (and it's off by default in PHP 4.2 and newer), you need to look at $_GET['id'] or $_POST['id'] instead (or $_REQUEST['id'], but there are reasons to avoid that).
The code to set $id to sometime from the query string (browse.php?id=3) would also need to be included. You might be thinking of the register_globals settings that PHP has that will automatically create variables when included in the query string. DO NOT re-enable that feature, for several years now that has been shown to be a really bad idea.
Any variable you are pulling from the query string needs to be checked for type/safety before using it. So for example you might pull the variable from the $_GET super global, check to see if it's numeric, if it is not then set the default:
if (!is_numeric($_GET['id']) {
setdefault($_GET['id'], 0);
}