how to secure $_GET - php

I'm trying to secure my $_GET inputs everything are working the problem is $_GET['action'] can't pass preg_match() and always resulta in:
"action is not valid"
I don't know why it's happening, the action can have only two values like or hate which both are in lowercase but still can't pass it.
if(isset($_GET['username']) && isset($_GET['action']))
{
$username = $_GET['username'];
$action = $_GET['action'];
$user_filter_a = mysqli_real_escape_string($username);
$user_filter_b = mysqli_real_escape_string($user_filter_a);
$user_filter_c = strip_tags($user_filter_b);
$action_filter_a = mysqli_real_escape_string($action);
$action_filter_b = mysqli_real_escape_string($action_filter_a);
$action_filter_c = strip_tags($action_filter_b);
$action_filter_c_lower = strip_tags($action_filter_b);
if(preg_match('%^[a-z0-9\.]{4,69}$%',$user_filter_c))
{
if(preg_match('%^[a-z]$%',$action_filter_c_lower))
{
if($action_filter_c_lower=='like')
{
echo 'you have liked this post';
}elseif(action_filter_c_lower=='hate')
{
echo 'you have hated this post';
}
}else
{
echo 'action is not valid';
}
}else
{
echo 'username is not valid';
}

When you need to validate that a string is completely comprised of letters, you can use ctype_alpha().
Code: (Demo)
$string = 'asdgadsfg';
if (ctype_alpha($string)) {
echo "all alphabetical";
} else {
echo "not entirely alphabetical";
}
Output:
all alphabetical
If you need to check if a submitted value is like, hate, or [something else]; create a "whitelist" array to look up the value.
Code: (Demo)
$string = 'like';
$whitelist = ['like', 'hate'];
if (in_array($string, $whitelist)) {
echo "valid: $string";
} else {
echo "invalid: $string";
}
Output:
valid: like
Additional tips:
if(isset($_GET['username']) && isset($_GET['action']))
can be written more simply as:
if (isset($_GET['username'], $_GET['action'])) {
You shouldn't be performing escaping until just before applying the data to the query, this way you don't accidentally mangle your data or confuse yourself. Honest, I always use prepared statements to build a query with user-submitted data, so I'll recommend that you research that topic.
In your regular expression, you don't need to escape a dot inside of a character class so the character class can look like this: [a-z0-9.] If you also wish to allow capital letters, you can add i after the end pattern delimiter (%) so that it looks like %^[a-z0-9.]{4,69}$%i or %^[a-z\d.]{4,69}$%i.
_lower is a strange thing to append to your variable name, because you aren't calling any functions that force the string to lowercase.

The total number of actions is limited. It is more secure to not use the user input at all. Instead, use a switch statement with a default action:
$userAction = strtolower($_GET['action']);
switch($userAction) {
case "like":
$action = "like";
break;
case "hate":
$action = "hate";
break;
default:
$action = "default-action";
}
Or, you can have the list of valid actions in an array:
$validAction = ["like","hate","other"];
$action = "";
if (in_array($_GET["action"],$validAction)){
$action = $_GET["action"];
}
Here you have the guarantee that the action is valid.

Related

How to determine if something is not a string and if something is just empty?

In my php I have 2 optional inputs. input1= and input2=. Both are optional inputs. My question is how do I determine if an input was just not provided or if the input provided was not a string?
I only want people to put an actual string. Not different types of data structures.
Examples
Valid: www.example.com/myfile.php?input1=hello&input2=bye
Valid: www.example.com/myfile.php?input1=hello
Valid: www.example.com/myfile.php?input2=hello
Valid: www.example.com/myfile.php
Invalid: www.example.com/myfile.php?input1[]
<?php
function check_valid($string) {
if (!is_string($string)) {
echo "This is a not string. We tested: ".$string."<br>";
} else {
echo "This is is string. We tested: ".$string."<br>";
}
}
$input1 = check_valid($_GET['input1']);
$input2 = check_valid($_GET['input2']);
?>
You should use the isset() method to check if $input1 or $input2 are provided or not.
function check_valid($test) {
if (isset($test) && is_string($test)) {
echo "Valid";
return;
}
echo "Not Valid";
}
You can check if the value exists, containts empty or null.
<?php
function check_valid($string) {
if (trim($string) == NULL OR trim($string) == "") { //Will check if value without a space is null or empty
return "valid";
}
}
$input1 = check_valid($_GET['input1']);
$input2 = check_valid($_GET['input2']);
?>

Is there a way to Sanitize a number of variables at once?

I am trying to sanitize 54 variables at once.
Currently I am doing them one by one like so
if($vissuedate != '') {
$vissuedate = filter_var($vissuedate, FILTER_SANITIZE_STRING);
if($vissuedate == ''){
$vvalidate++;
}
}
I have a whole bunch more and want to ask if there is a way to validate all of them in one go?
You could put all the keys from $_GET in an array:
$get_array = array_keys($_GET);
Or, if you want to skip some, you could use your own array:
$get_array = array('name','adress','phone',...);
Then:
foreach($get_array as $v=>$key){
//check if exists
if(!isset($_GET[$key]){
//do something
}
else{
//do sanitizing here }
}
}
Or even, if you want to do different kinds of sanitizing, you could use an array with number:
$get_array = array('name'=>1,'adress'=>1,'phone'=>2,...);
foreach($get_array as $v=>$key){
//check if exists
if(!isset($_GET[$key]){
//do something
}
else{
switch($v){
case 1:
//do something here;
break;
case 2:
//do something else
break;
}
}
}
}
You can iterate through your $_GET like this:
<?php
foreach($_GET as $key => $value) {
if($_GET[$key] != '') {
$vissuedate = filter_var($_GET[$key], FILTER_SANITIZE_STRING);
if($vissuedate == ''){
$vvalidate++;
}
}
Or as you says assign them all to an array and then attempt to validate from there

Validate password policy with PHP preg_match

I'm trying to validate a password using preg_match and RegEx but it doesn't seem to work. What I want to do is: ensure the password meets the following minimal conditions:
- Contains mixed case letters
- Contains atleast one number
- The rest can be anything as long as the two conditions above are met.
I've tried the following RegEx but it doesn't seem to work properly:
(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])
I've had other previous easier RegEx'es like: [A-Za-z0-9] but without success. I'm checking if preg_match($string, $pattern) == 0 (meaning the pattern doesn't match => validation fails) but it always returns 0. What am I doing wrong ?
Just add a starting anchor to your regex,
^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])
OR
^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9]).*
Example:
$yourstring = 'Ab';
$regex = '~^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9])~m';
if (preg_match($regex, $yourstring)) {
echo 'Yes! It matches!';
}
else {
echo 'No, it fails';
} // No, it fails
I always try to avoid regex if it's possible so I took a different approach to the problem. The below code will test the password for at least one uppercase, one lowercase and one digit.
function isValidPassword($password)
{
$hasUppercase = false;
$hasLowercase = false;
$hasDigit = false;
foreach (str_split($password) as $char)
{
$charAsciiValue = ord($char);
if ($charAsciiValue >= ord('A') && $charAsciiValue <= ord('Z')) {
$hasUppercase = true;
}
if ($charAsciiValue >= ord('a') && $charAsciiValue <= ord('z')) {
$hasLowercase = true;
}
if ($charAsciiValue >= ord('0') && $charAsciiValue <= ord('9')) {
$hasDigit = true;
}
}
return $hasUppercase && $hasLowercase && $hasDigit;
}
var_dump(isValidPassword('Ab9c'));
var_dump(isValidPassword('abc'));
Output
bool(true)
bool(false)
I offer a different solution, mainly because regexp provides little error reporting, and you would have to manually test the string afterwards anywhay for cohesion. Consider breaking the patterns apart and adding their own error. Iterate each of the requirements, and test the pattern. Push errors into an array and check after for their existence. Return a predeclared variable as true/false for the purpose of validating using if(validate_password($pass)):. Here's the mockup:
function validate_password($pass){
$requirements = array();
//uppercase
$requirements['uppercase']['pattern'] = '/[A-Z]/';
$requirements['uppercase']['error'] = 'Your password must contain at least one uppercase letter.';
//lowercase
$requirements['lowercase']['pattern'] = '/[a-z]/';
$requirements['lowercase']['error'] = 'Your password must contain at least one lowercase letter.';
//requires a number
$requirements['number']['pattern'] = '/[0-9]/';
$requirements['number']['error'] = 'Your password must contain at least one number.';
//special characters
$requirements['special_character']['pattern'] = '/[!##$%^&*()\\-_=+{};\:,<\.>]/';
$requirements['special_character']['error'] = 'Your password must contain at least one special character.';
//length
$requirements['length']['pattern'] = '/^.{8,}/';
$requirements['length']['error'] = 'Your password must be at least 8 characters in length total.';
$is_valid = false; //our flag to return as true once all tests have passed.
$errors = false;
//validate all requirements
foreach($requirements as $idx => $req):
if(preg_match($req['pattern'], $pass, $matches)):
$is_valid = true;
else:
$errors[] = $req['error'];
$is_valid = false;
endif;
endforeach;
//if we had errors above
if($errors):
$is_valid = false;
foreach($errors as $error):
echo '<p>', $error, '</p>';
endforeach;
endif;
return $is_valid;
}
$pass = 'j!Adz6'; //change this to test
echo validate_password($pass);
And an eval.in example for your pleasure.

