I'm currently working on building a download platform in which a user receives a random code and uses it to access an mp3 for download for up to three downloads. I generated a list of random codes using Python and imported them into a SQL table with an empty column for an associated email addresses and a default 0 for the use count. I wrote the following PHP script in order to associate an email with a certain code and add to the count so a download can be accessed up to three times.
$email = $_POST["email"];
$email = stripslashes($email);
$uniqueCode = $_POST["uniqueCode"];
$uniqueCode = stripslashes($uniqueCode);
// check that all fields are filled
if($uniqueCode=="" || $email=="")
apologize("Please fill out all fields.");
// check to make sure that the e-mail is valid
if (verifyEmail($email) == FALSE)
apologize("Please enter a valid e-mail address.");
// check if uniqueCode input is alphanumeric
if (verifyCode($uniqueCode) == FALSE)
apologize("Download codes are alphanumeric.");
// check to see if unique code is correct
$sql = mysql_query("SELECT * FROM wd009 where uniqueCode='$uniqueCode'");
$result = mysql_fetch_array($sql);
if($sql==FALSE)
{
apologize("Your download code is invalid. Please try again");
}
// only allow users with less than 3 downloads to proceed
else if ($result['count'] <= 3) {
if ($result['email'] == ""){
mysql_query("UPDATE wd009 SET email='$email', count=1 WHERE uniqueCode='$uniqueCode'");
apologize("added email");
}
else if ($result['email'] != $email)
apologize("different email from record!!");
else if ($result['email'] == $email){
mysql_query("UPDATE wd009 SET count=count+1 WHERE uniqueCode='$uniqueCode'");
apologize("updated the count!");
}
else
apologize("Your download code is used up!");
Obviously I use some functions in the above that aren't included in the code but I've checked all of them and none of them should interfere with the MySQL query. It may be of note that apologize() exits immediately after apologizing. When I input a correct code into the form, it works correctly and updates the SQL database. However, as long as the download code input is alphanumeric, the form will accept it even though the string definitely does not match any in the table. Namely, mysql_query returns a resource no matter the input. I've checked the database connection but since the table is correctly updated when the download code is correct, that doesn't seem to be the problem.
I've tried debugging this every way I could think of and am genuinely befuddled. Any help you could offer would be greatly appreciated!
As you can see in the manual, mysql_query always returns a resource for a valid query so you need to change your logic and count the number of rows it returns, not the result of mysql_query.
Apart from that, mysql_query, is deprecated and you should use mysqli or PDO.
You can count the number of rows with the - equally deprecated - mysql_num_rows function. 0 rows would be no valid code in your case.
This
if($sql==FALSE)
should probably be something like
if(mysql_num_rows($sql) == 0)
Edit: I agree, mysqli or PDO is preferred now.
The issue probably is this line:
if($sql==FALSE)
{
apologize("Your download code is invalid. Please try again");
}
Since the sql is a string it is accepting is as true and passing it as valid. One thing you also might like to do to avoid sql injection is use parameters instead of directly injecting user input.
$sql = mysql_query("SELECT * FROM wd009 where uniqueCode='$uniqueCode'");
Instead, do something like this:
$stmt = $mysqli->prepare("SELECT * FROM wd009 where uniqueCode=?");
$stmt->bind_param($uniqueCode);
$stmt->execute();
while ($stmt->fetch()) {
.....
You'll also want to do it this way for the update statement too.
If you have a lot of data in that table you may want to restrict the columns returned in the SQL statement so it lessens the load on the database.
Related
I have written a long program. Im just sharing here some lines to understand a concept which is my issue here. I want to check if the previous data is equal to the newly entered, if it is, then an error should occur that the data is already entered, without storing again in db. Following code is running but it duplicates data. It is not quite good. may you please check where I am wrong. thanks
Database Name Booking:
Table Name data:
Name: John, Anjel, Smith, David
CNIC: 33, 46, 33, 91
//// storing data in database from html form fields
<?php
$emp_name = addslashes($_POST['emp_name']);
$emp_cnic = addslashes($_POST['emp_cnic']);
?>
//// Selecting CNIC that is duplicated (already existed) on a new form submission
$already = mysql_query("SELECT CNIC FROM data WHERE CNIC = '$emp_cnic'");
//Dieing script without storing the data if it already exists.
if($already){
die('Sorry! you are already registered. For any query, contact the administrator. ');
You couls also REPLACE mysql function.
If not, just change your request with a COUNT() and check if the returned result is greater than 0.
I would also highly recommand you to go on PDO instead of still using mysql_* (which is deprecated / removed)
You could use mysql_num_rows
$already = mysql_query("SELECT CNIC FROM data WHERE CNIC = '$emp_cnic'");
$record_count = mysql_num_rows($already );
// $record_count > 0 means data already exist
if( $record_count > 0 ) {
// Show error
} else {
// Save data
}
The problem I see with your code is the following:
For SELECT, SHOW, DESCRIBE, EXPLAIN and other statements returning
resultset, mysql_query() returns a resource on success, or FALSE on
error.
For other type of SQL statements, INSERT, UPDATE, DELETE, DROP, etc,
mysql_query() returns TRUE on success or FALSE on error.
The error you mentioned when you use mysql_num_rows() is caused because of the false return of the unsuccessful query.
You are checking a value that might be not boolean, to solve this use:
if(!isset($already) OR mysql_num_rows($already) == 0){
// die
}
else{
// do stuff
}
This check if the query was unsuccessful or if it is empty, so you always get a boolean to check your result set.
As #Sakuto pointed out is important to use not deprecated functions, therefor instead of using mysql_query() you should use mysqli or PDO functions.For further information read this.
Good evening everyone, I'm having issues with my below code, Variable $uname is declared from a http post but for some reason the print out the the err log stays blank where it should show the results of the MySQL query
Field in table is called firstname (no caps)
$da= mysqli_query($c,
"SELECT * FROM users WHERE username='".$uname."'") or die(mysqli_error($c));
while ($row = mysqli_fetch_assoc($da)) {
error_log("User $Uname: match.");
error_log("FN : ".$row['firstname']."");
}
Any ideas ?
Add a message to tell you if no results were found by the query.
Since there can be only one user with a given username (I assume), you don't need a while loop. Just fetch that one row.
$row = mysqli_fetch_assoc($da));
if ($row) {
error_log("User $uname: match.");
error_log("FN : ".$row['firstname']."");
} else {
error_log("User $uname: no match.");
}
You also had a typo in your echo statement, $Uname should have been $uname.
First of all use a prepared statement: for security reasons you should never trust user inputs, prepared statements do the escaping for you.
Other than that I only see two reasons why nothing is logged:
error reporting is turned off ( this seems unlikely since you expect to see something in there)
the query is returning an empty result set and the while loop is skipped
try to print $uname or the full query and execute it
For some reason, this query is running case-sensitive:
$stmt = $db->prepare("SELECT * FROM people WHERE email = :email LIMIT 1");
It simply returns whether or not it found the user:
$stmt->bindParam(':email', $email);
$stmt->execute();
$row = $stmt->fetch();
if($row['email'] == $email)
{
return "<span style='color: red;'>User found.</span><br>";
} else {
return "<span style='color: red;'>User not found.</span><br>";
}
(By the way, this is all just staging. There will be password hashing as soon as I see this is working properly).
It finds the user no problem if I use the same case as the database entry.
Here is my table, so you can see it's all defined ci:
This is actually an existing site that I built when I didn't know much about php, so I'm totally re-writing a lot, and setting up proper password hashing and https. This was all working fine before I wrote the new function and nothing in the database has changed...
So right now it only checks the email entered, just to see if the query is functioning and we're getting results from the database, later on we'll check the password and add actual login functionality.
Here is the data in the database:
Now if I fill out my username as "chris", and run the function, it returns "User Found", so I know the query was successful. If I fill it in as "Chris", however, it returns "User not found.", so I know it was unsuccessful.
Found the issue, though. Posted as an answer.
The issue is with:
if($row['email'] == $email)
The "==" comparison of the strings is case sensitive. A better way to do this would be to use:
if(!empty($row['email']))
If $row['email'] is not empty, then the query returned a result and was successful, else the query failed, which would be caused by using an email address which does not match any in the database.
To make the string comparison case inssensitive requires one of three approaches
Use lower (this will effect performance)
$stmt = $db->prepare("SELECT * FROM people WHERE LOWER(email) = LOWER(:email) LIMIT 1");
Another way is to use collation
And a way that I use is that I store the email as lower case to begin with and convert the search string to lower before doing the search.
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.
I use query string to access my pages. I try to make if anyone type unknown query string manually, then redirect to somewhere..
For example:
Url: test.php?m=1 (this is a valid url)
test.php?m=1324234 (this is not a valid url )
test.php?m=1asdaa (this is not a valid url )
include("config/database.inc");
$qm=$_GET['m'];
$query = "select acc from test where acc=$qm";
$numresults=mysql_query($query);
$numrows=mysql_num_rows($numresults);
if ($numrows == 0){
header("Location: index.php");
exit;
}
In the database i have two line, LINE 1: acc=1; LINE 2: acc=2;
If i type to the url: test.php?m=12312431, then redirect to index.php 'coz $numrows=0. Thats ok.
But if i type: test.php?m=1sdfsfsf, then i got this error msg.:
Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource in..
How can i do that? Need to check the $_GET['m'] before query from database?
Thank you.
You should never be placing the value of a GET variable directly into an SQL query without properly filtering and escaping it. Your problem is that you're allowing things which aren't numbers to be put into your SQL query. In this particular case, your test is harmless and just errors, but a malicious user could also do things much more harmful via SQL injection.
Instead, what you really want to do is convert the variable's value to a type that you know is okay (in this case, an integer) and then query the database using that. Try this instead:
include("config/database.inc");
$qm=intval($_GET['m']);
$query = "select acc from test where acc=$qm";
$numresults=mysql_query($query);
$numrows=mysql_num_rows($numresults);
if ($numrows == 0){
header("Location: index.php");
exit;
}
Notice the call to intval() which forces the result to be an integer value, instead of potentially harmful strings.
Do this:
$qm=intval($_GET['m']);
if m is invalid, mysql_query will return $query = false.
If you provide "false" to mysql_num_rows, it does not know what query you sent, and correctly get an invalid numrows result, e.g. a failure.
Check that m is integer with intval($m);
As well as validating the input or forcing a typecast as others have suggested to make sure the input makes sense, try to get into the habit of enclosing user-submitted values passed to a query in quotes, e.g.
$query = "select acc from test where acc='$qm'";
Although here you're technically comparing an integer to a string, MySQL allows this (and will still use integer keys where available), and it provides one last line of defence if you forget to filter the input.