How to validate PHP form data with htmlspecialchars() [closed] - php

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last month.
Improve this question
I have data coming from html <form>. That did basic HTML5 validation for users convenience, now I need to do server side check. I have this so far that came from W3.
Now that I run the vars through the validator, how do I apply that validated data? Or is it already validated and I am over thinking it? But I would still like to use htmlspecialchars()...
<?php
$fname = $lname = $email = $message = "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$fname = test_input($_POST["fname"]);
$lname = test_input($_POST["lname"]);
$email = test_input($_POST["email"]);
$message = test_input($_POST["message"]);
}
function test_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
// email body stuff here
// send email
mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
?>
Or is this better? But not sure how to include htmlspecialchars()
$EmailFrom = Trim(stripslashes($_POST['email']));
$EmailTo = "xxx#xxx.com";
$Subject = "Contact Form";
$fname = Trim(stripslashes($_POST['fname']));
$lname = Trim(stripslashes($_POST['lname']));
$email = Trim(stripslashes($_POST['email']));
$message = Trim(stripslashes($_POST['message']));
// email body stuff here
// send email
mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
?>

I have this so far that came from W3.
I assume you mean W3Schools (not to be confused with the W3C who have the domain w3.org): They are extremely low quality. They are often wrong, and even where they are right they frequently leave important things out of their explanations.
Now that I run the vars through the validator, how do I apply that validated data?
test_input returns the data. You then assign it to variables. Just use those variables instead of the original data stored in $_POST.
That said … the test_input function is entirely unsuitable for the context and you should not use it.
Any escaping and sanitization must be tailored based on what you do with the data.
In this case…
mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
… you are sending a plain text email.
$data = trim($data);
You might want to remove extra spaces from the start and end of strings that the user has typed by accident.
This shouldn't be needed and doesn't help with security, but it does no harm and can make the results tidier.
$data = stripslashes($data);
This is used to remove escaping. The data you are getting should not be escaped in the first place.
This used to be needed when PHP had a misguided and unreliable auto-escaping feature but that was removed from PHP years ago and, even when it was there, code should test to see if the feature is turned on or not.
Since there isn't any escaping to remove, this can remove real data.
Don't do that.
$data = htmlspecialchars($data);
You are sending a plain text email. Not an HTML email. The input isn't going anywhere near HTML.
Escaping it for HTML will just risk making the reader of the email see HTML entities instead of the characters you want them to see.
Don't do that.

Four related issues here: trimming data, escaping characters, data validation, and data sanitization.
Trimming the input is good, because the input may have some unnecessary characters such as space or figures. For example, if the input is $str = " 1.8 Some input" and you only want to store "Some input" then you could use $str = ltrim($str, ' .0123456789'); (with a space at the beginning of the second parameter).
It is common to pass posted data through mysqli_real_escape_string() which helps create a legal sql statement. This would, among other things, escape quotes and allow them to be entered smoothly into the dataset. For details see: http://php.net/manual/en/mysqli.real-escape-string.php.
Using stripslashes could remove splashes that were used to escape some characters. For example, if $name = "O\'reilly"; using stripslashes($name) gives the $name as O'reilly which can disrupt the logic of your sql statement because of the unescaped quote. So then you would not use stripslashes after using mysqli_real_escape_string.
It is always important to validate data on the server side. But use of htmlspecialchars() will not remove anything. Whatever is encoded with it will be as it was when the data is decoded with htmlspecialchars_decode(), after reading it from the database where it was stored.
PHP Filters should be used for both validation and sanitation of data sent to the server side. For example, you could sanitize and check a posted email as follows:
// Sample email address, possibly got from $email = $_POST['email']
$email = "someone##example.com";
// Remove all illegal characters from email
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
// Validate e-mail address
if(filter_var($email, FILTER_VALIDATE_EMAIL)){
echo "The <b>$email</b> is a valid email address";
} else{
echo "The <b>$email</b> is not a valid email address";
}
// gives: The **someone##example.com** is not a valid email address
For a gentle introduction to PHP filters, see for example, https://www.tutorialrepublic.com/php-tutorial/php-filters.php from where I got the above example. For more details see http://php.net/manual/en/book.filter.php.

Related

Regex validate items with delimiter