PHP Regex for Filtering Domain Names in _POST Username

I have some hardcoded if/else statements to set $page variable - (for later use in header: ".page") - to a given website based on the $_POST["username"] input.
CODE:
if ($_POST["username"] == "username1#domain1.com") {
$page = "http://www.google.com";
}
else if ($_POST["username"] == "username2#domain1.com"){
$page = "http://www.yahoo.com";
}
else if ($_POST["username"] == "username1#domain2.com"){
$page = "http://www.stackoverflow.com";
}
else if ($_POST["username"] == "username2#domain2.com"){
$page = "http://www.serverfault.com";
}
else if ($_POST["username"] == "username#domain3.com"){
$page = "http://www.superuser.com";
}
else if (!preg_match($domain2.com, $_POST["username"])) { //THIS IS VERY WRONG
$page = "http://www.backblaze.com";
}
else{
$page = "DefaultBackupPage.php";
}
I am trying to say if your username has "#domain.com" at the end of it, set the $page to, in this case backblaze.com, but could be anything.
I am aware it is messy, I don't actually like this implementation. It just has to fit in this schema and I need a quick fix!
The current error I am receiving is that the regular expression is empty. I am hoping this is a no brainer for someone who knows PHP - which I have been hastily trying to learn!
if (preg_match('/#domain2\.com$/i',$_POST['username']))
will catch username ending with domain2.com. Please note the escaping backward slash for the dot. If you want to test against the opposite (aka. does NOT end with domain2.com) then use an exclamation mark before the preg_match() function.
Is this what you were asking?
EDIT 1: I added the i flag to the pattern to make it look for a case-insensitive match.
EDIT 2: For the sake of readability and control I would wrap this in a function instead, but that's my own preference so it's not the suggested approach or anything. In case your code is long and complicated…
function get_page($username) {
$username = strtolower($username);
switch ($username) {
case "username1#domain1.com" : return "http://www.google.com";
case "username2#domain1.com" : return "http://www.yahoo.com";
case "username1#domain2.com" : return "http://www.stackoverflow.com";
case "username2#domain2.com" : return "http://www.serverfault.com";
case "username#domain3.com" : return "http://www.superuser.com";
}
return preg_match('/#domain2\.com$/',$username) ?
"http://www.backblaze.com" : "DefaultBackupPage.php";
}
$page = get_page($_POST['username']);
The line:
else if(!preg_match($domain2.com, $_POST["username"]))
has to be:
else if(!preg_match("/domain2\.com/", $_POST["username"]))

