Jump to content

Security


Wright

Recommended Posts

Hello people, I’m new here to PHPFreaks.

 

You know how information out there can be misleading, outdated or not consistent between sources, so I decided to stop by and ask you guys personally.

 

I’m making a file-hosting game related website which will have a user system. I’m currently creating user related functions (and saving them into my little library folder) but I’m stuck on the user input part. As you know you have to take some precautions with user inputted data before allowing it to pass to a MySQL server.

 

As I intend to make a safe community for all, I would like to know how I should sanitize/clean user data before allowing it any further to MySQL queries. Here goes a couple questions:

 

1. I heard about many filtering options. Which should be ideal to filter user input such as username?

 

2. What should I allow in username and passwords? Just a-Z and 0-9 characters? And how do I do that?

 

3. What encryption should I use for my fellow users passwords? Md5()?

 

I think that’s for now. Please don’t bother answering if you are in a rush or something (take no offense), this is a delicate question and any professional feedback would be greatly appreciated.

As for my skills, let’s say I’m between novice and medium user of PHP (more for medium really).

 

Sincerely,

Wright

 

Link to comment
Share on other sites

The function mysql_real_escape_string() pretty much takes care of any risk of injection. The usernames really shouldn't matter as long as you escape them. As for your passwords, MD5 is fine, but I would suggest salting them with your own string to prevent any possible reverse look-ups.

 

$salt = "%4$@";
$pass = MD5($_POST['pass'] . $salt);

 

Good luck!

Link to comment
Share on other sites

The function mysql_real_escape_string() pretty much takes care of any risk of injection. The usernames really shouldn't matter as long as you escape them. As for your passwords, MD5 is fine, but I would suggest salting them with your own string to prevent any possible reverse look-ups.

 

$salt = "%4$@";
$pass = MD5($_POST['pass'] . $salt);

 

Good luck!

 

Thanks for your answer!

Though wikipedia says at http://en.wikipedia.org/wiki/SQL_injection

 

"A straight-forward, though error-prone way to prevent injections is to escape  dangerous characters. One of the reasons for it being error prone is that it is a type of blacklist which is less robust than a whitelist. For instance, every occurrence of a single quote (') in a parameter must be replaced by two single quotes ('') to form a valid SQL string literal."

 

What does this mean? Is it hack-able after all?

Link to comment
Share on other sites

If you want to protect your users, then you must perform the following and in the correct order.

 

1) Whenever inserting or updating database data, you must escape the data properly to prevent injection.

 

2) Whenever selecting data from the database and displaying in HTML, you must use htmlentities() to prevent malicious markup-based attacks.

 

As far as escaping data when inserting / updating, you can use mysql_real_escape_string() and be safe.  Personally, I prefer to use PDO for my database interaction because it allows the user of placeholders and will escape data without my having to do so.  You can also configure PDO to throw exceptions on errors, which I prefer as it generally results in less error-handling code.

 

I can not stress enough that you must, absolutely must, call htmlentities() on the data after you've retrieved it from the database.  Many programmers insist on calling htmlentities() before inserting data into the database thinking that will save them from calling htmlentities() after retrieving data.  The problem with this approach is if the database is attacked directly, the attacker will insert malicious markup that will not pass through htmlentities().  Since the application assumes all data in the database has had htmlentities() applied to it, the application will blindly pass the harmful markup to your users.

 

As far as validating data, it's best to ensure that every piece of user input is in the correct format.  Regular expressions are helpful for this in addition to some functions (like http://www.php.net/manual/en/ref.ctype.php).

 

As stated, it doesn't really matter what you accept for input as long as you escape before inserting / updating and escape before displaying to a browser.

 

You will have to be wary of any user input that causes access to the file system.  There are plenty of exploits where sites expect something like:

showfile.php?filename=foo.txt

A malicious user might try:

showfile.php?filename=../../../../../etc/passwd

 

If your site doesn't watch out for that type of input, users might gain access to files you don't want them to see.

 

3.  What encryption should I use for my fellow users passwords? Md5()?

You need to understand the difference between encryption and hashing.  Encryption is two-way, meaning you can turn data into junk and then back into the same data.  Hashing completely trashes the data in a way you can never, ever, not in a million years know what the original piece of data was again.

 

Password hashing is quite common.  md5() is considered a weaker hashing algorithm by today's standards.  You should move on to sha1() or sha256().  As mentioned you should salt your hashed data before hashing it; do some research to see why.  Never, ever hash a hashed value.