I am using this regex for email validation in php (based on here)
^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*#[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$;
My question is how can I validate input that is a list of emails separated by a delimiter.
Let's say delimiter is "," or ";" or ", " or "; ".
I suppose i should add something like that
(\s*(;|,)\s*|\s*$)
but it doesn't work...
Validating an email for real is better done by a module than a short regex. See http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html
But fortunately, php have a validator :
<?php
$email_a = 'joe#example.com';
$email_b = 'bogus';
if (filter_var($email_a, FILTER_VALIDATE_EMAIL)) {
echo "This (email_a) email address is considered valid.";
}
if (filter_var($email_b, FILTER_VALIDATE_EMAIL)) {
echo "This (email_b) email address is considered valid.";
}
?>
See http://php.net/manual/en/filter.examples.validation.php
Dont use regex to validate emails, PHP has a function for this filter_var():
$email = 'joe#example.com';
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
//valid
}else{
//not
}
You can adapt this code and use explode(',',$email) to validate multiple emails.
At the risk of giving you an answer you can't use if you're only accepting a pure regex solution, for a few reasons I would recommend using explode with your delimiter and proceed to iterate over the array and validate each email in turn:
Your regex and the code handling it will be simplified.
Your regex will only have to handle the general use case of emails and can be a general re-usable operation any time you need to validate an email address. It will also be simple to swap the regexp operation out for a call to a library meant for email validation, or any other custom validator.
It will be easier to handle possible related necessities, like indicating in your output which email failed to validate, or accepting all addresses which validated and discarding those that didn't.

php mail() remove CC, BCC and security

I'm new to stackoverflow and couldn't find an answer to my question which is; How do I secure my mail() code in php to prevent people from adding bcc which would ultimatly result in mass mailing? My website uses the PHP mail() service to email me when a new comment has been entered in my site. What is the best way to prevent people tampering with it, such as removing the bcc? What I have so far is:
function mres($input){
if (get_magic_quotes_gpc()){
$input = stripslashes($input);
}
return mysql_real_escape_string($input);
}
$name = strip_tags(mres($_POST['name']));
$comment = strip_tags(mres($_POST['comment']));
$to = 'myself#gmail.com';
$subject = 'Website - comment';
$body = 'A new comment has been entered on the website.'."\n\n"."$name".' said: '."\n\n"."$comment";
mail($to,$subject,$body);
Assuming that this code is followed by:
mail($to, $subject, $body);
Then it's safe, if overkill -- the only arguments to mail() which are vulnerable to injection are the ones that control header fields ($to, $subject, and $additional_headers). strip_tags and mysql_real_escape_string are both unnecessary, and the latter will make apostrophes show up as \' in your email.
If there's no following call to mail(), then it's trivially safe, because it doesn't do anything. :)
I would look at something like http://mailgun.com/
As it is, your mail will often end up in people's spam if you just use mail()

Injection to the mail command when only $text is user input

Is there any security risk of injection in the following PHP script?
<?php
$empfaenger_1 = "test#example.com";
$sender = "test#example.com";
$name = $_POST['name'];
$telefon = $_POST['phone'];
$betreff = "Test";
$text =
"Please contact me
Name: $name
Telefon: $telefon";
mail($empfaenger_1, $betreff, $text,"from:$sender");
$url = htmlspecialchars($_SERVER['HTTP_REFERER']);
echo "<center><br><br>Thank you<br><br>";
echo "<center><a href='$url'>Back</a>";
I think mail injections are only possible in header fields. Mail injections into body text are not know to me.
Anyway look out for XSS, you should use strip_tags():
$name = strip_tags($_POST['name']);
$telefon = strip_tags($_POST['phone']);
Injection in mail
Here the risk appears minimal. A couple of answers here point to the possibility of HTML injection into the email. For HTML emails, this is a possibility, but HTML mail messages will have the Content-type header set to text/html or as a part of a multipart message. RFC 1521 stipulates that a HTML content-type must be set explicitly, and that if no content type is specified that plain text is default:
Default RFC 822 messages are typed by this protocol as plain text in the US-ASCII character set, which can be explicitly specified as "Content-type: text/plain; charset=us-ascii". If no Content-Type is specified, this default is assumed.
In the above code, the user-provided text is inserted after the headers; an attacker would have no opportunity to change the content type (either to HTML or to multipart, the latter allowing injection of a MIME boundary).
The end result cannot be anything but a plain text message. If a user injects HTML tags into the message, the person reading the email would see those HTML tags in the message. Email clients generally don't opportunistically examine plaintext messages to locate and parse embedded HTML and JavaScript.
Injection elsewhere
While the use of mail is probably safe, there is a potential injection vulnerability in the remaining code:
$url = htmlspecialchars($_SERVER['HTTP_REFERER']);
echo "<center><a href='$url'>Back</a>";
By default, htmlspecialchars uses the ENT_COMPAT | ENT_HTML401 flags, which does not convert single quotes to '. The link href attribute is delimited with single quotes. So if an attacker can coerce the HTTP referrer to include a single quote, he/she can invoke a routine XSS attack. (for instance, if referrer is coerced into the equivalent of http://whatever/a' onclick='alert(), clicking the link can invoke arbitrary JavaScript. To resolve this, either place "$url" on the second line in double quotes, or call htmlspecialchars with the ENT_QUOTES flag.
There is possibility yes, one can inject javascript code for example if someone put this as their name:
<script type="text/javascript">
window.location = "http://www.google.com/"
</script>
Anyone viewing that name would be redirected to google.com; You can prevent this by saying:
$name = htmlentities(strip_tags($_POST['name']));
If the client that opens the email in html view mode, and the user injects script, then yes, it's vulnerable to XSS, and by extension, CSRF. You should, of course, sanitize all untrusted input.
More specific XSS protection information can be found at the OWASP web site.