PHP GET String with explode and if statements

I am writing what I thought would be a simple script but I am stuck.
The scenario is that I want to create 2 strings from the GET request.
eg: domain.com/script.php?Client=A12345
In script.php it needs to grab the "Client" and create 2 variables. One is $brand and needs to grab the A or B from the URL. The Other is $id which needs to grab the 12345 from the URL.
Now, after it has these 2 variables $brand and $id it needs to have an if statement to redirect based on the brand like below
if ($brand=="A") {
header('Location: http://a.com');
}
if ($brand=="B") {
header('Location: http://b.com');
At the end of each URL I want to apend the $id though and I am unsure on how to do this.
So for example I would access the script at domain.com/script?Client=A1234 and it needs to redirect me to a.com/12345
Thanks in advance!
$fullCode = $_REQUEST['Client'];
if(strpos($fullCode, 'A') !== false) {
$exp = explode('A',$fullcode);
header('Location: http://a.com/' . $exp[1]);
}
else if(strpos($fullCode, 'B') !== false) {
$exp = explode('B',$fullcode);
header('Location: http://b.com/' . $exp[1]);
}
else {
die('No letter occurence');
}
You can easily do,
$value = $_GET['Client'];
$brand = substr($value, 0, 1);
$rest = substr($value, 1, strlen($brand)-1);
now you have the first character in $brand string and you can do the if statement and redirect the way you want...
You mean like this?
Notice: this will only work if brand is just 1 character long. If that's not the case, please give better examples.
<?php
$client = $_GET['Client'];
$brand = strtolower(substr($client, 0, 1));
$id = substr($client, 1);
if ($brand == 'a')
{
header("Location: http://a.com/$id");
}
elseif ($brand == 'b')
{
header("Location: http://b.com/$id");
}
?>
Try using:
preg_match("/([A-Z])(\d*)/",$_GET['Client'],$matches);
$matches[1] will contain the letter and $matches[2] will contain your id.
Then you can use:
if ($matches[1]=="A")
{
header('Location: http://a.com/{$matches[2]}');
}
if ($matches[1]=="B")
{
header('Location: http://b.com/{$matches[2]}');
}
suggest you could also try
$requested = $_GET["Client"];
$domain = trim(preg_replace('/[^a-zA-Z]/',' ', $requested)); // replace non-alphabets with space
$brand = trim(preg_replace('/[a-zA-Z]/',' ', $requested)); // replace non-numerics with space
$redirect_url = 'http://' . $domain . '/' . $brand;
header('Location:' . $redirect_url);
but it'd be better if you could get the domain name and brand as two individual parameters and sanitize them individually before redirecting them to prevent the overhead of extracting them from a single parameter.
Note: this expression might be useless when the domain name itself has numerics and because the Client is obtained through get a good deal of validation and sanitation would be required in reality.
$brand = strtolower($_GET['Client'][0]);
$id = substr($_GET['Client'], 1);
header("Location: http://{$brand}.com/{$id}");
If for some purpose you want to use explode, then you need to have a separator.
Let's take '_' as the separator, so your example would be something like this: domain.com/script.php?Client=A_12345
$yourstring = explode("_",$_GET["Client"]);
echo $yourstring[0];
//will output A
echo $yourstring[1];
//will output 12345
//your simple controller could be something like this
switch($yourstring[0]){
case: 'A':
header('Location: http://a.com?id='.$yourstring[1]);
exit();
break;
case: 'B':
header('Location: http://b.com?id='.$yourstring[1]);
exit();
break;
default:
//etc
}

Categories