I understand this has been discussed before but since this post in late 2010 and other discussions around that time when issues were raised - Does FILTER_VALIDATE_EMAIL make a string safe for insertion in database? - I have tried some of the situations described, such as using single quotes and the ` characters in an email form where I am using FILTER_VALIDATE_EMAIL and it has blocked them from being entered into the database.
Have recent releases of PHP fixed earlier issues and is it safe?
I'm tempted to also use mysql_real_escape_string(), presumably the two functions can be used in parallel without any conflict?
Here is the mailing list code that I am using to put addresses into the database
<?php
// connects the database access information this file
include("mailing_list_include.php");
// the following code relates to mailing list signups only
if (($_POST) && ($_POST["action"] == "unsub")) {
// trying to ubsubscribe; validate email addresses
if ($_POST["email"] == "") {
header("Location: mailing_list_remove.php");
exit;
} else {
// connect to database
doDB();
// filtering out anything that isn't an email address
if ( filter_var(($_POST["email"]), FILTER_VALIDATE_EMAIL) == TRUE) {
echo '';
} else {
echo 'Invalid Email Address';
exit;
}
// check that email is in the database
emailChecker($_POST["email"]);
// get number of results and do action
if (mysqli_num_rows($check_res) < 1) {
// free result
mysqli_free_result($check_res);
// print failure message
$display_block = "We couldn't find ".$_POST["email"].". No action has therefore been taken.";
} else {
// get value of ID from result
while ($row = mysqli_fetch_array($check_res)) {
$id = $row["id"];
}
// unsubscribe the address
$del_sql = "DELETE FROM subscribers
WHERE id = '".$id."'";
$del_res = mysqli_query($mysqli, $del_sql)
or die(mysql_error($mysqli));
$display_block = " Your email address, ".$_POST["email"].", is unsubscribed!";
}
mysqli_close($mysqli);
}
}
?>
<html>
<?php echo "$display_block";?>
</html>
The filter_var flag FILTER_VALIDATE_EMAIL will do what it says = Validate value as e-mail, meaning if its not an email it will return false.
You might be looking for FILTER_SANITIZE_EMAIL which will (Remove all characters, except letters, digits and !#$%&'*+-/=?^_`{|}~#.[] )
or
FILTER_SANITIZE_STRING will Strip tags, optionally strip or encode special characters.
Tho I don't recommend w3schools it has a list of filter_var flags http://www.w3schools.com/php/php_ref_filter.asp
Also as others have said, use PDO's prepared query's tobe safe, you can find a great pdo example here: http://www.phpro.org/tutorials/Introduction-to-PHP-PDO.html#10 which will explain a few things and there is also a simple pdo CRUD (Create Retrieve Update Delete) class here: http://www.phpro.org/classes/PDO-CRUD.html
good luck...
Related
I have a problem with either registration and login shows (undefined offset) and look at registration is there some problem with my code?it is the registration form the problem it sometimes saves the existing email in spite of the fact that I wrote a function for not submitting the existing email which is inside my data.txt. shortly the functions do not work properly
<?php
if(isset($_POST['submit_reg'])){
$var=file("data.txt");
$userData = $_POST['email'] . " " . $_POST['password'] . "\r\n";
$lines=0;
$db = fopen("data.txt", "a+");
foreach($var as $key=>$value){
$user = (explode(' ', $value));
if ($_POST["password"] === $_POST["confirm_password"]) {
//print_r($value);
if (trim($user[0]) == $_POST['email']) {
$lines++;
}
break;
}
}
if($lines){
echo "The email is already exists ";
}else{
fwrite($db,$userData."\r\n");
fclose($db);
echo "you are registered successfully ";
}
}
?>
and it is my login form the problem with login is it gives an error undefined offset 12
<?php
if (isset($_POST['submit_log'])) {
$email =isset($_POST['email']);
$password =isset($_POST['password']);
$file = explode( PHP_EOL, file_get_contents( "data.txt" ));
$auth = false;
foreach( $file as $line ) {
list($email, $password) = explode(" ", $line);
if ($_POST['email'] == $email && $_POST['password'] == $password) {
$auth =true;
break;
}
}
if($auth) {
echo "Login successfull!";
} else {
echo "Invalid username or password";
}
}
?>
Let me say first off, storing plaintext passwords in a .txt file is probably not the best way of building a longin system. (that's the disclaimer anyway).
Undefined offset (just a guess)
That said I see a few places to improve your code. My guess without more specifics about the error, is you may be pulling a empty array at the end of the file, it's typical to leave a hanging line return at the end (a new line with nothing else for the last line). Which may turn into something like this once you explode it for the second time on the space ['']. And then you try to access it using list which gives you undefined offsets.
You could use array_filter and maybe trim but instead of doing this:
$file = explode( PHP_EOL, file_get_contents( "data.txt" ));
You could try (which you should know as you use this function already)
$file = file( "data.txt", FILE_SKIP_EMPTY_LINES|FILE_IGNORE_NEW_LINES ));
The file function, takes a file and breaks it into an array based on the line returns. So this takes the place of both explode and file_get_contents.
Then it has 2 (bitwise) flags which you could make use of:
array file ( string $filename [, int $flags = 0 [, resource $context ]] )
Reads an entire file into an array.
FILE_IGNORE_NEW_LINES
Omit newline at the end of each array element
FILE_SKIP_EMPTY_LINES
Skip empty lines
http://php.net/manual/en/function.file.php
These take the place of filtering the data for empty lines (something you weren't doing). Granted this is a file you created but you never know when a errant line return could creep in there.
Non-unique entries
if(isset($_POST['submit_reg'])){
$var=file("data.txt");
$userData = $_POST['email'] . " " . $_POST['password'] . "\r\n";
$lines=0;
$db = fopen("data.txt", "a+");
foreach($var as $key=>$value){
$user = (explode(' ', $value));
if ($_POST["password"] === $_POST["confirm_password"]) {
//NOTE: the uniqueness check only happens when the confirm password matches
if (trim($user[0]) == $_POST['email']) {
$lines++;
}
break;
}
}
if($lines){
echo "The email is already exists ";
}else{
//NOTE:yet you save it no matter if that is the case
fwrite($db,$userData."\r\n");
fclose($db);
echo "you are registered successfully ";
}
}
Your uniqueness check only works when the confirm password matches the password, however when it comes time to save the data, there is no check. Instead of just adding that check in around the saving bit, it would be better to wrap the whole thing inside this confirm test, as both pieces of that are known before touching the file:
Here I reworked this a bit for you
if(isset($_POST['submit_reg'])){
if ($_POST["password"] === $_POST["confirm_password"]) {
//VERIFY AND SANITIZE user input, if you put junk in you get junk out
$password = trim($_POST['password']);
//again use something better then die
if(empty($password))die('Password cannot be empty');
//because you split on space, you cannot allow it in inputs
if(preg_match('/\s+/', $password)) die('Password cannot contain spaces');
$email = trim($_POST['email']);
if(empty($email))die('Email cannot be empty');
//you may want to validate using something better
if(preg_match('/\s+/', $email )) die('Email cannot contain spaces');
//Use the flags
$var=file("data.txt", FILE_SKIP_EMPTY_LINES|FILE_IGNORE_NEW_LINES);
//for duplication we only care if there is 1 previous entry
//which is enough to say its a duplicate
$exists=false;
foreach($var as $key=>$value){
$user = explode(' ', $value);
if (trim($user[0]) == $email) {
//we found a match this is enough to call it a duplicate
$exists = true;
break;
}
}
if($exists){
echo "The email is already exists ";
}else{
file_put_contants("data.txt", $email." ".$password.PHP_EOL, FILE_APPEND);
echo "you are registered successfully ";
}
}else{
echo "Confirm password must match password";
}
}
Other stuff
These are also incorrect:
$email =isset($_POST['email']);
$password =isset($_POST['password']);
Isset returns a boolean value, so you are assigning true or false to those two variables. This doesn't matter as you never check them and in your loop you overwrite with the call to list(). But just because someting "doesn't matter" doesn't mean it's correct.
These really should be something like this:
if(!isset($_POST['email']))
die("no email"); //dont use die but do some kind of error message
if(isset($_POST['password']))
die("no password"); //dont use die but do some kind of error message
SUMMERY
Really it's quite a mess. What I mean by this is you used 3 different ways to open and access the file data. You used the PHP line constant in some places but not all. You had code that was somewhat haphazardly thrown around, where you were setting things long before you need them, and in some cases you may not have needed them, so you were wasting resources setting them.
Please don't take the criticism hard, as I am not trying to offend. Simply pointing out places you could improve the flow of the code and simplify things. The big thing is don't get discouraged, in order to program effectively you have to have a no-quite attitude and the drive for continuous self improvement. Even after 9 years of PHP programing I still learn new things all the time, I learned (and wrote a library around it) something new just 2 days ago...
As I said at the beginning and to be honest a database would actually reduce the amount of code you need. It might be intimidating at first to use a database but you'll find that it's easier then doing this. An example is your check for uniqueness, you can set a field to be unique in the Database then you never need to worry about duplicates, only catching the errors for them.
I would suggest looking into PDO and prepared statements, password_hash and password_verify.
A final word of warning is I didn't test any of this so forgive me if there are any typos...
Hope it helps.
i have a login form that transmit the information to the php code in the same page but even the username and password are correct it always ignore him this is my php code:
$mail = test_input($_POST["email"]);
//check if the email address format is valid
if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
echo "<h3>Invalid email format</h3>";
}
$pass = trim($_POST["password"]);
$stmtn="SELECT first_name, email , password FROM `users` WHERE email= '$mail'";
$result = mysqli_query($connection,$stmtn);
$rows= mysqli_fetch_assoc($result);
if($rows && $rows['password']== md5($pass)){
session_start();
$_SESSION['username']=$rows['first_name'];
header("Location: index.php");
}else{
echo "<h3> Sorry! your email or password is incorrect </h3>";
echo "<h3> Please <strong><a href='login.php'>login</a></strong> again to your account </h3>";
echo var_dump($rows);
}
the statements in the else part always are executed and the result of the var_dump for the array $rows is equal to the information in the db exactly
and this is the code of the function test_input
<?php
function test_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
Please stop using stripslashes, it's really not needed. Instead you should properly clean your user input using a filter_var or a regex.
After you've set the header() to redirect you should always and immediately set an exit statement to cease the script execution.
You really, REALLY should use parameterised queries with MySQLi to stop your SQL being abused and attacked (take a peak at this question just for a flavour of the layout of parameterised MySQLi)
Stop using md5. Now. if your PHP is above 5.3 you really, really should be using password_hash and password_verify for your and your users security. Good habits start at home.
Also don't trim user passwords. Don't make any changes to a password variable at all (until after they've been password_verifyed), because if I want to have " dickens !" as my password, why the hell shouldn't I?
With the above code changes in place there is no need for the test_input function at all on your script.
And there is no need to test_input on the email address because you validate it straight afterwards with filter_var. Inefficient.
Good luck.
I have a login for my site. Below shows the registration page. The emailaddress is their username. How do create an error message alert if an # symbol and . has not been inserted into the username(emailaddress) field?
<?php
// Check if he wants to register:
if (!empty($_POST[emailaddress]))
{
// Check if passwords match.
if ($_POST[password] != $_POST[password2])
exit("Error - Passwords don't match. Please go back and try again.");
// Assign some variables.
$date = mktime("d - m - Y");
$ip = $_SERVER[REMOTE_ADDR];
require_once("config.php");
// Register him.
$query = mysql_query("INSERT INTO neworders
(emailaddress, firstname, surname, password, datereg, ip)
VALUES ('$_POST[emailaddress]','$_POST[firstname]','$_POST[surname]','$_POST[password]','$datereg','$ip')")
or die ("Error - Couldn't register user.");
echo "Welcome $_POST[username]! You've been successfully reigstered!<br /><br />
Please login <a href='login.php'><b>here</b></a>.";
exit();
}
?>
You should probably use a more robust solution to validate emails. Use PHP's filter_var() function with the FILTER_VALIDATE_EMAIL flag.
$validEmail = filter_var($email, FILTER_VALIDATE_EMAIL);
Of course, this is just for validating the email. If you're inserting it into a database, use that database's escape mechanism for strings or use bound queries.
Use the function at the bottom of this page:
http://www.linuxjournal.com/article/9585
You can check if a string has characters in it with the stristr() php function (This is not the ideal solution, but just very simply checks if characters in a string exist like you described. Using filter_var as described below is a better solution).
So to do what you are asking you could do something like:
if(!(stristr($_POST['emailaddress'], '#') && stristr($_POST['emailaddress'], '.')) {
echo 'Email not valid';
exit(1);
}
Something else you could do is use the prebuilt php filter_var function to do this: http://php.net/manual/en/function.filter-var.php
if(!filter_var($_POST['emailaddress'],FILTER_VALIDATE_EMAIL)) {
echo 'Email not valid';
exit(1);
}
You can also use regex pattern matching if you want. I would also advise to do some sort of cleaning (mysql_real_string_escape) on the $_POST['emailaddress'] field, if you are inserting it into a database.
Although the item is successfully added to the database, I'm not sure that I'm executing the mysql_real_escape_string() function correctly and, thus, getting the error. Any help is appreciated.
Success!
Warning: array_map() [function.array-map]: Argument #2 should be an array in /home/site4/public_html/lab/mailing_list_dev_1-0/mailing_list_add.php on line 32
Thanks for signing up!
Here's the code in question...
<?php
// connects the database access information this file
include("mailing_list_include.php");
// the following code relates to mailing list signups only
if (($_POST) && ($_POST["action"] == "sub")) {
if ($_POST["email"] == "") {
header("Location: mailing_list_add.php");
exit;
} else {
// connect to database
doDB();
// filtering out anything that isn't an email address
if ( filter_var(($_POST["email"]), FILTER_VALIDATE_EMAIL) == TRUE) {
echo 'Success!';
} else {
echo 'Invalid Email Address';
exit;
}
// check that the email is in the database
emailChecker($_POST["email"]);
// get number of results and do action
if (mysqli_num_rows($check_res) < 1) {
// free result
mysqli_free_result($check_res);
// cleans all input variables at once
$email = array_map("mysqli_real_escape_string", ($_POST["email"]));
// add record
$add_sql = "INSERT INTO subscribers (email)
VALUES('".$_POST["email"]."')";
$add_res = mysqli_query($mysqli, $add_sql)
or die(mysqli_error($mysqli));
$display_block = "<p>Thanks for signing up!</p>";
// close connection to mysql
mysqli_close($mysqli);
} else {
// print failure message
$display_block = "You're email address, ".$_POST["email"].", is already subscribed.";
}
}
}
?>
<html>
<?php echo "$display_block";?>
</html>
You're treating $_POST['email'] as an array, which it probably ins't.
If you only intended to escape email, do
$email = mysqli_real_escape_string($dbConn, $_POST['email']);
Then in your INSERT statement, use the escaped $email instead of $_POST['email']
$add_sql = "INSERT INTO subscribers (email) VALUES('$email')";
array_map() is meant for arrays. If all you have is a single value then just call the function directly.
There is at least one bug, here:
// Does not work because $_POST["email"] is a string, not an array
$email = array_map("mysqli_real_escape_string", ($_POST["email"]));
This looks like something you adapted from code that was working, but right now it's broken. You probably wanted something like this:
$post = array_map("mysqli_real_escape_string", $_POST["email"]);
after which you can use $post["email"] safely, as it has been escaped.
Of course escaping everything inside $_POST is possibly not the best way to go about this. There's still the mundane but spot-on way to consider:
$email = mysqli_real_escape_string($_POST['email']);
This is apparently not mysqli_real_escape_string problem but array_map() problem. Or rather misuse of the latter one.
However, you will face mysqli_real_escape_string() problem as soon as you solves this one.
To solve this latter your doDB() function have to return connection id, which you have to use with every mysqli_* function.
$conn = doDB();
$email = mysqli_real_escape_string($conn,$_POST["email"]);
thus you will have all your [listed] problems solved but I believe that emailChecker will may cause the same kind of problem of inexistent $check_res variable. Instea d of which such a function apparently have to return just a boolean and used like
if (!emailChecker($_POST["email"])) {
I was wondering how do I allow only one email address? Also how can I only check for the # sign in the email address to validate the email?
Here is my PHP code.
if (isset($_GET['email']) && strlen($_GET['email']) <= 255) {
$email = mysqli_real_escape_string($mysqli, strip_tags($_GET['email']));
} else if($_GET['email'] && strlen($_GET['email']) >= 256) {
echo '<p>Your email cannot exceed 255 characters!</p>';
}
Don't.
Use a completely RFC-compliant validator instead, followed up with an actual mail to the address. Truly, sending a mail to the address is the only real way to make sure it's a legitimate email address.
PHP has filter_var which can be used like this:
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
if (strpos($email, "#") === true) {
// VALID
}
}
This is a simple way to check if common address are valid (and will not allow obvious fakes) however, this doesn't make sure your email address is valid according to the RFC 822, RFC 2822, or RFC 3696.
I would also like to point this out. That will validate an email address according to the proper RFCs.
If this is a form, you can use input type="email" in your form. It is part of HTML5, so it isn't implemented in all browsers yet.
This won't serve the full purpose, but it will prevent a single page load for obvious mistakes (forgetting # or .com) to help a little. Browsers which implement it prevent you from submitting the form if it's invalid; also, Apple devices will utilize a special keyboard for that entry with "#" and ".com" present.
(Just an extra piece of info, since I don't know your whole situation.)
how do I allow only one email address?
Run SELECT query to see if there is such an email already.
how can I only check for the # sign in the email
strpos would be enough.
Though it would be a good idea to confirm email address by sending a letter to that address, you know.
Also you have a few things to correct in your code.
your else if statement is not necessary, there should be just else
and mysqli_real_escape_string shouldn't be in the validation section. It is database related function, not validation one.
And if it's registration form, it should use POST method
so, smth like this
$err = array();
if (empty($_POST['email']) $err['email'] = "email cannot be empty";
if (strlen($_POST['email']) >= 256) $err['email'] = "email is too long";
if (!strpos("#",$_POST['email'])) $err['email'] = "malformed email";
$query = "SELECT 1 FROM members WHERE email ='".
mysqli_real_escape_string($mysqli, $_POST['email'])."'";
$res = mysqli_query($mysqli, $query) or trigger_error(mysqli_error($mysqli).$query);
if (mysqli_num_rows($res)) $err['email']="email already present";
//other validations as well
if (!$err) {
//escape all the data.
//run your insert query.
header("Location: ".$_SERVER['REQUEST_URI']);
exit;
} else {
foreach($_POST as $key => $value) {
$_FORM[$key]=htmlspecialchars($value,ENT_QUOTES);
}
include 'form.php';
}
try using regex expression for it... you can find patterns in google
on eg:
if (!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $email)){
echo "<center>Invalid email</center>";
}else{
echo "<center>Valid Email</center>";}
}
Edited for preg_match:
if (!preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i", $email)){
echo "<center>Invalid email</center>";
}else{
echo "<center>Valid Email</center>";
}