Link to comment
Share on other sites

Yeh Daniel0 though filter_var is not good for emails. I discovered this one at some linux website and made some modifications so it doesn't check for the domain anymore:

function validateEmail($email)
{
/**
Validate an email address.
Provide email address (raw input)
Returns true if the email address has the email 
address format and the domain exists.
*/
   $isValid = true;
   $atIndex = strrpos($email, "@");
   if (is_bool($atIndex) && !$atIndex)
   {
      $isValid = false;
   }
   else
   {
      $domain = substr($email, $atIndex+1);
      $local = substr($email, 0, $atIndex);
      $localLen = strlen($local);
      $domainLen = strlen($domain);
      if ($localLen < 1 || $localLen > 64)
      {
         // local part length exceeded
         $isValid = false;
      }
      else if ($domainLen < 1 || $domainLen > 255)
      {
         // domain part length exceeded
         $isValid = false;
      }
      else if ($local[0] == '.' || $local[$localLen-1] == '.')
      {
         // local part starts or ends with '.'
         $isValid = false;
      }
      else if (preg_match('/\\.\\./', $local))
      {
         // local part has two consecutive dots
         $isValid = false;
      }
      else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain))
      {
         // character not valid in domain part
         $isValid = false;
      }
      else if (preg_match('/\\.\\./', $domain))
      {
         // domain part has two consecutive dots
         $isValid = false;
      }
      else if
(!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/',
                 str_replace("\\\\","",$local)))
      {
         // character not valid in local part unless 
         // local part is quoted
         if (!preg_match('/^"(\\\\"|[^"])+"$/',
             str_replace("\\\\","",$local)))
         {
            $isValid = false;
         }
      }
   }
   return $isValid;
}

 

What you guys think of this function to check email upon registration? Don't need to escape anything here, since it doesn't accept unusual/suspicious emails.

 

Where can I learn about PDO? I always used mysql_connect and mysql_query etc for my stuff. Any good professional tutorials out there?

Link to comment
Share on other sites

Yeh Daniel0 though filter_var is not good for emails.

 

How does filter_var() not work for emails?

 

if (!filter_var('daniel.egeberg@gmail.com', FILTER_VALIDATE_EMAIL)) {
echo 'invalid email';
}

if (!filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL)) {
echo 'invalid email';
}

 

Where can I learn about PDO?

 

Have you checked out the manual?

 

http://php.net/pdo

http://php.net/pdo.connections

http://php.net/pdo.prepared-statements

Link to comment
Share on other sites

Yeh Daniel0 though filter_var is not good for emails.

 

How does filter_var() not work for emails?

 

if (!filter_var('daniel.egeberg@gmail.com', FILTER_VALIDATE_EMAIL)) {
echo 'invalid email';
}

if (!filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL)) {
echo 'invalid email';
}

 

Where can I learn about PDO?

 

Have you checked out the manual?

 

http://php.net/pdo

http://php.net/pdo.connections

http://php.net/pdo.prepared-statements

 

I made some testing myself and read about it, it says it's false for some valid emails.

 

Thanks for the PDO links, didn't know PHP.net had those.

Link to comment
Share on other sites

I made some testing myself and read about it, it says it's false for some valid emails.

 

Could you please submit a bug report at http://bugs.php.net then? :)

 

Really? Try it yourself, things like something@localhost are accepted. I never thought of it as a bug  :o But sure... I'll take a look at that bug website. I kinda wish it worked for all compliant emails out there, now that you mention it...

 

EDIT: Wow, wait a moment!

 

if (!filter_var('something@localhost', FILTER_VALIDATE_EMAIL)) {
echo 'invalid email';
}

^ With this it doesn't recognize as invalid..

 

if (!filter_input(INPUT_POST, 'something@localhost', FILTER_VALIDATE_EMAIL)) {
echo 'invalid email';
}

^ But here it says it is. What might be the reason?

 

This changes alot. should i mysql_real_escape_string("email") and then apply this filter and then allow it to go through MySQL query? Is it safe?

Link to comment
Share on other sites

I made some testing myself and read about it, it says it's false for some valid emails.

 

Could you please submit a bug report at http://bugs.php.net then? :)

 

Really? Try it yourself, things like something@localhost are accepted. I never thought of it as a bug  :o But sure... I'll take a look at that bug website. I kinda wish it worked for all compliant emails out there, now that you mention it...

 

EDIT: Wow, wait a moment!

 