Complete protection against mail-injection

Suppose we're sending trivial feedback and going to make these fields dynamic:
sender name
sender e-mail
subject
message body
would be this PHP code enough to protect us from all kinds of mail-injections?
//sanitizing email address
if ($email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)){
//encoding subj according to RFC and thus protecting it from all kinds of injections
$subject = "=?UTF-8?B?".base64_encode($_POST['subject'])."?=";
//encoding name for same reasons, and using sanitized email
$from = "From: =?UTF-8?B?".base64_encode($_POST['name'])."?= <$email>\r\n";
//protecting body as it mentioned in http://php.net/mail
$message = str_replace("\n.", "\n .", $_POST['text']);
mail('me#example.com',$subject,$message,$from);
}
at the moment I am playing with names like "some#email.com, other#email.com," but it seems that all available mail clients handling it correctly
would be this PHP code enough to protect us from all kinds of mail-injections?
It looks pretty comprehensive, just as long as your email client supports the RFC 2047 encoding method you're using in the headers. (Some webmail clients don't recognize the encoding.)
My only recommendation, other than not using mail() to begin with, would be considering is_email rather than the built-in filter. The built-in fails a number of edge cases.
It depends, if the filter complies with rfc that specify that the local part cant contain anything if it is surrounded by " or whatever character some address like "foo\r\nTo: poor-guy#dom.tld\r\nTo: dummy"#foo.tld will give you headers like :
Subject: foo
To: poor-guy#dom.tld
To: dummy"#foo.tld
quite bad ...

Sanitize contact form without mysql_real_escape_string

I normally use this function to sanitize my form inputs before storing them into my database:
//Function to sanitize values received from the form. Prevents SQL injection
function clean($str) {
$str = #trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return mysql_real_escape_string($str);
}
Until today I didn't realize that mysql_real_escape_string required a database connection as I've only used it when I've been cleaning the data before storing it into the database.
I tried using the function on a contact form and got the "A link to the server could not be established" error. I could connect to the database but there is no need because I simply am trying to sanitize the data before it's being sent out to my e-mail via the contact form.
What is the best way to sanitize data that's not being stored in a mysql database and does this data still need to be sanitized?
use filter_var()
http://php.net/manual/en/function.filter-var.php
like if you want to sanitize an email:
$_POST['email'] = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
to message
$_POST['message'] = filter_var($_POST['message'], FILTER_SANITIZE_STRING);
is enogth
The purpose of sanitizing the data with mysql_real_escape_string is to avoid SQL injection. If you're not using SQL, you're already immune.
Men don't get cervical cancer.
Use a sanitization function appropriate to the special characters you need to avoid. Ideally, don't strip something which won't cause harm.
The whole concept is wrong.
This function doesn't help not for email not even for database.
mysql_real_escape_string do not "sanitize" anything. It is merely escape delimiters and nothing else. Just to prevent syntax errors if you have a delimiter in your data:
SELECT * FROM table WHERE name = 'it's me' # error!
after data being escaped, your data become 'it\'s me' and there is no error.
Therefore, this function works only with SQL query and for data, enclosed in quotes only.
Thus, there is no sense in doing just mysql_real_escape_string without having quotes around. mysql_real_escape_string should be used
a) alone. stuff like trim or stripslashes has nothing to do here
b) right before query string composing and not elsewhere
c) only with data that going to be enclosed in quotes.
d) all other data need another ways of sanitization
As for the email, you don't need any sanitization it you send it as plain text.
The only precaution you have to take is against mail injection
Not a big deal though. Just put user input into message body only. not into subject, to or from or any other header. Message body only. And you are safe
(I'm new to stackoverflow so I'm going about this the wrong way/doing a bad job with the styling of my answer feel free to let me know.)
Correct me if I'm wrong as I'm also dealing with the same issue right now, but I don't think that the accepted answer using filter_var is enough as attackers could bypass this using unicode.
Example: "& #66;& #99;& #99;& #58;" (spaces added so stackoverflow will display it correctly)
This wouldn't get removed from the string, and would later be replaced with "Bcc:".
This is my solution, but there may be a better way. If anyone knows of one I'd love to hear it.
$string = str_replace("&", "(and)", $string);
$string = str_replace("#", "(num)", $string);
$string = str_replace(";", "(semi-colon)", $string);
$string = str_replace(":", "(colon)", $string);
$string = str_replace("#", "(at)", $string);
$string = str_replace("\\", "(backslash)", $string);
$string = filter_var($string, FILTER_SANITIZE_STRING);

Categories