Securing a PHP form and MySQL database - php

I started using php and MySQL last week so I apologise if my lack of basic understanding is frustrating.
I have a simple html form on my website that records a name, an email address and a short message.
<form action="insert.php" method="post">
Your full name:<input type="text" name="name">
Email address:<input type="text" name="email">
Short message:<textarea cols="30" rows="3" name="message"></textarea>
And insert.php looks something like this...
<?php
$con=mysqli_connect();
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$name = mysqli_real_escape_string($con, $_POST['name']);
$email = mysqli_real_escape_string($con, $_POST['email']);
$message = mysqli_real_escape_string($con, $_POST['message']);
$sql="INSERT INTO Donators (name, email, message, date)
VALUES ('$name', '$email', '$message', NOW())";
if (!mysqli_query($con,$sql)) {
die('Error: ' . mysqli_error($con));
}
header('Location: thankyou.php');
exit;
mysqli_close($con);
?>
This is then sent to a mysql database which i manage with php my admin.
My questions are...
Is the code safe for the user i.e. are there email addresses protected?
Is the code safe for me i.e. is the data being submitted protected in my database?
And finally what is the best way to stop "bots" filling in the forms i.e. is recaptcha the best thing to implement? I tried using googles recaptcha but maybe because of my css or something, it didn't appear correctly.
Thank you very much for your help!
James

1) It's never 100% secure. The code seems find but you should be aware of new patches and releases to update your MySQL instance as well as PHP and HTTP services.
2) Again... yes and no... it's your role to be sure it is. So as long as you don't display them on any webpage we can assume it's "secure"
3) The best way is to make your own module so you won't suffer from bots that are "generics". By generic I mean those that counter generic captcha module. You can for example ask a simple humain question to the user. This should be sufficient enough to avoid the biggest spamming bots.
Best regards, hope this will help.

Related

PHP chat application: Accumulation+Concatenation working, but only on individual hosts?

