How dangerous is this php code? What can be done about it?
$name = $_POST["user"];
$pwd = $_POST["pwd"];
$query = "SELECT name,pwd FROM users WHERE name = '$name' AND pwd = '$pwd'";
Possible Problems:
SQL Injection
XSS Injection (if this code was an insert query, it would be a definite problem)
Plain Text Password
Your SQL Statement can be problematic. It is bad practice to leave yourself open for SQL injection.
SQL Injection is bad. Trust me.
If you want to display the $user on an HTML page, then you may not want to include the ability for people to "hack" your layout by typing in commands like
<H1>HI MOM</H1>
or a bunch of javascript.
Also, never store your password in plain text (good catch cagcowboy!). It gives too much power to people administering (or hacking) your database. You should never NEED to know someone's password.
Try tactics like these:
// mostly pulled from http://snippets.dzone.com/posts/show/2738
function MakeSafe($unsafestring)
{
$unsafestring= htmlentities($unsafestring, ENT_QUOTES);
if (get_magic_quotes_gpc())
{
$unsafestring= stripslashes($unsafestring);
}
$unsafestring= mysql_real_escape_string(trim($unsafestring));
$unsafestring= strip_tags($unsafestring);
$unsafestring= str_replace("\r\n", "", $unsafestring);
return $unsafestring;
}
// Call a function to make sure the variables you are
// pulling in are not able to inject sql into your
// sql statement causing massive doom and destruction.
$name = MakeSafe( $_POST["user"] );
$pwd = MakeSafe( $_POST["pwd"] );
// As suggested by cagcowboy:
// You should NEVER store passwords decrypted.
// Ever.
// sha1 creates a hash of your password
// pack helps to shrink your hash
// base64_encode turns it into base64
$pwd = base64_encode(pack("H*",sha1($pwd)))
It's this dangerous:
SQL Injection aside, it looks like your passwords might be stored in plain text, which isn't great.
That code is very safe if you never pass $query to a SQL database.
If one were to post 0';drop table users;-- for a name
your command would end up being
select name, pwd form users where name='0';
drop table users; --'and pwd = '[VALUE OF PWD]'
So first it would get your data, then kill your users table, and do nothing with the rest since it is a comment.
Certain mysql commands in php will perform multiple queries when passed sql, the best way to avoid this is parametrized queries.
I use PDO for all my DB access, and highly recommend it. I do not have any links off the top of my head but I remember the tutorials I used topped Google.
It is not only prone to SQL injections, it will also fail in cases where an injection is not even intended:
For example a user wants the name "Guillaume François Antoine, Marquis de L’Hospital". Since the username contains a quote and you are not escaping it, your query will fail, although the user never wanted to break the system!
Either use PDO or do it in this way:
$query = sprintf(
"SELECT 1 FROM users WHERE name = '%s' AND password = '%s'",
mysql_real_escape_string($_POST['name']),
mysql_real_escape_string(md5($_POST['password']))
);
Believe it or not, this is safe... if magic_quotes_gpc is turned on. Which it will never be in PHP6, so fixing it prior to then is a good idea.
$_POST['user'] = "' or 1=1; --";
Anyone gets instant access to your app
$_POST['user'] = "'; DROP TABLE user; --";
Kiss your (paid?) user list goodbye
If you later echo $name in your output, that can result in a XSS injection attack
:O don't do it never ever,
This can cause SQLInjection attack. If for example user input somehow:
' drop table users --
as input in $username; this code will concatinate to your orginal code and will drop your table. The hackers can do more and can hack your website.
This is typically very dangerous. It could be mitigated by database permissions in some cases.
You don't validate the input ($name and $pwd). A user could send in SQL in one or both of these fields. The SQL could delete or modify other data in your database.
Very very dangerous. A good idea for passwords is to convert the password into a MD5 hash and store that as the user's 'password'.
1) protects the users from having their passwords stolen
2) if a user writes a malicious string they could wipe out your entry/table/database
Also you should do some basic match regex expression on the name to make sure it only uses A-Za-z0-9 and maybe a few accented characters (no special characters, *'s, <'s, >'s in particular).
When user data is involed in a SQL query, always sanatize the data with mysql_real_escape_string.
Furthermore, you should store just a salted hash of the password instead of the password itself. You can use the following function to generate and check a salted hash with a random salt value:
function saltedHash($data, $hash=null)
{
if (is_null($hash)) {
$salt = substr(md5(uniqid(rand())), 0, 8);
} else {
$salt = substr($hash, 0, 8);
}
$h = $salt.md5($salt.$data);
if (!is_null($hash)) {
return $h === $hash;
}
return $h;
}
All together:
$query = 'SELECT pwd FROM users WHERE name = "'.mysql_real_escape_string($_POST['user']).'"';
$res = mysql_query($query);
if (mysql_num_rows($res)) {
$row = mysql_fetch_assoc($res);
if (saltedHash($_POST["pwd"], $row['pwd'])) {
// authentic
} else {
// incorrect password
}
} else {
// incorrect username
}
Its not safe, you might want to look into something like PDO.
PHP PDO
Related
We have been attacked; the hackers entered the system from a page <login> that's in the code shown below, but we couldn't figure out the actual problem in this code.
Could you point out the problem in this code and also a possible fix?
<?php
//login.php page code
//...
$user = $_POST['user'];
$pass = $_POST['password'];
//...
mysql_connect("127.0.0.1", "root", "");
mysql_select_db("xxxx");
$user = mysql_real_escape_string($user);
$pass = mysql_real_escape_string($pass);
$pass = hash("sha1", $pass, true);
//...
$query = "select user, pass from users where user='$user' and pass='$pass'";
//...
?>
The problem here is in $pass= hash("sha1",$pass, true);
You need to put it like this $pass= hash("sha1",$pass, false);
A good option is to move to PDO.
Let's see why this happen:
What your code is doing is returning a raw binary hash that means at a point in time the hash may contain an equal character =,
for your example the hash that going to result in SQL injection in this case is "ocpe" because hash ("ocpe",sha1) have a '=' character,
but how can I figure that out?
You only need to run a simple brute force and test if it contains a '=' inside the hash raw bit.
This is a simple code which can help you with that
<?php
$v = 'a';
while(1)
{
$hash = hash("sha1",$v, true);
if( substr_count( $hash, "'='" ) == 1 ) {
echo $v;
break;
}
$v++;
}
?>
Now you you have a string that gives a hash that has an equal inside of it '='
The query becomes:
$query = "select user, pass from users where user='$user' and pass='hash("ocpe",sha1)'";
then
$query = "select user, pass from users where user='$user' and pass='first_Part_of_hash'='Second_part_of_hash'";
In this case I assume that ocpe string has a hash of this format first_Part_of_hash'='Second_part_of_hash
Because pass='first_Part_of_hash' going to result in 0 and 0='Second_part_of_hash' is typecasted by the SQL engine, but in case of string if we type cast it to a int it's going to give as 0 ((int)'Second_part_of_hash' is result in 0)
so in the end 0=0
$query = "select user, pass from users where user='$user' and 0=0";
Which going to result in "true" every time and as you can see it can be applied to all hash functions like MD5 and sha256 etc.
Good resources to check:
How can I prevent SQL injection in PHP?
Could hashing prevent SQL injection?
To supplement the excellent answer from zerocool.
The problem here is the false notion that mysql(i)_real_escape_string prevents SQL injection. Unfortunately, too many people have been led to believe that this function's purpose is to protect them from injections. While of course it is not nearly true.
Had the author of this code the correct understanding of this function's purpose (which is escaping special characters in a string literal), they would have written this code as
$user = mysql_real_escape_string($user);
$pass = hash("sha1", $pass, true);
$pass = mysql_real_escape_string($pass);
and there wouldn't have been any injections at all.
And here we come to an important conclusion: given escaping's purpose is not to prevent SQL injections, for such a purpose we should use another mechanism, namely prepared statements. Especially given the fact that mysql extension doesn't exist in PHP anymore while all other extensions support prepared statements all right (yet if you want to reduce the pain of transition you should definitely use PDO, however paradoxical it may sound).
(Supplementary to the other answers / comments about using PDO, correct use of passwords etc; Logging this here in case someone else stumbles on this question.)
No one has pointed out:
mysql_connect("127.0.0.1","root","");
mysql_select_db("xxxx");
as being a point of weakness.
This means that:
- the DB server is on the same host as the web server, and therefore has a network interface to the world.
- this have the most basic user (root) available,
- and without a password.
Hopefully this is an example/test, but if not, ensure that at least the server port (3306) is blocked by firewall / not accessible externally.
Otherwise a simple mysql -h [webserver address] -u root will connect and it's game over.
You can rewrite your validation logic as a quick fix to the issue explained by #zerocool.
// don't send password hash to mysql, user should be uniqe anyway
$query = "select user, pass from users where user='$user'";
// validate hash in php
if (hash_equals(hash('sha1', $pass, true), $user_hash_from_db)){...}
And as others wrote, stop using mysql_* functions ASAP, and use stronger hashing algo.
You can fix your existing code, without breaking any of the existing passwords, by adding one line:
$pass = $_POST['password']; // the actual password
$pass = mysql_real_escape_string($pass); // escaped version of the actual password
$pass = hash("sha1",$pass, true); // binary hash of the escaped password
// At this point, $pass is the exact string that is stored in the database.
$pass = mysql_real_escape_string($pass); // ***ADD THIS LINE***
$query = "select user, pass from users where user='$user' and pass='$pass'";
Note that the password stored in the database is the binary hash of the escaped version of the actual password. Since it is a binary string, you need to escape it.
Be sure to add the extra escaping to the code that stores the password in the first place, otherwise password setting will also have a SQL injection vulnerability.
This question already has answers here:
How can I prevent SQL injection in PHP?
(27 answers)
Closed 6 years ago.
This is my basic code of a login.php. It contains a basic $_POST method to get the values from form. Someone said me it is insecure. It is prone to SQl Injection. I dont want to make it complex. Please tell me how to make it more secure by simple methods.
<?php
session_start();
require('connection.php');
$email=$_POST['username'];
$password=$_POST['password'];
//variables in query will be in single quotes.
$query="select * from user_info where email='$email' and password = '$password' ";
//fetching result
if($result=$con->query($query))
{
$row =$result->fetch_array(MYSQLI_NUM);
if($row>0)
{
//creating session variables
$_SESSION['loggedIn']=true;
$_SESSION['email']=$row[1];
$_SESSION['id']=$row[0];
header("Location:profile.php");
}
else
{
echo "flag1";
header("Location:index.php?loginerror=invalid login");
}
}
else {
echo "Failed";
}
?>
Dangers for the database: SQL-Injections
Those are the dangers when using input data from the user to operate with the database. Some input might cause the sql-syntax to break, the database will get a badly formatted statement and run onto an error (if it was unintentionally) or even execute statements it was not supposed to (sql injection).
A possibility for unintended syntax breaking would be to ask the user for his name and he inserts something like O'Connor.
Way to treat them: How can I prevent SQL injection in PHP? tldr: prepared statements are king.
(+) I would guess most ORMs or abstraction layers between your application and your database would solve this problem on their own, should inform yourself about it tho - better safe than sorry.
Dangers for the frontend: XSS attacks
A user uses your form to input some JavaScript. When you are displaying it on your website it will get parsed and executed.
Way to treat them: use htmlentities() when displaying userdata.
(+) There are libraries like htmlpurifier which allow you to only sanitize specific elements. So if you want to let your users use HTML for a blogpost or whatever, you can a library like this which will allow certain elements and defuse the dangerous stuff.
Dangers for the filesystem: shell-injections (?)
When you are for some reason using user input to do something on your filesystem (creating a directory with the same name as the username or whatever..) someone could do the same as in sql - break your syntax and inject some commands.
Way to treat them: escapeshellcmd + Don't use user input for something like directory or filenames on your server! Put the names of the user into the database, use the id or a hash or something you generated as the filename.
Cross-Site-Request-Forgery:
Doesn't directly involve the data given to you, but I am adding it because it is something you should know about: Understanding CSRF
Remember: Treat the data based on the context in which you are using it!
There are different places for attacks and each one uses other weaknesses. So there is not the one solution to fix all the problems.
You just have to consider the context in which you are handling the data - are you saving it or displaying it.
When displaying it you want to take care of the JavaScript that might be hidden in the data so you want to escape the charakters which are special in html.
When saving data you worry only about the charakters which might break the syntax of your sql. Your database knows best which charakters to escape and how to treat which data, so just use the escaping and quoting functions provided by your database-api OR BETTER: Prepared Statements (just use them always when interacting with the database + userdata) as they work differently then normal queries and you don't have to think about escaping and quoting.
I would use PDO's. A very basic example;
<?php
session_start();
require('connection.php');
$sError = "";
$sEmail = "";
$sPassword = "";
if(isset($_POST['Login'])){
if(isset($_POST['username'])) $sEmail = $_POST['username'];
if(isset($_POST['password'])) $sPassword = $_POST['password'];
if($sPassword != '' && $sEmail == ''){
// create an instance of the connection
$conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );
// prepare the sql
$sSQL = "SELECT Email, Id from user_info where email=:email and password = :password ";
$st = $conn->prepare( $sSQL );
// bind the input vars
$st->bindValue(":email", $sEmail, PDO::PARAM_STR);
$st->bindValue(":password", $sPassword, PDO::PARAM_STR);
$st->execute();
// if data is returned from calling fetch
if( $row = $st->fetch() ){
$_SESSION['loggedIn'] = true;
$_SESSION['email'] = $row['Email'];
$_SESSION['id'] = $row['Id'];
header("Location:profile.php");
exit;
}
}else{
// must have empty input add to errror string
$sError .= "[EmptyInput]";
}
// user hasnt been logged in set error
$sError .= "[InvalidLogin]";
}
// detect anthign in error stirng
if($sError != ""){
// do something
}
?>
Havent tested it but should work.
I would also salt and encrypt your passwords.
Additionally I would generate a code to use to identify the user publicly.
Considering a project where will work more than one developer and that will receive constant update and maintenance, what of the two codes below can be considered the best practice in PHP, considering Readability and Security? If we talk in performace, the second option will probably be a little better, but there are ways to solve this point.
Option 1
$user = $_POST['user'];
$pass = $_POST['pass'];
// Prevent SQL Injection and XSS
$user = anti_injection($user);
$pass = anti_injection($pass);
if(strlen($user) <= 8 && strlen($pass) <= 12)
{
// SQL query
$sql = "SELECT id
FROM users
WHERE username = '$user' AND password = '$pass';";
}
Option 2
// Retrieve POST variables and prevent SQL Injection and XSS
$_POST['user'] = anti_injection($_POST['user']);
$_POST['pass'] = anti_injection($_POST['pass']);
if(strlen($_POST['user']) <= 8 && strlen($_POST['pass']) <= 12)
{
// SQL query
$sql = "SELECT id
FROM users
WHERE username = '" . $_POST['user']. "' AND password = '" . $_POST['pass'] . "';";
}
EDIT 1
I am not using MySQL, my database is PostgreSQL
Don't do either.
I can only assume anti_injection is some sort of custom filtering function, which is a Bad Idea™. If you really want to adopt either of these idea, you should be using mysql_real_escape_string.
The only way to remain secure when writing SQL queries is to use parameters, e.g. through MySQLi. The mysql_* functions are becoming deprecated anyway, so you're best to move across as soon as possible.
In fact mysql_real_escape_string is not a foolproof defense against injection attacks. Consider an integer comparison in a query, such as WHERE $var > 30. I could inject 1=1 or 100 into $var successfully, and completely break the logic.
Parameters completely separate data from query language, completely mitigating the injection risk. The server receives a query containing parameter notation, and a set of values to insert, so it can handle the query language and data completely differently.
Furthermore, you seem to be storing passwords in plaintext. This is a bad idea. You should look into a strong password storage hash algorithm such as bcrypt, which makes it very difficult to obtain plaintext passwords from the hashes.
MD5 and SHA1 are not ideal for password storage, because they are designed to be fast, meaning an attacker can quickly crack even strong salted passwords. Modern GPUs can achieve 5 billion MD5 hashes per second. In fact, there are people with dedicated hash cracking rigs, some of which can crack MD5 at 45 billion hashes per second.
You should also take a look at these awesome questions, which completely cover SQL injection attacks, password storage, and a multitude of other security issues:
The definitive guide to form-based website authentication
How can I prevent SQL injection in PHP?
Secure hash and salt for PHP passwords
Update: You mentioned you're using postgres. You can use PDO to run parameterised queries from PHP, as described briefly here.
Neither of these options is a best practice, if only for the dubious anti_injection which is almost certainly not working as advertised. There's also the matters of:
munging your input data before validating them
storing plaintext passwords
constructing SQL queries manually instead of using bound parameters
Depending on the scope of the project the last one might be acceptable.
Regarding performance, the first option is going to be theoretically faster because it does less array indexing. But the difference would definitely be so small as to not be observable at all. The first option is also more readable and provides better abstraction, all for just a negligible amount of extra memory.
Both are wrong. You appear to be storing the password as plain text, which is just asking for trouble.
Also, if my username is "123456", I would be unable to log in because you would escape it to \"123456\" and that would fail the "length <= 8" check.
I would suggest using PDO if you want security and performance. You cannot sql inject when using parameters. Below is an example, you need to "define" the mysql params for the below to work.
This also allows for the database to cache your query, since it won't change every time you execute with a different parameter which will increase performance as well.
:p_user and :p_pass
is used to denote the parameters and
array ( ':p_user' => $user, ':p_pass' => $pass ' )
sets the parameters to the values you need to pass in.
You should also consider adding a password salt, and storing the sha1 of that in the databased, so that if your database is compromised, your passwords are not clearly revealed to the hacker.
class users{
function __construct() {
define('MySQLHost', 'localhost');
define('MySQLName', 'databasename');
define('MySQLUser', 'username');
define('MySQLPass', 'password');
define('pwsalt', 'tScgpBOoRL7A48TzpBGUgpKINc69B4Ylpvc5Xc6k'); //random characters
}
static function GetQuery($query, $params)
{
$db = new PDO('pgsql:host='.MySQLHost.';dbname='.MySQLName.';', MySQLUser, MySQLPass);
$cmd = $db->prepare($query);
$cmd->execute($params);
if($cmd->rowCount()==0)
return null;
return $cmd->fetchAll();
}
static function GetUser($user, $pass)
{
$query = "select id
from `users`
where username = :p_user and password = :p_pass";
$rows = users::GetQuery($query, array(
':p_user' => $user,
':p_pass' => sha1($pass.pwsalt) //Append the salt to the password, hash it
));
return $rows;
}
}
$user = $_POST['user'];
$pass = $_POST['pass'];
if( strlen($user) <= 8 && strlen($pass) <= 12 )
{
$result = users::GetUser($user, $pass);
if($result != null)
print 'Login Found';
}
While I agree with the other posters, don't try to "reinvent the wheel" regarding SQL security, the question seems to be about performance and how to use the superglobals.
As best practice, do NOT mutate the superglobals ($_GET, $_POST, $_REQUEST, $_SYSTEM, etc.) I can't think of a single example where violating this rule would improve your performance, and any modification will cause uncertainty and confusion down the road.
So in this case, neither option is correct. Option1 copies a variable needlessly (a no-no according to the Google performance docs). Option 2 mutates the superglobals, which violates the maxim above. Instead do something like:
$user = anti_injection( $_POST['user'] );
$pass = anti_injection( $_POST['pass'] );
if( strlen($user) <= 8 && strlen($pass) <= 12 ) ...
However I should reiterate that "homemade sanitation" is a frightening prospect, the other commentors have elaborated quite thoroughly on this point.
Is this login system secure ?
if ($_POST[$submit]){
$user = $_POST[$user];
$pass = $_POST[$pass];
if ($user && $pass){ //if user and pass is enterered
require("vars.php"); //require MySQL conection settings
mysql_connect($auth_mysql_server, $auth_mysql_user, $auth_mysql_pass); //connect to MySQL
mysql_select_db($auth_mysql_db); // select MySQL database
$pass = md5($pass); // hash password
$query = mysql_query("SELECT * FROM $auth_mysql_table WHERE user='$user'"); // run query
$numrows = mysql_num_rows($query);
if ($numrows == 1){ //check if user exists
$row = mysql_fetch_assoc ($query);
$dbid = $row[$auth_mysql_id_row];
$dbuser = $row[$auth_mysql_user_row];
$dbpass = $row[$auth_mysql_pass_row];
if ($pass == $dbpass){ // if password is equal to the one in the database start session
session_start();
//set session information
$_SESSION['user'] = $dbuser;
header("Location:$auth_loggedin"); // goto logged in page
}
else return (3);
}
else return (2);
mysql_close(); // close MySql connection
}
else return (1);}
If not how could i make it secure ?
I hashed the password but i know md5 can be decrypted however sha1 can be too.Also is themysql_close() needed ?
Add salt to your hashes. This can be a random string, a user's name, timestamp of account creation or pretty much whatever you like, as long as it's the same every time a given user logs in. The purpose is to first, break rainbow tables (long lists of common passwords that have been md5 encrypted) and second, add entropy to normally short passwords.
$pass = $_POST['pass'];
$salt = "7y9fhu8a"
$secure_pass = md5( $pass . $salt );
Secondly, you're not sanitizing your username input. You could add a mysql_real_escape_string to your username to prevent sql injection attacks.
$query = "SELECT * ".
"FROM $auth_mysql_table ".
"WHERE user='" . mysql_real_escape_string($user) . "'";
$result = mysql_query($query);
There are other ways to sanitize user input, but that's the quick and dirty. As far as the mysql_close goes, i wouldn't bother. If you need to run any other queries you'd need to reopen the connection.
no it is not secure. you are opening yourself to sql-injection attacks. imagine what would happen, if somebody entered this into your user-input-field (which goes into the $_POST[$user])
a'; drop table user; select '1'='1
this would result in an sql statement like this:
SELECT * FROM $auth_mysql_table WHERE user='a'; drop table user; select '1'='1'
which you would execute against your database. that's bad!
you need to sanitize your input. read this: http://php.net/manual/en/security.database.sql-injection.php
edit: relevant
No it's not secure.
You need to sanitize your user inputs or (even better) use bind variables. For PHP you can use PDO: PHP PDO prepared statements
Also, an unsalted md5 is a terrible choice for hashing passwords and provides no real security. A salted md5 is only marginally better (but still in the terrible category).
You should either:
Not store any passwords- use federated login (OpenID, OAuth, etc)
If you are going to handle passwords yourself then use a much stronger hash function such as scrypt, bcrypt, or PBKDF2.
mysql_close is not needed but is good practice. PHP will automatically close the connection at the end of the script.
MD5 is not the best encryption method, you can use some other PHP encryption library like AES
See this link for more information about PHP encryption:
http://php.net/manual/en/ref.mcrypt.php
keep in mind that you also have to ALWAYS sanitize the code !!
MySQL injection can be a bad beast!!
I need the following authentication script finished. I am weak at php/pdo so I do not know how to ask for the number of rows equalling one and then setting the session id's from the results of the query. I need to not only set the $_SESSION['userid'] but also the ['company'] and the ['security_id'] as well from the results.
here is what I have:
$userid = $_POST['userid'];
$password = $_POST['pass'];
if ( $userid != "" || $password != "" )
{
$sql = "SELECT * FROM contractors WHERE userid = '" . $userid . "' AND password = '" . $password . "'";
$result = $dbh->query( $sql );
} else
{
echo "login failed. Your fingers are too big";
}
Optional Information:
Browser: Firefox
DO NOT EVER USE THAT CODE!
You have a very serious SQL injection open there. Every user input that you take, whether from cookies or CGI, or wherever, must be sanitized before it's used in an SQL statement. I could easily break into that system by attempting a login with an username like:
user'; UPDATE contractors SET password = '1337'
... after which I could then login as anyone. Sorry if I sound aggressive, but what that code does is like forgetting to lock the front door into your company which probably doesn't even contain an alarm system.
Note that it doesn't matter whether the input is actually coming from the user or not (perhaps it's in a pre-filled, hidden from). From the security point of view, anything that comes from anywhere outside has to be considered to contain malicious input by the user.
As far as I know, you need to use the quote function of PDO to properly sanitize the string. (In mysql, this would be done with mysql_real_escape_string().) I'm not an expert on PDO, mind you, somebody please correct if I'm wrong here.
Also you probably shouldn't store any passwords directly in the database, but rather use a hash function to create a masked password, then also create a hash from the user provided password, and match the hashes. You can use the PHP hash function to do this.
As for other issues, I don't know if the approach you have on SQL SELECT is the best approach. I would just select the corresponding user's password and try matching that in the program. I don't think there's any fault in the method you're using either, but it just doesn't seem as logical, and thus there's a greater chance of me missing some bug - which in case of passwords and logins would create a window for exploits.
To do it your way, you need to notice that the result you are getting from the PDO query is a PDOStatement, that doesn't seem to have a reliable function to diretly count the amount of result rows. What you need to use is fetchAll which returns an array of the rows, and count that. However, as I said this all feels to me like it's open for failures, so I'd feel safer checking the password in the code. There's just too much distance from the actual password matching compasion for my taste, in such a security-critical place.
So, to the get the resulting password for the userid, you can use PDOStatement's fetch() which returns the contents of the column from the result. Use for example PDO::FETCH_ASSOC to get them in an associative array based on the column names.
Here's how to fix it:
$userid_dirty = $_POST['userid'];
$password_dirty = $_POST['pass'];
$success = false; // This is to make it more clear what the result is at the end
if ($userid != "" || $password != "") {
$userid = $dbh->quote($userid_dirty);
$passwordhash = hash('sha256',$password_dirty);
$sql = "SELECT userid, passwordhash, company, security_id FROM contractors WHERE userid = ".$userid;
$result = $dbh->query( $sql );
if ($result) { // Check if result not empty, that userid exists
$result_array = $result->fetch(PDO::FETCH_ASSOC);
if ($result_array['PASSWORDHASH'] == $passwordhash) {
// login success
$success = true;
// do all the login stuff here...
// such as saving $result_array['USERID'], $result_array['COMPANY'], $result_array['SECURITY_ID'] etc.
} // else fail, wrong password
} // else fail, no such user
} else {
// fail, userid or password missing
echo ' please enter user id and password.';
}
if (!$success) {
echo ' login failed.';
}
Of course, the code can be cleaned up a bit, but that should explain what needs to be done. Note that since the password is both hashed, and never used in the SQL, it doesn't actually need cleaning. But I left it there just in case, since in the original code it was used in the query.
Note that all the code concerning storing passwords need to be changed to store the hash instead of the password. Also, it would be a very good idea to use a salt added to the password before hashing.
Also, I provided the code simply for educational purposes - I just thought that code was the clearest way to explain how to do this. So do not mistake this site as a service to request code. :)
The php manual is an excellent resource for learning PHP. It looks like you know a little SQL, and you have heard of PDO, which is a good start. If you search google for "PDO", or look in the PHP manual for the term, you'll find the PDO section of the manual. It looks like you've found the ->query function, so now you need to see what that returns. Going to the that function's manual page, we see that it returns a PDOStatement object. The word PDOStatement is helpfully linked to the relevant page in the manual, which lists the methods available on that object. There is a rowCount() method that will likely do what you want.