I am using WYSIWYG Webbuilder 8 to construct a website. Part of the website will be restricted access to registered users only. To this end I have created a MySQL database. I also have a sign-up form. When a new user wishes to sign-up I would like to have the username automatically checked against the database to make sure it doesn't already exist. I intend doing this using an AJAX function as the WYSIWYG software has this option built in. What I need to build myself and this is where I'm struggling is the validate.php that the AJAX command will go to.
I have something like this at present (please excuse my ignorance!):
<?php
$username = $_POST['data'];
// TODO: lookup username in database...
if ($username == 'user')
{
echo "true";
}
else
{
echo "false";
}
?>
I have no real idea if this is adequate or secure. I have been reading some scary stuff about sql injection and other black arts involving the use of forms and I'd like to avoid pitfalls if possible.
Would some kind soul please have a look at my request and help me out? I'm not a programmer by any stretch of the imagination and I'm way out of my depth here.
Thanks in advance for your help
You want to use something that will handle the chatter between your application and the database for you. One of the best tools available for this today is the PDO library, specifically PDO-MySQL for your usage. It will handle escaping and SQL injection issues for you by using parameterized (prepared) statements
Here's an example of connecting to a database and issuing a query in MySQL
$db = new PDO('mysql:host=localhost;dbname=dbname;charset=UTF-8', 'username', 'password');
$statement = $db->prepare('SELECT user_id FROM users WHERE username = :username LIMIT 1');
$statement->bindValue(':username', $_POST['data']);
$statement->execute();
if (false == $userId = $statement->fetchColumn()) {
// No matching username was found in the database
} else {
// A matching username was found in the database
// $userId contains the matching user ID
}
Knowing how to pass this back to your JS/AJAX integration could be dependent on what framework (if any) you are using and what format you would like that data in
Related
I have a login form and I'm confused why my SQL Injection parameters doesn't work in here. I don't have any function or method for preventing the SQL Injection.
I made this login form for the testing of SQL injection and it's written in PHP.
Here is my code.
<?php
include("myconnection.php");
$error="";
if(isset($_POST["submit"]))
{
if($_POST["username"] == '' || $_POST["password"]== '')
{
$error='Please fill the blanks!';
}else
{
$username=$_POST['username'];
$password=$_POST['password'];
$sql="SELECT * FROM users WHERE username='$username' AND password='$password'";
$result=mysqli_query($db,$sql);
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);
if(mysqli_num_rows($result)==1)
{
$login_user=$_POST["$username"];
header("location: myhome.php");
$error="Connected";
}
else
{
//$error="Incorrect Username/Password";
$message="Incorrect Credentials";
echo "<script='text/javascript'>$message</script>";
}
}
}
else
{
}
?>
I tried admin'OR'1'='1 in both username and password fields and any other possible basic injections but it doesn't work. I tried using the basic sql injection in most of working sites and it works, I'm just confused my my code doesnt accept sql injections.
And it gives me the same echo when you have an incorrect credentials.
I hope this is done for academic purposes as I have no idea why you would ever want to have this in any production websites. That being said it is probably because of the AND needing to also be true for the query to return any results. Where as if you had submitted admin'OR'1'='1 in the username field your query would look like
SELECT * FROM users WHERE username='admin'OR'1'='1' AND password='123'
That reads to me as WHERE username equals admin OR 1 equals 1 AND password equals 123. You would probably need to figure out how to also bypass that check as it will try to match password field still and vice versa the username field.
Seems odd to say but if you wanted to inject something maybe this would work in the username field injection' OR 1 LIMII 1# Which would make something like this
SELECT * FROM users WHERE username = 'injection' OR 1 LIMIT 1#' AND password = 'pass'
Essentially you are already injecting SQL, you are just not doing it in such a way that is yielding the results you want. Try echoing the query and running it directly in the mySQL CLI to see what the result set is and if it is a valid query. Maybe play around with the query there to try and obtain your intended injection.
After successfully verifying username exists in the database, I need to verify whether the password matches as well. Therefore, I need to retrieve the hashed password from the database, right? Or do I have other choices?
I'm using PHP 5.5 API bcrypt for hashing. My code would give me a 500 internal server error when I get the password from database. How do I do this correctly?
Here's my php code:
// If found username, check password, else respond invalid username
if($countRows!==0){
// Get hash from database
// Prepare statement
$stmt = $conn->prepare('SELECT password FROM users WHERE username = ?');
// Bind
$stmt->bind_param('s', $ps_username);
// Set Parameters
$ps_username = $username;
// Execute
$hash = $stmt->execute();
// Check password
if (!password_verify($password, $hash)) {
if($error != ""){
$error .= '<br>';
}
$error .= 'The user or password you entered do not match, please try again.';
}
else {
echo 'OK';
// Session start
// Redirect user to profile/homepage
}
}
And can someone recommend something that I can learn SQL commands? I can't find a good place for that.
execute() does not return any column data. It returns a boolean (true/false). This is where your code block first fails:
$hash = $stmt->execute();
You can view examples on how to fetch data from the result set here: http://php.net/manual/en/mysqli-stmt.fetch.php
An example being:
$stmt->execute();
$stmt->bind_result($hash);
$stmt->fetch();
In response to:
And can someone recommend something that I can learn SQL commands?
This is pretty much off topic for Stackoverflow but the PHP manual for mysqli can show you how to use the mysqli API fairly well with plenty of examples. If you want to learn the Structured Query Language itself, then there are plenty of external resources for that, including MySQL's documentation.
I have the following code which is supposed to insert a row into a DB table "clicks" (consisting 1 Primary AI column "id" and another column "user" which contains the user's sessions id) upon clicking the Like button. For each user assuming they have a session id set from a login I would like to return to them their most recently inserted id from the table. So the first time the button is clicked it will return 1 etc.
I would like this to be accessible to multiple users through a login system. I was wondering if there are any major security vulnerabilities with my code e.g can the results be forged etc?
index.php:
<?php
include 'init.php';
include 'connect.php';
?>
<!doctype html>
<html>
<body>
<?php
$userid = $_SESSION['user_id'];
echo '<a class="like" href="#" onclick="like_add(', $userid,
');">Like</a>';
?>
<script type ="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type ="text/javascript" src="like.js"></script>
</body>
</html>
connect.php:
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "DB";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>
init.php:
<?php
session_start();
$_SESSION['user_id']='1';
$userid = $_SESSION['user_id'];
include 'connect.php';
include 'like.php';
?>
like.js:
function like_add(userid) {
$.post('like_add.php', {userid:userid}, function(data) {
if (data == 'success'){
add_like($userid);
} else{
alert(data);
}
});
}
like.php:
<?php
function add_like($userid){
include 'connect.php';
$stmt = $conn->prepare("INSERT INTO clicks (user) VALUES (?)");
$stmt->bind_param("s", $userid);
$stmt->execute();
$stmt = $conn->prepare("SELECT max(id) FROM clicks WHERE user=?");
$stmt->bind_param("s", $userid);
$stmt->execute();
$stmt->bind_result($click);
$stmt->fetch();
echo $click;
$stmt->close();
}
?>
like_add.php:
<?php
include 'init.php';
if (isset($userid)) {
$userid = $userid;
add_like($userid);
}
?>
Your query might give incorrect results if the same user sends multiple requests almost at the same time, case when your query will not return the currently inserted id. You can use the last_insert_id() mysql function which gives you the last inserted auto-increment value, regardless if meanwhile other requests updated the table.
Also, you don't need to pass the user_id parameter with the ajax request, as you anyway can obtain it from the session. Passing the user_id can be considered a security hole, as anyone can modify the onclick handler and trigger clicks for other users. I'd recommend avoiding as much as possible sending user ids in plain text as response.
To add more security: in your connection script. Change the $servername, $username etc to constants. These don’t need to change, and you don’t want them to be changed.
Do you have any type of checks for your sessions? Sessions are more secure than cookies but they can be hijacked in transit when a user logs in. To add some security to your session, use the session_regenerate_id() function when the user logs in, this will generate a new session id therefore if the users id has been hijacked, it is of no use as it will have changed. There are other checks that can be carried out on sessions to secure them but this is a good quick way of adding an extra level.
#nomistic makes some good suggestions also especially regarding encryption of passwords and other sensitive information. Using the crypt() function or PHP’s password hashing API - http://php.net/manual/en/book.password.php. Is also a good way.
This looks pretty good on the php side. You are using session ids for user verification, and have prepared your SQL inserts. (One question, why are you setting $_SESSION['user_id']='1'? Do you plan on only having one user? That doesn't seem necessary to me)
However, you might want to tighten up your database-side security. It's probably a good idea to set up a different user for public database access, and limit the actions on the database side. For instance, if all they are going to do is select or insert, that user should only have access to do so. I wouldn't use your root account for that. Though it's probably not a huge risk (you are doing pretty well against SQL injection, at least the first two times) just to add another layer is always a good idea.
When dealing with security, it's helpful to think of a "use-case" scenario. What sort of data are you storing? Is it something that somebody really would want? (e.g. is it financial?) It's always a good idea to look at the human element. Would someone want to spend more than a day trying to hack your data (is it worth it for them?).
Also, though it's not evident here, you probably want to make sure you have a good form of encrypting passwords.
Another thought: even if it is minor risk, it's not a bad idea to run daily backups, so you can recover your data in a worst-case scenario.
Edit:
Since it was asked, here's how to setup security at the database side:
First create a new user (following this pattern):
CREATE USER 'newuser'#'localhost' IDENTIFIED BY 'password';
Granting permissions work like this:
GRANT [type of permission] ON [database name].[table name] TO ‘[username]’#'localhost’;
Types of privileges include ALL PRIVILEGES, CREATE,DROP, DELETE, INSERT, SELECT, UPDATE, GRANT OPTION.
If you want to read up on this more, here's the documentation: https://dev.mysql.com/doc/refman/5.1/en/adding-users.html
I'm having a problem with a PHP-Script, where I want to check, if the MySQL-Logindata are valid. I found somewhere the mysql_ping() function, but it doesn't work as it should, because it returns in every case true, even if the inserted data are totally wrong. How do I solve it?
$verbindung = mysql_connect($_POST["mysql_host"], $_POST["mysql_user"],
$_POST["mysql_pwd"]);
if (!mysql_ping($verbindung)) {
$break = true;
}
else {
// Check here also via SQL, if database $_POST["mysql_db"] exists
}
This already is more complex than it needs to be and should do the correct thing. The reason why all usernames work quite likely is that MySQL by default has an anonymous user ''#'localhost' which accepts any username. Probably you want to remove that user. (DROP USER ''#'localhost', be sure you can login as other user before doing that, use SHOW GRANTS to see which user you are using)
For simplification mind that the connect cal will fail if there is something wrong, so you wont need that ping call. ping can be used if you have a longer living connection and you want to check whether the connection is still working.
A simple form for the check might look like this:
$verbindung = new mysqli($_POST["mysql_host"], $_POST["mysql_user"], $_POST["mysql_pwd"]);
if (!$verbindung) {
echo "Wrong settings";
}
Mind that I changed to the mysqli insterface. the old mysql extension in PHP providing the mysql_* functions is deprecated and shouldn't be used anymore.
I actually found out a nicer solution with the mysqli_connect_errno() function and a requirement of inputs from the database name and the user name.
$verbindung = #mysqli_connect((!empty($_POST["mysql_host"]) ? $_POST["mysql_host"] : 'localhost'),
$_POST["mysql_user"], $_POST["mysql_pwd"], $_POST["mysql_db"]);
if (mysqli_connect_errno() || empty($_POST["mysql_user"]) || empty($_POST["mysql_db"])) {
$break = true;
}
Yesterday my site was hacked, the hacker managed to login to the admin area and post a blog which contained a redirect link to his website. So Im asking for a bit of help in making my login secure.
Here is my script:
$username = $_POST['username'];
$password = md5_base64($_POST['password']);
$stmt = $mysqli->prepare("SELECT id, username, password, permission FROM user WHERE username = ? AND password = ?");
$stmt->bind_param('ss', $username, $password);
$stmt->execute();
$stmt->bind_result($userid, $username, $password, $permission);
$stmt->store_result();
if(($numRows = $stmt->num_rows) > 0)
{
$response_array['status'] = "success";
$response_array['message'] = "Logged in";
}
else
{
$response_array['status'] = "error";
$response_array['message'] = "Sorry, Wrong Username/Password Combination" .$password;
}
Heres the md5_base64 function:
function md5_base64 ( $data )
{
return preg_replace('/=+$/','',base64_encode(md5($data,true)));
}
Any help, advice and improvements are greatly appreciated.
A little bit information regarding how attack happened is required.
It may be the possibility of SQL Injection rather than programming
defect.
Always sanitize your input before sending the query to database , a
single quote can create a SQL Injection attack.
Watch for default passwords or simple passwords like admin ,
admin123 , 12345 etc.
One can easily guess or can use Dictionary attack to crack it.
Use Complex Passwords as well.
If you are using Database , use Prepared Statement .
Regards
Anshul Katta
There is nothing wrong with your login script, save for the function md5_base64() which is quite silly. But from the SQL injection point of view it's innocent.
With such a password like 'qwerty123' one don't need a cunning injection to break through. A name, I suspect, is as easy guessable as a password.
Or there can be some other flaw. like silly cookie to remember the user or such
The question you need to ask yourself - How did that person get the password?
Did he log into the actual machine? This can be prevented by a security audit and remove access to ftp/telnet - edit /etc/inetd.conf and remove unnecessary services. Also check daemons that are started upon boot - Google runlevels and what is started. Just use SSH to access the machine.
Then look at the database. MySql can be configured to only accept connections from certain IP addresses and use SSH. Also ensure that access to the database is via user accounts with known permissions for particular databases
Also check your firewall. This can prevent people accessing the machine remotely.
Then look at the configuration of the web server. Perhaps use HTTPS for access to the administration area.
Also follow the advice given by anshulkatta above