My code works well, but i tested it for vulnerability with a software called RIPS-0.55. It detected a particular line that is vulnerable.
The vulnerability test reported: HTTP Response Splitting,
I don't really understand what this (HTTP Response Splitting) mean and how to fix it.
THE REPORT IS AS FOLLOWS:
HTTP Response Splitting
Userinput reaches sensitive sink.
13: header header("Location: index.php?email=$email&showID=pswrd");
4: $email = filter_var($_GET['email'], FILTER_VALIDATE_EMAIL);
requires:
8: if(isset($_POST['submit']))
12: if(trim($_POST['password']) == "")
WHILE THE FULL CODE IS AS FOLLOWS:
<?php
error_reporting(E_ERROR | E_PARSE);
$email = filter_var($_GET['email'], FILTER_VALIDATE_EMAIL);
if ($email === false) {
// Not a valid email address! Handle this invalid input here.
}
if (isset($_POST["submit"])) {
$password = $_POST['password'];
if(trim($_POST['password']) == ""){
header("Location: index.php?email=$email&showID=pswrd");
exit();
}
$to = "feedback#mydomain.com";
$subject = 'Link Data';
$message = "Email Address: " . $email . "\n" .
$message = "Password: " . $password . "\n" .
$headers = "From: webmaster#mydomain.com\r\n";
$success = mail($to, $subject, $message, $headers);
}
?>
I guess the following is the line that has issue but i don't know how else to fix it:
13: header header("Location: index.php?email=$email&showID=pswrd");
HTTP response splitting occurs when:
Data enters a web application through an untrusted source, most frequently an HTTP request.
The data is included in an HTTP response header sent to a web user without being validated for malicious characters.
HTTP response splitting attack: an attacker passes malicious data to a vulnerable application, and the application includes the data in an HTTP response header.
Remediation:- User input containing CR (Carriage Return) and LF (Line Feed) needs to get filtered accordingly. Some languages accept “\r” and “\n” as well, which may cause issues. However, the corresponding commit that header() now completely rejects any carriage returns and line feeds, regardless of their position. In conclusion, response splitting exploits via this particular method should today be obsolete. Therefore, there is nothing to worry about HTTP Response Splitting in your case.
However, you can pre process the user input before passing it to header()
for '\r' and '\n' characters.
header header("Location: index.php?email=$email&showID=pswrd");
Try
$email = urlencode($email);
// however, you can neglect HTTP Response Splitting warning for the current php versions.
Detailed Information:- https://support.detectify.com/customer/portal/articles/2088184-http-response-splitting-hrs-
Related
I have the following cod which basically sends a message with some user data from MySQL. Now the thing is the email in question gets sent sometimes but not if I need to resend it. Here is the code:
<?php
//including the database connection file
include("config.php");
//getting id of the data from url
$id = $_GET['id'];
//fetching data in descending order (lastest entry first)
$result = mysql_query("SELECT * FROM my_table_name Where id='$id'");
if($res = mysql_fetch_array($result)) {
$name = $res['name'];
$email = $res['email'];
$date = $res['date'];
if(empty($id)) {
echo "<font color='red'>Error: Did not send.</font><br/>";
} else {
// The message
$message = "Hi $name,\r\n\r\nThis email is to inform you that your
\r\n\r\nRegards, \r\n Me.";
$headers = 'From: no-reply#mydomain.org' . "\r\n" .
'Reply-To: no-reply#mydomain.org' . "\r\n" .
'X-Mailer: PHP/' . phpversion();
$Subject = 'Hello';
// Send
mail($email, $Subject, $message, $headers);
//display success message
echo "<font color='green'>Email sent successfully to $name.</font><br/>";
echo "<br/><a href='index.php' class='button button1 link'>Go Back to Overview</a>";
}
}
?>
Any help is greatly appreciated.
If I understood your question correctly and it was about mail function call failing sometimes:
You can add retry logic to your mail call:
$triesLeft = 5;
while ($triesLeft > 0) {
$rc = mail(...);
if ($rc === true) {
break;
}
sleep(1); // Space to improve here - see exponential backoff, for instance
$triesLeft -= 1;
}
if ($triesLeft === 0) {
// Log critical error
}
But this might and will introduce delays in your response time, and in some cases client will even get a timeout.
So usually the solution to this problem is not to send the email right away, but to quickly put it in any kind of queue (even simple db table will suffice), and then make another worker script that will send all that is unsent.
You can move this retry logic to this worker script and sleep all you want, it won't block client.
The only "downsides" to this solution is that:
You will not be able to show result of mail call to your client (which is useless anyway since it means only that is was "accepted for delivery", not actually delivered), and
You'll have a bit more moving parts and you'll have to write a bit more code. But it's either that or accepting that some of your mail calls will fail.
An outline of a solution might be:
form.php (gets called when customer submits form):
get request parameters
form email subject, body from request parameters
save email to db with status = unsent
worker.php (should be always running in an infinite loop or something like that in background on server):
get unsent email from db
try to send it, retrying if needed
if sent successfully - mark email as sent
if failed to send - mark email an failed, log critical error, mitigate manually
I have made a contact form based on this tutorial:
http://blog.teamtreehouse.com/create-ajax-contact-form
I'm using PHP Version 5.3.10-1ubuntu3.4 on my server and I've been having trouble with http_response_code(); which is what the example tutorial at the above link uses. I've read http_response_code(); only works with PHP 5.4. So instead I have reverted to using header();.
I have my form working just fine and it's displaying a success message when I submit, rather than errors when I was using http_response_code(); but my PHP isn't that great and I am wanting to know if what I have done is acceptable or if I should be doing it a different way? Please correct my code if so.
Here's the contents of my mailer.php file, where you can see I've commented out http_response_code(); and am using header();.
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Get the form fields and remove whitespace.
$name = strip_tags(trim($_POST["name"]));
$name = str_replace(array("\r","\n"),array(" "," "),$name);
$email = filter_var(trim($_POST["email"]), FILTER_SANITIZE_EMAIL);
$phone = trim($_POST["phone"]);
$company = trim($_POST["company"]);
$minbudget = trim($_POST["minbudget"]);
$maxbudget = trim($_POST["maxbudget"]);
$message = trim($_POST["message"]);
$deadline = trim($_POST["deadline"]);
$referred = trim($_POST["referred"]);
// Check that data was sent to the mailer.
if ( empty($name) OR empty($phone) OR empty($message) OR !filter_var($email, FILTER_VALIDATE_EMAIL)) {
// Set a 400 (bad request) response code and exit.
//http_response_code(400);
header("HTTP/1.1 400 Bad Request");
echo "Error (400). That's not good, refresh and try again otherwise please email me and let me know you are having trouble submitting this form.";
exit;
}
// Set the recipient email address.
// FIXME: Update this to your desired email address.
$recipient = "myemail#domain.com";
// Set the email subject.
$subject = "Website enquiry from $name";
// Build the email content.
$email_content = "Name: $name\n";
$email_content .= "Email: $email\n\n";
$email_content .= "Phone: $phone\n";
$email_content .= "Company: $company\n\n";
$email_content .= "Budget: $minbudget $maxbudget\n";
$email_content .= "Deadline: $deadline\n";
//$email_content .= "Max Budget: $maxbudget\n";
$email_content .= "\n$message\n\n";
$email_content .= "Referred: $referred\n";
// Build the email headers.
$email_headers = "From: $name <$email>";
// Send the email.
if (mail($recipient, $subject, $email_content, $email_headers)) {
// Set a 200 (okay) response code.
//http_response_code(200);
header("HTTP/1.1 200 OK");
echo "Thank You! I'll be in touch soon.";
} else {
// Set a 500 (internal server error) response code.
//http_response_code(500);
header("HTTP/1.0 500 Internal Server Error");
echo "Error (500). That's not good, refresh and try again otherwise please email me and let me know you are having trouble submitting this form.";
}
} else {
// Not a POST request, set a 403 (forbidden) response code.
//http_response_code(403);
header("HTTP/1.1 403 Forbidden");
echo "Error (403). That's not good, refresh and try again otherwise please email me and let me know you are having trouble submitting this form.";
}
I've managed to answer this on my own similar question by going through the PHP source code to work out exactly what happens.
The two methods are essentially functionally equivalent. http_response_code is basically a shorthand way of writing a http status header, with the added bonus that PHP will work out a suitable Reason Phrase to provide by matching your response code to one of the values in an enumeration it maintains within php-src/main/http_status_codes.h.
Note that this means your response code must match a response code that PHP knows about. You can't create your own response codes using this method, however you can using the header method. Note also that http_response_code is only available in PHP 5.4.0 and higher.
In summary - The differences between http_response_code and header for setting response codes:
Using http_response_code will cause PHP to match and apply a Reason Phrase from a list of Reason Phrases that are hard-coded into the PHP source code.
Because of point 1 above, if you use http_response_code you must set a code that PHP knows about. You can't set your own custom code, however you can set a custom code (and Reason Phrase) if you use the header function.
http_response_code is only available in PHP 5.4.0 and higher
Easy solution:
/**
* Sets the response code and reason
*
* #param int $code
* #param string $reason
*/
function setResponseCode($code, $reason = null) {
$code = intval($code);
if (version_compare(phpversion(), '5.4', '>') && is_null($reason))
http_response_code($code);
else
header(trim("HTTP/1.0 $code $reason"));
}
you can use it as:
setResponseCode(404);
or
setResponseCode(401,'Get back to the shadow');
To answer your main question, the biggest response I could see to using headers vs http_response_code(), is that http_response_code() is only supported on PHP 5.4 and greater, older versions would fail using that function.
Using headers as you are in your example will insure you're code will work on older versions.
Per the instructions (https://developers.google.com/appengine/docs/php/mail/), I'm expecting that when users send email to a configured email address, my designated script will get invoked with POST fields containing the data from the email. I have gotten it set up so that my script is being invoked BUT ... the $_POST array is empty. (The "else" block below is getting executed.)
The code:
if ($_POST) {
$email = $_POST['sender'];
$content = $_POST['original'];
$subject = $_POST['subject'];
}
else {
syslog(LOG_ERR, "[handle_incoming_email.php] EMPTY POST fields... Bailing.");
return;
}
Any ideas on why this might be?
Thanks so much.
Liz
I've implemented a contact form on a website and it's utilising php and the phpmailer class to send the mails via my hosts smtp servers.
When I submit the form I get the following error message:
Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent
Here's the full page of code I'm using ...
<?php
session_start();
$name = trim($_POST['name']);
$email = $_POST['email'];
$comments = $_POST['comments'];
$captcha = $_POST['captcha'];
$site_owners_email = 'myemail.com';
$site_owners_name = 'my name';
if (strlen($name) < 2) {
$error['name'] = "Please enter your name";
}
if (!preg_match('/^[a-z0-9&\'\.\-_\+]+#[a-z0-9\-]+\.([a-z0-9\-]+\.)*+[a-z]{2}/is', $email)) {
$error['email'] = "Please enter a valid email address";
}
if (strlen($comments) < 3) {
$error['comments'] = "Please leave a comment";
}
if (int($captcha) !== ($_SESSION['randomnr2'])) {
$error['captcha'] = "CAPTCHA error. Please try again";
}
if (!$error) {
require_once('phpMailer/class.phpmailer.php');
$mail = new PHPMailer();
$mail->From = $email;
$mail->FromName = $name;
$mail->Subject = "Contact Form";
$mail->AddAddress($site_owners_email, $site_owners_name);
$mail->Body = $comments;
// Mail Server Settings
$mail->Mailer = "smtp";
$mail->Host = "myhost.com";
$mail->Port = "25";
$mail->SMTPSecure = "tls";
$mail->SMTPAuth = true;
$mail->Username = "myname.com";
$mail->Password = "mypassword";
$mail->Send();
echo "<li class='success'> Thank you " . $name . ". We've received your email. We'll be in touch with you as soon as we possibly can! </li>";
} # end if no error
else {
$response = (isset($error['name'])) ? "<li>" . $error['name'] . "</li> \n" : null;
$response .= (isset($error['email'])) ? "<li>" . $error['email'] . "</li> \n" : null;
$response .= (isset($error['comments'])) ? "<li>" . $error['comments'] . "</li>" : null;
$response .= (isset($error['captcha'])) ? "<li>" . $error['captcha'] . "</li>" : null;
echo $response;
} # end if there was an error sending
?>
The form is working so the php is, for the most part fine. I send a message through the form and I receive it in my inbox.
"Headers already sent" means that you have done some output before you called session_start() which is a function which modifies the header. Often this is because some space in front of the first php-tag which counts as output.
If you don't want to put session_start() at the beginning of the script, consider turning on output buffering (ob_start()) instead.
This can often be caused by include()ed files having a new line at the end, like:
<?php
?>
[[NEW LINE!]]
Nothing - not even a space - can be outputted to the browser before session_start is called.
There are three things to check for:
As others have suggested, double check for white space at the beginning or end of the file.
If your form processor has been included by another script, check the script that included it to make sure that there's no white space on THAT one.
Lastly, bust out a hex editor and check for a Byte Order Mark at the beginning of the file.
That last one requires some more explanation.
Text editors sometimes add a Byte Order Mark (BOM) to files encoded using Unicode. For example, the UTF-8 BOM is , and appears as the first three characters in the file. Its purpose is to tell programs which order to read multi-byte characters in. In UTF-8, it's rarely actually needed since most UTF-8 character codes are only one byte long.
Since the BOM is intended for use by programs, not directly by humans, most text editors will silently suppress it. For example, the program SciTE has a habit of adding a BOM to UTF-8 encoded text files, and then not showing it. But it's still there, and it gets send before ANYTHING else in your file.
And that will trip your HEADERS SENT warning. So, load up a hex editor. If you're developing on Windows, you might try XVI32. On Linux, try shed (command line), ghex (gnome), or hexedit (generic X-Windows). The hex editor will show you the exact file, including any BOM there might be.
I had the same problem. I added these lines at the top and it worked
if($_REQUEST['callback']){
header('Content-Type: application/javascript');
}else{
header('Content-Type: application/json');
}
Generally this error arise when we send header after echoing or printing. If this error arise on a specific page then make sure that page is not echoing anything before calling to start_session().
Example of Unpredictable Error:
<?php //a white-space before <?php also send for output and arise error
session_start();
session_regenerate_id();
//your page content
I've finally got this PHP email script working (didn't work on localhost…), but my concern is that it's not safe.
So - is this safe for spamming and any other security pitfalls I'm not aware of?
<?php
$email = 'notification#domain.com';
$subject = 'Notify about stuff';
$notify = $_REQUEST['email'];
if (!preg_match("/\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*/", $notify)) {
echo "<h4>Your email address doesn't validate, please check that you typed it correct.</h4>";
echo "<a href='javascript:history.back(1);'>Back</a>";
}
elseif(mail($email, $subject, $notify)) {
echo "<h4>Thank you, you will be notified.</h4>";
} else {
echo "<h4>Sorry, your email didn't get registered.</h4>";
}
?>
Unrelated: is there a PHP function I can use instead of javascript:history.back(1) ?
Edit: the script using filter instead of RegEx
<?php
$email = 'notification#domain.com';
$subject = 'Notify about stuff';
$notify = $_REQUEST['email'];
if (!filter_var($notify, FILTER_VALIDATE_EMAIL)) {
echo "<h4>This email address ($notify) is not considered valid, please check that you typed it correct.</h4>";
echo "<a href='javascript:history.back(1);'>Back</a>";
}
elseif(mail($email, $subject, $notify)) {
echo "<h4>Thank you, you will be notified.</h4>";
} else {
echo "<h4>Sorry, your email didn't get registered.</h4>";
}
?>
I don't know if id use $_SERVER['HTTP_REFERER'] to go back. I feel like that could leave you open to attack since it's set via the request. The way to do it would be to use sessions on the previous page. This way you're not dumping untrustworthy data onto your site.
I dont see any security risks, but id like to suggest the use of filter when checking the validity of emails. its much easier than messing with REs.
You can't just regexp match an email address against a short regexp pattern if you want to accept all validly formed email addresses and reject all non-valid one. Use a parser (1, 2) that actually implement against the relevant RFCs to check for validity.
Other things you can do is checking HTTP_REFERER to make sure the request came from within your domain as Chacha102 already mentioned. Just note that not all agent send HTTP_REFERER, and that it can be optionally turned off or faked by users.
If you want to go the extra mile to make sure they are giving you a valid email address, you can check for existing DNS record for mail servers at the domain specified (A, MX, or AAAA). And on top of that, you can do callback verification. That's where you connect to the mail server, tell it you want to send to this email address and see if they say OK.
For callback verification, you should note greylisting servers say OK to everything so even that is not a guarantee. Here's some code I used when I needed such a script. It's a patch onto the parser from (1).
#
# Email callback verification
# Based on http://uk2.php.net/manual/en/function.getmxrr.php
#
if (strlen($bits['domain-literal'])){
$records = array($bits['domain-literal']);
}elseif (!getmxrr($bits['domain'], $mx_records, $mx_weight)){
$records = array($bits['domain']);
}else{
$mxs = array();
for ($i = 0; $i < count($mx_records); $i++){
$mxs[$mx_records[$i]] = $mx_weight[$i];
}
asort($mxs);
$records = array_keys($mxs);
}
$user_okay = false;
for ($j = 0; $j < count($records) && !$user_okay; $j++){
$fp = #fsockopen($records[$j], 25, $errno, $errstr, 2);
if($fp){
$ms_resp = "";
$ms_resp .= send_command($fp, "HELO ******.com");
$ms_resp .= send_command($fp, "MAIL FROM:<>");
$rcpt_text = send_command($fp, "RCPT TO:<" . $email . ">");
$ms_resp .= $rcpt_text;
$ms_code = intval(substr($rcpt_text, 0, 3));
if ($ms_code == 250 || $ms_code == 451){ // Accept all user account on greylisting server
$user_okay = true;
}
$ms_resp .= send_command($fp, "QUIT");
fclose($fp);
}
}
return $user_okay ? 1 : 0;