I have a textarea in which you type your message and a textarea that is supposed to output all messages from any hosts on my page:
<form action= "chatroom.php" method="post">
Chatbox:<textarea name="results" rows="10" cols="40">
<?php
$accum = $results . $message;
echo $accum;
?>
</textarea>
<br>
Send message:<textarea name="message" rows="7" cols="30"></textarea>
<input type="submit" value="Send" name="send">
</form>
It works fine, but only on any individual computer.
I don't believe it's a server issue (using WAMP). The site is on the web, and other echoes do appear on other clients such as
echo "<br>Connected to MySQL.<br>";
But, when I send a message on my server computer, it does not appear in the Chatbox textarea on my host computer, or vice versa.
Already tried moving the Chatbox textarea outside the form, which did not solve the problem.
Tried just echoing $accum straight onto the page and not into the textarea, which did not fix the problem.
<?php session_start(); ?>
at the start of chatroom.php didn't work either.
As a side note I'm using mySQL and used "localhost" rather than my external IP in mysqli, in my php code. I don't think this is the problem because the database does add a new user when they sign up remotely.
I thought that all PHP was executed on the server, so shouldn't $accum be picking up text from all hosts?
I'm wondering if the problem is that all clients have their own $message, $results and $accum variable because each client is connected to the server, and not to each other? So would that explain the behavior? (Or am I not completely correct?)
And forgot to mention, I am refreshing the page via clicking Send again to check if the text appeared; I haven't implemented auto-refresh yet.
edit:
in chatroom.php:
<?php
$message = $_POST["message"];
$results= $_POST["results"];
?>
I'm just turning the html name parameter into a php variable, then posting that variable right back onto the page in a different location.
Also have:
<?php
$username = $_POST["username"];
$word = $_POST["word"];
$conn = new mysqli("localhost", "user", "pw", "", 8080);
if($conn->connect_error)
echo $conn->connect_error;
else
echo "<br>Connected to MySQL.<br>";
if ($conn->query("create database chatdb")===TRUE)
echo "Created database";
else
echo $conn->error;
if ($conn->query("use chatdb")===TRUE)
echo "using chatdb";
else
echo "<br>Not using chatdb<br>";
if (
$conn->query("create table
users(
id INT(6) unsigned auto_increment primary key,
username varchar(30),
word varchar(30)
)")===TRUE)
{
echo "table created";
}
else
echo $conn->error;
if ($conn->query("insert into users (username, word) values ('$username',
'$word')")===TRUE)
echo "<br>inserted values<br>";
else
echo $conn->error;
?>
My sign-up page:
Sign Up:
<form action="chatroom.php" method="post">
Username:<input type="text" name="username"><br>
Password:<input type="text" name="word"><br>
<input type="submit" value="Sign Up!" name="signup">
</form>
Again, I don't think the mySQL has anything to do with it-- but I could be wrong.
The HTTP protocol is a Request/Response protocol. When someone displays the chat page, I assume your code reads messages from the database and shows those.
There was a request (GET /chat.php, Response is HTML for chatpage) and at that point the connection between the web browser and server is closed.
After that, there is no way for that web client to know that someone posted a new message into the database unless:
There is a persistent socket connection (websockets) OR
Your application is polling the server (perhaps using a timer and ajax calls OR
Your client application is refreshing at some interval
Solutions 1,2 and 3 require some level of javascript competency.

PHP request returns at professional hosting service returns "The requested URL / was not found on this server"

I am sending customer information from the webplatform Second Life via a weblink and php to an SQL server. My SQL server is hosted by the biggest web hosting company in Europe, so I wouldn't expect an error from their end.
I have written a very simple PHP script that translates the information for the database, something that I have done many times successfully. This time a very unexpected error occured: When I test the URL including the variables, then I get following message: The requested URL / was not found on this server ...
THis is the PHP code:
<?php
//You need to insert your specific data for "DB_HOSTNAME","DB_USERNAME","DB_PASSWORD","DB_DATABASE"
$con=mysqli_connect("DB_HOSTNAME","DB_USERNAME","DB_PASSWORD","DB_DATABASE");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
//use the parameters in the http-link to set the $ variables and real escape them for security reasons
$key = mysqli_real_escape_string($con, $_GET['key']);
$name = mysqli_real_escape_string($con, $_GET['name']);
$pass = mysqli_real_escape_string($con, $_GET['pass']);
$mailinglist = "1";
//Insert the $ variables into the table
$sql="INSERT INTO SAGS_Global_Players (key, name, pass, mailinglist) VALUES ('$key', '$name', '$pass', '$mailinglist')";
//for debugging: print out the db query on the screen
//echo $sql . '<br/>';
if (!mysqli_query($con,$sql)) { die('Error: ' . mysqli_error($con)); }
//close connection to database
mysqli_close($con); ?>
The web address "http://webaddress/8ftgde/newplayer.php&key=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX&name=estelle.pie&pass=6fcrV1vZUC&mailinglist=1"
gives the result:
Not Found
The requested URL http://webaddress/8ftgde/newplayer.php&key=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX&name=estelle.pie&pass=GpTIVOavhc was not found on this server.
If I use the same address without the variables (= http://webaddress/8ftgde/newplayer.php), then a new database entry is created. But (of course) only the auto incremented customer number and the constant variable "1" for mailinglist are entered.
Help anyone?
Please check your .htaccess file if there any change made this error
I am putting Scott's comment up as answer. If you want to do it yourself Scott, then I will mark it with best answer. I did the most stupid mistake possible: I forgot to put a question mark before my variables in the link. Shame on me!

Difficulty checking for existing user registration php/mysqli [duplicate]

This question already has answers here:
How to prevent duplicate usernames when people register?
(4 answers)
Closed 2 years ago.
I am having issues denying the registration of existing users. please see code below, and further details below that.
<?php
//connect to sql
$con=mysqli_connect("host","user","pass","db");
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
//pull user id from form input
$uid=$_POST[uid];
//make sure input is not blank
if($uid==""){echo 'Please enter a username.';exit();}
//make sure it is alphanumeric input only
if(!ctype_alnum($uid)){echo 'The username chosen must be alpha-numberic (A-z,0-9),
without special characters. Please use the back button in your
browser to try again using only alpha-numeric input.';exit();}
//check if user exists
$query=mysqli_query($con, "SELECT * FROM user WHERE uid='$uid'");
if(!$query){die 'query failed to execute';}
if(mysqli_num_rows($query)!=0){echo 'user already exists. Use back button and enter a new username'; exit();
}
//insert record into DB
$sql= "INSERT INTO user (uid, addr, password, passwordlastchanged, email)
VALUES
('$uid', '$_POST[addr]', '$_POST[pwd]','today', '$_POST[email]')";
if (!mysqli_query($con,$sql))
{
die('Error: ' . mysqli_error($con));
}
echo "1 record added";
mysqli_close($con);
?>
So when I input through my form it works fine, its just that if I try to register an already existing username, it allows it, when I have setup the query to look for an existing entry and cancel if it finds it (or so i thought...).
can you see what I am doing wrong? again I want it to check for the user name and deny further action if it finds it..
I know this is probably lacking in security, I am unfortunately learning as I go so just getting things to work comes first :) Although security suggestions will be logged and considered at the right time.
I spent an enormous amount of time researching this and I feel like I've tried a lot of various query/ function combos.
Try
if(mysqli_num_rows($query)>0)
instead of
if(mysqli_num_rows($query)!=0)

PHP/MySQL form doesn't display output

I am new to both PHP and MySQL, however I am currently learning both in order to create a basic website to administrate my RADIUS accounts. (I've switched to using MySQL for user authentication)
I have made a script that inserts user values into the database, however I need to make it so as if a user account already exists, a duplicate can't be created.
I was using a tutorial I found to try and create this, and it does work to an extent. When I add the user when there is no duplicate account, I am returned the message "Successful" however when there is a duplicate user, the page that is returned is blank, and I'm not sure what I'm doing wrong. Its workable, but annoying.
I am also curious if it is possible to create a pool of numbers (These will be IP Address's) and then have it so that when a new user is created, a number from this pool is used, and then removed from that pool. By doing this I hope I could automate assigning IPs to users without having to have someone manually add them every time a new user is created. Currently to achieve a slightly less desirable approach I made another script that displays a list of users IP address's from my table so that one can just add an IP that isn't on this list. Any advise on where I could learn to do this would be greatly appreciated, I don't know where to start.
Below is the php code that I am using, and the html code for the form. Thank you for any help or advise.
<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'test');
define('DB_USER','root');
define('DB_PASSWORD','123');
$con=mysql_connect(DB_HOST,DB_USER,DB_PASSWORD) or die("Failed to connect to MySQL: " . mysql_error());
$db=mysql_select_db(DB_NAME,$con) or die("Failed to connect to MySQL: " . mysql_error());
function AuthAccount()
{
$username = $_POST['username'];
$value = $_POST['value'];
$query = "INSERT INTO radcheck(username, attribute, op, value) VALUES ('$username', 'Cleartext-Password', ':=', '$value')";
$data = mysql_query ($query)or die(mysql_error());
if($data)
{
echo "User added to authentication database";
}
}
function AddAccount()
{
if(!empty($_POST['username']))
{
$query = mysql_query("SELECT * FROM radcheck WHERE username = '$_POST[username]'") or die(mysql_error());
if(!$row = mysql_fetch_array($query) or die(mysql_error()))
{
AuthAccount();
}
else
{
echo "Username already registered";
}
}
}
if(isset($_POST['submit']))
{
AddAccount();
}
?>
Sign-Up Page:
<!DOCTYPE HTML>
<html>
<head>
<title>Sign-Up</title>
</head>
<body id="body-color">
<div id="Sign-Up">
<fieldset style="width:50%"><legend>Registration Form</legend>
<table border="0">
<form method="POST" action="SignUp.php">
<tr>
<td>Username</td><td> <input type="text" name="username"></td>
</tr>
<tr>
<td>Password</td><td> <input type="text" name="value"></td>
</tr>
<tr>
<td><input id="button" type="submit" name="submit" value="Sign-Up"></td>
</tr>
</form>
</table>
</fieldset>
</div>
</body>
</html>
Your approach is riddled with problems:
You have introduced a race hazard, whereby two sessions simultaneously attempting to register the same username may both end up passing through the SELECT test before either one has proceeded to INSERT.
If you are using a transactional storage engine (such as Innodb), one can resolve this issue through proper use of transactions and locking reads, but it's far easier simply to impose a uniqueness constraint within the database and leave MySQL to do the rest:
ALTER TABLE radcheck ADD UNIQUE (username)
You can then simply go straight to INSERT and, if the username already exists, an error will be raised that you can handle accordingly.
You are not escaping your string literals when inlining them into your SQL. This is not only a (serious) security vulnerability, but it also introduces a bug whereby your code will break should someone post a username or password that contains a ' character. Passing literal values as parameters to prepared statements avoids these issues.
You are using an ancient PHP extension to access MySQL. It has not been updated since 2006 and its use has been explicitly discouraged in the PHP manual since 2011. As of PHP v5.5, it is deprecated and it will be removed from PHP altogether in a future version. You should switch to either the improved MySQL extension, PHP Data Objects or a third party abstraction layer.
You are storing plaintext passwords in your database. This not only poses a considerable risk to the security of your application in the event that your database is compromised, but it furthermore poses much wider risks to your users (since it is likely that they use the same passwords for other accounts). For their sake, you should always hash every password with salt; you should also be careful to protect their passwords between their browser and your server, e.g. using HTTPS.
As regards the assignment of IP addresses, your question provides no detail on how such addresses are chosen or for what they are used. Usually one leaves such matters to existing services such as DHCP, which only require configuring according to your policy requirements.
i think if(!$row = mysql_fetch_array($query) or die(mysql_error())) is not correct.
I would do it like this:
class db {
public static function dbFactory($host, $dbase, $user, $pass) {
$pdo = new PDO("mysql:host=$host;dbname=$dbase", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
return $pdo;
}
}
$db = db::dbFactory('localhost','mydbname','myusername','mypassword');
$db->beginTransaction();
try {
$stmt = $db->prepare("SELECT COUNT(*) as cnt FROM radcheck WHERE username = :uname");
$stmt->bindValue(':uname',$_POST[username]);
$stmt->execute();
$res = $stmt->fetch(PDO::FETCH_ASSOC);
if($res['cnt'] == 0) {
// go on ...
}
$db->commit();
} catch (Exception $e) {
$db->rollBack();
}

Trying to practice PHP and SQL- I am not sure if I get the logic right

I googled a short tutorial to connect a database and then I wanted to compare the username and password to then can gain access. It is kind of novice coding, but at least I can practice and hopefully pick up programming more.
check.php
<?php
//Need session//
$username = 'root';
$password = 'root';
$host = 'localhost';
$db_name = 'pcart';
$connection = mysql_connect($host, $username, $password) or die('cannot be connected');
mysql_select_db($db_name, $connection) or die ('Could not select database');
$user=$_POST['username'];
$pass=$_POST['password'];
$query = "Select username, password from tbladmins";
$user_result = mysql_query($query);
echo $user_result;
if (mysql_num_rows($user_result)==0)
{
echo "no rows to print";
}
while ($row=mysql_fetch_array($user_result, MYSQL_ASSOC))
{
$checkuser=$row["username"];
$checkpass=$row["password"];
}
mysql_free_result($user_result);
if !($user == $checkuser and $pass == $checkpass)
{
echo'where are you';
$results= mysql_query("Select * from tbladmins;") or die('error connecting to mysql');
if(mysql_num_rows($results)==0)
{
echo "no rows found, nothing to print so i am exiting";
}
print "test successful"
mysql_free_result($results);
}
else
{
echo "Wrong user or password! If you forgot, email Josephine for the username
and password";
}
?>
index.php
<html>
<head>
<title>Partner Portal
</title>
</head>
<body id="partners-page">
<div id="main">
<form method="post" action="Check.php">
<input type="text" name="username" value=""/>
<input type="password" name="password" value=""/>
<input type="submit" name="submit" value="submit">
<input type="reset" name="reset" value="reset">
</form>
forgotten password
</div>
<!--#main-->
</div><!--div partner-page-->
</html>
EDIT
The problem is that it won't show anything when I am testing if username and password match the one in database... So did I overlook anything? Why was the page blank when I tested inputting username and password?
Nice Josephine, that's a great start! Now I know you have heard of the saying, there are a million ways to skin a cat, and this is no exception. There are really a lot of ways to accomplish the above but as of late, this method of doing it have become a bit, how should I say it, un-secure.
Database connection and user management and implementing it all in a secure way is now contained in every PHP framework you can find. This is all done for you so basically what you have to do is fill in the configuration file and you are good to go.
Saying that, it's great to learn how it all ties together and you are on the right track. :D
A framework is there to re-use code that you use in every application, and being a software engineer, we re-use a lot, so you do not want to go and duplicate it all over the place.
Another thing is that the code like this, stays hidden so for instance, I, can't muck it up and I know it's tested properly.
Keep up the good work :D It gets a lot more interesting the deeper you go down the rabbit hole.
Josephine, for the base/groundwork yes. You can always add MD5 / SHA to hash your password for better security.
The only obvious reason I can see that it would fail is the typo here (locah-, instead of localh-):
$locahost = 'localhost';
It's a good start, but it looks like you've still got a long journey ahead of you.
Some of the obvious things are:
1) Why name a variable containing 'localhost' as $localhost? It would make more sense to have called this $host
2) indeed, the variables passed to mysql_connect are only ever used for mysql_connect - why did you not use literals? Why are the variable declarations and function invocation split across two separate include files?
3) Why have you not provided a specific error message? One was probably generated at some point during the execution - if you saw it then you should have provided it in your post. If you didn't see it, then spend some time thinking about why (and make sure you will see it next time).
4) Once you've got it working on your test rig, how do you think the script is going to perform when you've got a few thousand rows in tbladmin? While performing complex tasks using non-procedural code can result in code that is difficult to debug, pushing more logic down the tier almost always improves performance. Consider:
$qry = "SELECT * from tbladmins WHERE username='"
. mysql_real_escape_string($_REQUEST['username'], $connection)
. "' AND password = '"
. mysql_real_escape_string($_REQUEST['password'], $connection)
. "'";
5) using inline code within include files is a messy practice - if you restrict include files to only declaring functions, classes and constants then explicitly invoke them at the right time, then you'll save yourself a lot grief as your programs become more complex.
6) include files are not a substitute for modular programming - see also (5) above.
Good luck.

Categories