if (!filter_var('something@localhost', FILTER_VALIDATE_EMAIL)) {
   echo 'invalid email';
}

^ With this it doesn't recognize as invalid..

 

if (!filter_input(INPUT_POST, 'something@localhost', FILTER_VALIDATE_EMAIL)) {
   echo 'invalid email';
}

^ But here it says it is. What might be the reason?

 

This changes alot. should i mysql_real_escape_string("email") and then apply this filter and then allow it to go through MySQL query? Is it safe?

 

Thats is because you are not using filter_input correctly, when using input POST/GET the 2nd parameter should be the key for the data in the POST/GET array (The name of the HTML input field.)

Link to comment
Share on other sites

I made some testing myself and read about it, it says it's false for some valid emails.

 

Could you please submit a bug report at http://bugs.php.net then? :)

 

Really? Try it yourself, things like something@localhost are accepted. I never thought of it as a bug  :o But sure... I'll take a look at that bug website. I kinda wish it worked for all compliant emails out there, now that you mention it...

 

EDIT: Wow, wait a moment!

 

if (!filter_var('something@localhost', FILTER_VALIDATE_EMAIL)) {
   echo 'invalid email';
}

^ With this it doesn't recognize as invalid..

 

if (!filter_input(INPUT_POST, 'something@localhost', FILTER_VALIDATE_EMAIL)) {
   echo 'invalid email';
}

^ But here it says it is. What might be the reason?

 

This changes alot. should i mysql_real_escape_string("email") and then apply this filter and then allow it to go through MySQL query? Is it safe?

 

Thats is because you are not using filter_input correctly, when using input POST/GET the 2nd parameter should be the key for the data in the POST/GET array (The name of the HTML input field.)

 

This isn't the problem i'm talking about it's more like this, take this page for example:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>

<body>
<?php
if (!filter_var($_GET["email"], FILTER_VALIDATE_EMAIL)) {
echo 'invalid email on filter_var';
}
echo "<br />";
if (!filter_input(INPUT_GET, 'email', FILTER_VALIDATE_EMAIL)) {
echo 'invalid email on filter_input';
}
?>

</body>
</html>

 

Now if I do thatpage.php?email=peace@mylocalhostisniceandsmellslikechocolate it doesn't say it's invalid, when, and I think you agree, it should.

I'm sure there are other examples, but I can't be bothered to look for more haha.

Link to comment
Share on other sites

should i mysql_real_escape_string("email") and then apply this filter and then allow it to go through MySQL query? Is it safe?

 

You should use prepared statements if you are using PDO or MySQLi.

 

Rough, untested example:

<?php
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$errors = array();
if (!($name = filter_input(INPUT_POST, 'name'))) {
	$errors[] = 'Missing name.';
}
if (!($email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL))) {
	$errors[] = 'Invalid email address.';
}

if (count($errors)) {
	echo 'Errors:<br>' . join('<br>', $errors);
	// make this better of course 
}
else {
	$stmt = $db->prepare('INSERT INTO people (name, email) VALUES (?, ?)');

	$stmt->execute(array($name, $email));
}
}
else {
// show form
}

 

Now if I do thatpage.php?email=peace@mylocalhostisniceandsmellslikechocolate it doesn't say it's invalid, when, and I think you agree, it should.

I'm sure there are other examples, but I can't be bothered to look for more haha.

 

No, that's a perfectly valid email address. The validity does not refer to its existence, but its potential existence, i.e. is the format valid.

Link to comment
Share on other sites

But hmmm, I'm not going to allow emails that don't have any use... at least they should make it have a country code (.pt, .pl .co.uk, etc) or general ones (.com, etc), and not ones like that, that I can't send anything to (even to activate account or send reminders or whatever else!). What kind of validation is that? :S It's very confusing.

 

EDIT: I'm still researching on PDO, thanks for the example though!

Link to comment
Share on other sites

Well, you cannot send an email to khhjkjfakadk@phpfreaks.com. How do you imagine your script should catch that? That's why the typical way of checking the existence of an email address (as opposed to its validity) is to send an email to the user and have them follow a link with a unique, random code. If they got the code, they must have gotten the email.

Link to comment
Share on other sites

Well, you cannot send an email to khhjkjfakadk@phpfreaks.com. How do you imagine your script should catch that? That's why the typical way of checking the existence of an email address (as opposed to its validity) is to send an email to the user and have them follow a link with a unique, random code. If they got the code, they must have gotten the email.

 

Yeh I better not worry about it much right?  ;)

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.