Trying to retrieve information from the database t_awhole and input it into a_whole. I've read up on a lot of these, but cannot find anything specific to my question.
The code stops where I have placed the comment //>>>>Stops here<<<<<. I used an echo statement to find that it does not pass this point.
This is a email confirmation function, so t_awhole's table structure is different than a_whole. Everything is the same but in t_awhole the first column is a confirmation code associated to to that user. Once they click the link in their email, the row with that confirmation code should transfer all the data from that t_awhole into a_whole. However, it should not transfer the confirmation code, but should add a new column for the number of the row (first column) as an increment, and whether the user is an admin or not (last column). Everything else will stay as it was (fN, lN, aI, eml, pss) in between that first and last row. Please tell me how to do this. Someone attempted below, but his answer was difficult to understand (although he tried and I thank him for that).
Finally, I am using PDO as the structure. It was originally written in mysql. I notice a colon : is used instead of a money sign $. How do I switch this to be from sql to PDO?
<?php
include('db.php');
// passkey that got from link
$pk=$_GET['pk'];
$t_awhole_conf="t_awhole";
// Retrieve data from table where row that match this passkey
$sql_conf1="SELECT * FROM $t_awhole_conf WHERE confirm_code ='$pk'";
$result_conf=mysql_query($sql_conf1) or die ('Error updating database: '.mysql_error());
// If successfully queried
if($result_conf){
// Count how many row has this passkey
$count=mysql_num_rows($result_conf);
// if found this passkey in our database, retrieve data from table "t_awhole"
if($count==1){
$rows=mysql_fetch_array($result_conf);
$fN = $rows['fN']; // capitalizes the first letter (6-26-14)
$lN = $rows['lN']; // capitalizes the first letter (6-26-14)
$aI = $rows['aI'];
$eml = $rows['eml'];
$pss = $rows['pss'];
$pss2 = $rows['pss2'];
$a_whole_conf="a_whole";
// Insert data that retrieves from "t_awhole" into table "a_whole"
$sql_conf2= $conn->prepare("INSERT INTO $a_whole_conf(fN, lN, aI, eml, pss, admin)
VALUES ($fN, $lN, $aI, $eml, $pss, $admin)");
//>>>>Stops here<<<<<
$result_conf2=$sql_conf2->execute() or die ('Error updating database: '.mysql_error());
}
// if not found passkey, display message "Wrong Confirmation code"
else {
echo "Wrong Confirmation code";
}
// if successfully moved data from table"t_awhole" to table "a_whole" displays message "Your account has been activated" and don't forget to delete confirmation code from table "t_awhole"
if($result_conf2){
echo "Your account has been activated";
// Delete information of this user from table "t_awholeb" that has this passkey
$sql_conf3="DELETE FROM $t_awhole_conf WHERE confirm_code = '$pk'";
$result_conf3=mysql_query($sql_conf3);
}
}
?>
TO ANSWER THE QUESTION YOU ASKED
The colon (:) is used in SQL text to identify a placeholder for a named bind parameter within a prepared statement. That colon gets included in the SQL text, and value for that placeholder is provided when the SQL statement is executed.
The "money sign" identifies a PHP variable; in the string context, the variable is evaluated, and the value of the variable gets incorporated into the SQL text.
The following is not an answer to the question you asked. But I think this will answer several other questions you should be asking...
The inclusion of "unsafe" values in PHP variables is where the "SQL Injection" vulnerability comes in, if we don't have any guarantee that the value of the variables don't contain some characters that will be interpreted as SQL. And that's exactly why the mysql interface includes the mysql_real_escape_string function. This is a "wrapper" that inspects values, and properly escapes values so that they will be seen as values, and not be interpreted as SQL syntax.
Q: 1. The code stops where I have placed the comment //>>>>Stops here<<<<<.
A: Cool. It's not clear how you know the code "Stops here", if you're getting some sort of error or what.
We don't see any declaration or assignment for the reference to the $admin variable. We do see that we expected column pss2 to be retrieved by the SELECT. But we don't see anything be done with that, except to assign that to a PHP variable named $pss2, and that doesn't appear to be referenced anywhere else. Curious.
Q: 2. This is a email confirmation function, so t_awhole's table structure is different than a_whole. Everything is the same but in t_awhole the first column is a confirmation code associated to to that user. Once they click the link in their email, the row with that confirmation code should transfer all the data from that t_awhole into a_whole. However, it should not transfer the confirmation code, but should add a new column for the number of the row (first column) as an increment, and whether the user is an admin or not (last column). Everything else will stay as it was (fN, lN, aI, eml, pss) in between that first and last row.
A: Seems like an awkward design. It's not clear why you need to loop through all the individual rows returned by a SELECT (and your code is subject to SQL injection. Hopefully, "Little Bobby Tables" doesn't register... http://xkcd.com/327/)
I'm not sure why you don't just run a single INSERT .. SELECT statement to "copy" the rows from one table to the other in one fell swoop, for example:
$sql = "INSERT INTO a_whole
( fN , lN, aI, eml, pss, admin)
SELECT t.fN, t.lN, t.aI, t.eml, t.pss, '' AS admin
FROM t_awhole t
WHERE t.confirm_code = '" . mysql_real_escape_string($pk) "'";
(I don't see any declaration or assignment to $admin in the original code, so I replaced that reference with a literal string (zero length) in the example above.)
If you were going to do this with PDO, you could use a prepared statement with a bind placeholder. All of the SQL is the same, with the exception that we replace a reference to the value of the PHP $pk variable with a bind placeholder in the SQL text:
$sql = "INSERT INTO a_whole
( fN , lN, aI, eml, pss, admin)
SELECT t.fN, t.lN, t.aI, t.eml, t.pss, '' AS admin
FROM t_awhole t
WHERE t.confirm_code = :pk";
Now the SQL text is a constant string, and is not subject to SQL injection.
With PDO, you'd first call the prepare(), and then call bind_param() and execute(), e.g.
$sth = $dbh->prepare($sql);
$sth->bindParam(':pk', $pk, PDO::PARAM_INT);
$sth->execute();
BUT... to do that, you need to have a PDO connection (referenced as $dbh above); you can't use a mysql connection with PDO.
(If you don't check the result from each call, you'd want to set the error handling on the connection to throw an error, and use a try/catch block to catch any PDOException.)
UPDATE: actually, I see that your code only copies the first row returned from the SELECT, we don't see a normal while (fetch) loop we usually see. That was my oversight there, seeing something I expected but that wasn't there. That's my bad. Still, there's no need to retrieve the values into PHP variables, if all we are going to do with them is insert them into another table. Let the database do that, without mucking up the code with a bunch of variables we don't need.
Q: 3. Finally, I am using PDO as the structure. It was originally written in mysql. I notice a colon : is used instead of a money sign $. Why is this and where would I switch the : for the $ in my code?
A: The code modified in the edit, is now calling functions both from the deprecated mysql interface; and PDO (per the references to PDO functions.)
It's not valid to mix mysql interface functions and PDO interface functions. You can't use PDO functions on a connection obtained using the mysql interface, etc.
This is likely why your code "stops here".
(I was a little confused earlier; I didn't see the PDO functions, all I saw was mysql functions. We're not used to seeing mysql and PDO functions mixed like this, mostly because we never see this because it's not valid.)
TO ANSWER THE QUESTION YOU ASKED
The colon (:) is used in SQL text to identify a placeholder for a named bind parameter within a prepared statement. That colon gets included in the SQL text, and value for that placeholder is provided when the SQL statement is executed.
The "money sign" identifies a PHP variable; in the string context, the variable is evaluated, and the value of the variable gets incorporated into the SQL text. (This is where the "SQL Injection vulnerability comes in... we don't have any guarantee that the value of that variable doesn't contain text that will be interpreted as SQL.
And that's exactly why the mysql interface includes the mysql_real_escape_string function.
Related
Is the following update query a legal statement? It replaces the existing value with an empty value instead of the word gossamer. It does not fail as far as I can tell. It changes the value in the database from whatever it was before to empty.
$sqld = "UPDATE mynotes SET notes = 'GOSSAMER' WHERE id = '2039'";
$resupdate = mysql_query($sqld) or die(mysql_error());
if ($resupdate) {
$success=1;
$message .="success with update";
}
The query is part of an an API and it returns a result in JSON. While this makes debugging more time consuming, this should be besides the point. If the above is an entirely legal update statement, then at least I can rule out a syntax issue and search for the problem elsewhere.
I have verified that the above code does work in a standalone php file. Something else in code is causing the issue.
Yes, mysql is deprecated in favor of mysqli and PDO. But upgrading legacy site is not in job scope.
It replaces the existing value with an empty value instead of the word gossamer
Assuming this statement is accurate then either:
1) the attribute 'notes' is of type ENUM whose values do not include 'Gossamer'. But you didn't share the DDL for the table.
2) Your code is not executing the query you've shown us here - the query it is executing should be in your MySQL logs
I have the following MySQL that gets executed by a PHP script. To summarize, the script first clears the table "members". Then it opens a file called members.db and reads all the lines into an array, and then for each line in the array, it splits the line up into components and inserts each of those components as a new row into the "members" table.
The thing is, every time I execute the PHP script, some rows don't get inserted, and it's a different set of rows every time. For example, on one exection, 135 rows out of 144 might get inserted, and on the next execution, 137 will get inserted, and on the next, 129, etc. The rows that don't get inserted seem to vary randomly from one execution to the next, and I'm not changing anything in between.
What could cause this?
mysql_query("DELETE FROM members;");
mysql_query("ALTER TABLE members AUTO_INCREMENT=0;");
$lines = file('cgi-bin/sigphon/members.db', FILE_IGNORE_NEW_LINES);
for ($i=0; $i < count($lines); $i++){
$line = explode(';;', $lines[$i]);
$salt = openssl_random_pseudo_bytes(16);
$password = md5($line[11] . $salt);
$query = " INSERT INTO members (
surname,
names,
home,
affil,
country,
email,
private,
interest,
specialty,
jointime,
password,
salt)
VALUES (
'{$line[1]}',
'{$line[2]}',
'{$line[3]}',
'{$line[4]}',
'{$line[5]}',
'{$line[6]}',
'{$line[7]}',
'{$line[8]}',
'{$line[9]}',
'{$line[10]}',
'{$password}',
'{$salt}'
)";
mysql_query($query);
}
As comments above imply, you aren't escaping special characters in the text of your file. So any apostrophes for example would upset the SQL syntax. For example, what happens if someone is named O'Reilly?
INSERT INTO members (name, ...) VALUES ('O'Reilly', ...)
The apostrophe inside the string looks like it ends the string, which confuses MySQL.
You also aren't checking for success or failure of your call to mysql_query(), so you don't know how many are causing errors.
Always check the return value of mysql_query(). It return false to indicate an error, and then you should output mysql_error(). This is the most common beginner-level mistake when programming MySQL.
if (mysql_query($query) === false) {
die(mysql_error());
}
To fix this by escaping special characters, read the documentation and examples for mysql_real_escape_string().
As others have stated, using prepared statements with query parameters also solves the issues with special characters, without requiring escaping. Prepared statements aren't supported in the old ext/mysql API, that's why it's deprecated.
But a further suggestion for you that will solve the errors and make your data import much faster is to use LOAD DATA INFILE. I tested the following:
mysql> LOAD DATA LOCAL INFILE 'members.db' INTO TABLE members FIELDS TERMINATED BY ';;'
(surname, names, home, affil, country, email, private, interest, specialty, jointime, password)
SET salt = UNHEX(MD5(RAND())), password = MD5(CONCAT(password, salt));
I also agree with others that MD5 is not considered best practice for hashing passwords. Use SHA2() instead.
I have a database. I had created a a table containing only one row in DB if it wasn't constructed before.
Why it has only 1 row is that I just use it to keep some info.
There is a field of TYPE NVARCHAR(100) which I want to use it to store session id,
and here comes the headache for me:
It seems that I can't even properly INSERT(I use phpmyadmin to check and it's blank) and UPDATE(syntax error...) it with a session id obtained from session_id(), which is returned as a string.
Here is the portion of my code relating to my action:
//uamip,uamport is in URL;I use $_GET[]
$_SESSION[uamport] = $_GET['uamport'];
$_SESSION[uamip] = $_GET['uamip'];
**$_SESSION[sid] = session_id();**
//construct
$sql="CREATE TABLE trans_vector(
`index` INT NOT NULL AUTO_INCREMENT,
`sid` NVARCHAR(100),
`uamip` CHAR(15),
`uamport` INT,
PRIMARY KEY (`index`)
)" ;
mysql_query($sql);
//insert(first time, so not constructed)
$sql="INSERT INTO trans_vector (sid,uamip,uamport) VALUES(
'$_SESSION[sid]',
'$_SESSION[myuamip]',
'$_SESSION[myuamport]'
)";
mysql_query($sql);
//update(from 2nd time and later, table exists, so I want to update the sid part)
$sql="UPDATE trans_vector SET sid="**.**$_SESSION[sid];
mysql_query($sql)
Now, when I use phpmyadmin to check the sid field after INSERT or UPDATE, It is blank;
But if I do this:
$vector=mysql_fetch_array(mysql_query("SELECT TABLES LIKE 'trans_vector'"));
and echo $vector[sid] ,then it's printed on webpage.
Another question is:
With the UPDATE statement above, I always get such error:
"Unknown column xxxxxx....(some session id returned, it seems it always translate it first and put it in the SQL statement, ** treating it as a column NAME** that's not what I want!)"
I tried some TYPE in CREATE statement, and also lots of syntax of the UPDATE statement(everything!!!) but it always give this error.
I am dealing trouble with ' and string representation containing a variable where the latter's value is actually what I want... and maybe the problem arise from type in CREATE and string representation in UPDATE statement?
Should CAST() statement helpful for me?
Wish you can help me deal with this...and probably list some real reference of such issue in PHP?
Thanks so much!!
$insert = "INSERT INTO trans_vector (`sid`, `uamip`, `uamport`) VALUES(
'".$_SESSION["sid"]."',
'".$_SESSION["myuamip"]."',
'".$_SESSION["myuamport"]."'
)";
this should solve at least some warnings, if not errors.
and for update...
$update = "UPDATE trans_vector SET `sid`='".$_SESSION["sid"]."';";
Notes about your code:
Array values have to be put into the string with operator '.' and cannot be inserted directly. Array indexes must be strings (note the ") or integers.
Column names should have `` around them. To insert a string with SQL, you have to put string into ''s, so the parser knows what is string and what column name. Without ''s parser is assuming you are stating a column.
and for mysql_escape_string, I assumed you handle that before storing data to sessions. Without those, you might can get unwanted SQL injections. And in case you did not do that, you can either do that (before you create queries):
foreach($_SESSION as $key => $value)
$_SESSION[$key] = mysql_escape_string($value);
or manually escape strings when you create a query.
As for the update statement, it’s clear that there are apostrophes missing. You always need apostrophes, when you want to insert a string value into the database. Moreover, you should use mysql_real_escape_string.
However, I think standard mysql is deprecated and has been removed in newer versions of PHP in favor of MySQLi and PDO. Thus you should switch to MySQLi or PDO soon.
You should also use apostrophes when referencing values within $_SESSION. Otherwise PHP will try to find a constanst with the name sid and later fallback to the string 'sid'. You will get into trouble if there once really is a constant called sid defined.
Here, the corrected update statement in mysql library:
$sql = "UPDATE trans_vector SET sid='" . mysql_real_escape_string($_SESSION['sid']) . "'";
Even better:
$sql = "UPDATE `trans_vector` SET `sid`='" . mysql_real_escape_string($_SESSION['sid']) . "'";
Using backticks makes clear for MySQL that this is a column name. Sometimes you will have column names that are called like reserved keywords in SQL. Then you will need apostrophes. A common example is a column called order for the sequence of entries.
I've user profile update page and have some forms to update, here they are
NAME
SURNAME
password
phone
And I am trying to make this update without big script, I mean I don't want to define if for example NAME exists or not and so on. I want that if any marked form value exists it changed in mysql. How I know this is possible with mysqli_prepare statement. I've written sql like this
$stmt = "UPDATE table SET NAME=?,SURNAME=?,PASSWORD=?,PHONE=? WHERE email='" . $email . "'";
but something wrong, any ideas how to do it ? And also please advice why it is better way to use mysqli_prepare , why it is safe too ?
PS. I do not write php script because I've not any problem with it
UPDATE
I've marked sql statement and above this script in php I am writting this =>
if (isset($_POST['name']){
$name = $_POST['name'];
} else {
$name = null;
}
and so on ...
but it doesn't execute , nothing error msg is shown up , because I think something wrong with sql statement
Just want if some of detail is filled it updated and if all fields are filled all updated, how to write with script?
I can not understand this question marks in sql statement , does it means that if for example NAME is not exists it is ignored ?
The question marks in your SQL string not part of the SQL syntax, they are placeholders for the actual parameters. If you want to do it like this, you should first make a SQL statement, and then set the parameters.
Something like
$con = new mysqli($hostname,$username,$password,$database);
$statement = $con->prepare( "UPDATE table SET NAME=?,SURNAME=?,".
"`PASSWORD`=?,PHONE=? ".
" WHERE email=?");
$statement->bind_param("sssss",$name,$surname,$pass,$phone,$email);
example derived of http://www.xphp.info/security/getting-started-with-mysqli/
Also note the comment of ThiefMaster: password is a reserved word in MySQL so you will need to put it in backticks (``)
Alternatively you directly insert the values into the mysql string, like you initially did with the email address. You need to escape the values in that case, by using mysql_real_escape_string()
Note that you are in both cases replacing ALL values with what was set, be it NULL or a string, or whatever.
I know that mysql_real_escape_string()
prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a
I know how this protects a query from injection into something like a variable in a where clause. But here's a scenario I am unsure of:
$query = "SELECT * FROM $db WHERE 1";
If $db is taken from a user input, then the user could insert something like:
$db = 'RealDatabase WHERE 1; DELETE FROM RealDatabase WHERE 1; SELECT FROM RealDatabase';
From my understanding, mysql_real_escape_string() would not affect this string,
making the final query:
$query = "SELECT * FROM RealDatabase WHERE 1; DELETE FROM RealDatabase WHERE 1; SELECT FROM RealDatabase WHERE 1";
which would delete the database. Is there another level of protection I am unaware of?
The level of protection you are looking for is supplied by backticks:
"SELECT * FROM `$db` WHERE 1";
Backticks are used to qualify identifiers that could otherwise be ambiguous (ie. MySQL reserved words), and if you are accepting user input or have variably-named columns or databases, you absolutely should use backticks, or I can promise that you will run into trouble in the future. For example, what if you had a system where a temporary field name was created with some user input, only it turned out the field ended up being named update?
"SELECT field1,field2,update FROM table;"
It fails miserably. However:
"SELECT `field`,`field2`,`update` FROM table"
works just fine. (This is actually a real example from a system I worked on a few years ago that had this problem).
This solves your problem in terms of putting in bad SQL. For instance, the following query will simply return an "unknown column" error, where test; DROP TABLE test is the injected attack code:
"SELECT * FROM `test; DROP TABLE test`;"
Be careful though: SQL Injection is still possible with backticks!
For instance, if your $db variable contained data that had a backtick in it, you could still inject some SQL in the normal way. If you're using variable data for database and field names, you should strip it of all backticks before putting it into your statement, and then qualifying it with backticks once inside.
$db = str_replace('`','',$db);
$sql = "SELECT * FROM `$db` WHERE 1";
I utilize a database wrapper which has separate functions for sanitizing data and sanitizing database identifiers, and this is what the latter does :)
You should really look into binding your SQL queries.
This will protect you from basically all SQL injection. It boils down to this:
(taken from PHP.net)
$stmt = mssql_init('NewUserRecord');
// Bind the field names
mssql_bind($stmt, '#username', 'Kalle', SQLVARCHAR, false, false, 60);
// Execute
mssql_execute($stmt);
And PHP has support for binded queries on basically all databases. Oh and of course you should still sanitize all input & output(display).
More info:
- http://php.net/manual/en/function.mssql-bind.php
No, mysql_real_escape_string isn't going to help you here. The function is not context-sensitive (it can't be, because it doesn't HAVE any context), and this is a completely different threat model.
You need to go and validate that the table exists, without sending the user-inputted table name directly to the server. The best solution is to use a server-side array/look-up table containing the table names they are allowed to use. If they try to use something that's not in there, then don't let them.
If you really need ALL of the tables, then you can just ask the server "what tables do you have?" and run through it's output (optionally caching it for some period of time to prevent asking the server every time) - but chances are, eventually you'll have a table that you don't want then to poke around in, and then you need to use the array thing anyway, so just go ahead and do that.
Instead of inserting the database name in the get query you can make a separate table of database names and ids. Then append only the id to the query. Then you can look up the corresponding database name for that id and use that. You can then make sure that the id received is numeric (is_numeric) and you can also be certain that the user can only choose from the databases that are in your list.
(Additionally this will prevent users from finding out names of databases and possibly use them elsewhere in an SQL injection on your site.)
Using the first method you parse the database name before using it in your query and make sure it contains no spaces.
Since table names do not accept whitespace characters, just strip them out. That would make the above $DB RealDatabaseWHERE1;DELETEFROMRealDatabase..... Such would invalidate the query, but prevent the flaw.
If you want to prevent this kind of 'hackish' things, just do explode(' ', $db) then get the result array's [0]. That would get the first part (RealDatabase) and nothing else.
Its just best to use it any time that you have questionable data being used. If you are specifying the table yourself and there's no room for tampering, there's no need to escape it. If your users are deciding anything that could potentially get run as a query, escape it.
If you really really must use a get from the user (bad bad bad) for your database then use the following style of coding...
$realname = '';
switch ($_GET['dbname']){
case 'sometoken' : $realname = 'real_name'; break;
case 'sometoken1' : $realname = 'real_name1'; break;
case 'sometoken2' : $realname = 'real_name2'; break;
case 'sometoken3' : $realname = 'real_name3'; break;
case 'sometoken4' : $realname = 'real_name4'; break;
case default : die ('Cheeky!!!');
}
$query = "SELECT * FROM `{$realname}` WHERE 1";
or alternatively ...
$realname = $tablenames[$_GET['dbname']];
if (!$realname)
die ('Cheeky!!!');
Using these 2 ways or some similar coding will protect your input from unexpected values.
It also means the user never gets to see the real table or database names which they may be able to infer information from.
Make sure you check the content of $_GET['dbname'] to make sure it's valid first otherwise warnings will be issued.
I still say this is a very bad design, it is reminiscent of allowing users to provide a filename and passing that through to I/O functions without a check. It simply too unsafe to consider.
Security is too important to let laziness rule.