This code works and adds tables to my database. My question is how do I protect it with prepared statements.
require "conn.php";
$MyServer =($_POST["username"]);
$sql = ("CREATE TABLE $MyServer (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(30) NOT NULL
)");
if($conn->query($sql) === TRUE){
echo "Table created successfully";
}
I am using MySQLi.
I tryed this and it isn't adding the table.
$MyServer =($_POST["username"]);
if (!preg_match('^/[A-Za-z][A-Za-z0-9]{0,7}$/', $MyServer)) {
throw new Exception ('username unsuitable for use as a table name');
}
$sql = ("CREATE TABLE `$MyServer` (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(30) NOT NULL
)");
if($conn->query($sql) === TRUE){
echo "Table created successfully";
} else {
echo "Table is not created successfully ";
}
I guess you're thinking about how to avoid SQL injection into your query via that $MyServer variable in your sample program.
You cannot use a parameterized value to name a table (or a database, or a column) in SQL. You must do the variable substitution shown in your program.
You can use php to sanitize your $MyServer variable before you use it for subsitution though.
For example: How to check, if a php string contains only english letters and digits?
You could do this, or something like it. This requires the variable to start with a letter, then contain up to seven more characters that are letters or digits. If the variable doesn't match it throws an exception.
if (!preg_match('^/[A-Za-z][A-Za-z0-9]{0,7}$/', $MyServer)) {
throw new Exception ('username unsuitable for use as a table name');
}
Although it is questionable whether it is a good plan to allow users to create tables, to answer your question:
First of all, make sure your variable doesn't contain any strange character. Although MySQL allows (a subset of) unicode characters, you probably only want normal letters and numbers:
if (
!preg_match('/^[a-z0-9]+$/i', $MyServer)
|| preg_match(/^[0-9]+$/, $MyServer) // Identifiers may begin with a digit but unless quoted may not consist solely of digits.
|| strlen($MyServer) > 64 // Limit of table-name length
) {
// Insert your own error handling
die('Not allowed');
}
Second, to make sure SQL treat it as a identifier, quote it in backticks
$sql = "CREATE TABLE `$MyServer` (...etc..."
CREATE TABLE index (...etc... will raise an error in MySQL because index is a keyword
CREATE TABLE `index` (...etc...
wont, because it is marked as an identifier.
Honestly, you are really opening yourself up to SQL injection here by getting the data from $_POST.
Your method is a definite no go unless your usernames are already stored in your database and do not have special characters that will lead to SQL injection (such as quotations).
EDIT: I see two answers above that compliment what I have said, you could use one of those two answers (O. Jones is my preferred one).
If you want your code to be a bit more in line with PDO procedure (binding PDO values to avoid SQL injection), why not create one table with columns (username, saved_username) and you can interact with the information in that table effectively with PDO statements.
For example, all you will have to do to insert data would be:
$query = 'INSERT INTO table (username, saved_username) VALUES (:username, :username_to_save)';
$stmt->bindParam(':username', $_POST['username']);
$stmt->bindParam(':username_to_save', $_POST['username_to_save']);
$stmt->execute()
And to select the data:
$query = 'SELECT * FROM table WHERE username = :username';
$stmt->bindParam(':username', $_POST['username']);
$stmt->execute()
$users_saved_usernames = $stmt->fetchAll();
Related
I have a table called user_bio. I have manually entered one row for username conor:
id: 1
age: 30
studying: Business
language: English
relationship_status: Single
username: conor
about_me: This is conor's bio.
A bio is unique to a user, and obviously, a user cannot manually set their bio from inserting it in the database. Consider the following scenario's:
Logged in as Conor. Since Conor already has a row in the database, I simply want to run an UPDATE query to update the field where username is equal to conor.
Logged in as Alice. Since Alice has no row in the database corresponding to her username. Then I want to run an INSERT query. For all users, all users will have to have their details inputted, and then updated correspondingly.
At the moment, I am struggling with inserting data in the database when no rows exist in the database.
Here is my current approach:
$about_me = htmlentities(trim(strip_tags(#$_POST['biotextarea'])));
$new_studying = htmlentities(trim(strip_tags(#$_POST['studying'])));
$new_lang = htmlentities(trim(strip_tags(#$_POST['lang'])));
$new_rel = htmlentities(strip_tags(#$_POST['rel']));
if(isset($_POST['update_data'])){
// need to check if the username has data already in the db, if so, then we update the data, otherwise we insert data.
$get_bio = mysqli_query($connect, "SELECT * FROM user_bio WHERE username ='$username'");
$row_returned = mysqli_num_rows($get_bio);
$get_row = mysqli_fetch_assoc ($get_bio);
$u_name = $get_row['username'];
if ($u_name == $username){
$update_details_query = mysqli_query ($connect, "UPDATE user_bio SET studying ='$new_studying', language ='$new_lang',
relationship_status = '$new_rel', about_me = '$about_me' WHERE username ='$username'");
echo " <div class='details_updated'>
<p> Details updated successfully! </p>
</div>";
} else {
$insert_query = mysqli_query ($connect, "INSERT INTO user_bio
VALUES ('', '$age','$new_studying','$new_lang','$new_rel', '$username', '$about_me'");
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
echo " <div class='details_updated'>
<p> Details added successfully! $row_returned </p>
</div>";
}
}
The UPDATE query works fine, when logged in as Conor. But again, INSERT does not work when logged in as Alice.
MySQL support INSERT ... ON DUPLICATE KEY UPDATE type of queries. So you do not need to make few queries to check existance of row in your php code, just add corrct indexes and let your DB take care about this.
You can read about such type of queries here
Here are a few things you could do to make it work:
Prevent SQL injection
As this is an important issue, and the suggested corrections provided below depend on this point, I mention it as the first issue to fix:
You should use prepared statements instead of injecting user-provided data directly in SQL, as this makes your application vulnerable for SQL injection. Any dynamic arguments can be passed to the SQL engine aside from the SQL string, so that there is no injection happening.
Reduce the number of queries
You do not need to first query whether the user has already a bio record. You can perform the update immediately and then count the records that have been updated. If none, you can then issue the insert statement.
With the INSERT ... ON DUPLICATE KEY UPDATE Syntax, you could further reduce the remaining two queries to one. It would look like this (prepared):
INSERT INTO user_bio(age, studying, language,
relationship_status, username, about_me)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY
UPDATE studying = VALUES(studying),
language = VALUES(language),
relationship_status = VALUES(relationship_status),
about_me = VALUES(about_me);
This works only if you have a unique key constraint on username (which you should have).
With this statement you'll benefit from having the data modification executed in one transaction.
Also take note of some considerations listed in the above mentioned documentation.
NB: As in comments you indicated that you prefer not to go with the ON DUPLICATE KEY UPDATE syntax, I will not use it in the suggested code below, but use the 2-query option. Still, I would suggest you give the ON DUPLICATE KEY UPDATE construct a go. The benefits are non-negligible.
Specify the columns you insert
Your INSERT statement might have failed because of:
the (empty) string value you provided for what might be an AUTO_INCREMENT key, in which case you get an error like:
Incorrect integer value: '' for column 'id'
a missing column value, i.e. when there are more columns in the table than that you provided values for.
It is anyway better to specify explicitly the list of columns in an INSERT statement, and to not include the auto incremented column, like this:
INSERT INTO user_bio(age, studying, language,
relationship_status, username, about_me)
VALUES (?, ?, ?, ?, ?, ?)
Make sure you get notified about errors
You might also have missed the above (or other) error, as you set your error reporting options only after having executed your queries. So execute that line before doing any query:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
And also add there:
error_reporting(E_ALL);
ini_set('display_errors', 1);
In a production environment you should probably pay some more attention to solid error reporting, as you don't want to reveal technical information in the client in such an environment. But during development you should make sure that (unexpected) errors do not go unnoticed.
Do not store HTML entities in the database
It would be better not to store HTML entities in your database. They are specific to HTML, which your database should be independent of.
Instead, insert these entities (if needed) upon retrieval of the data.
In the below code, I removed the calls to htmlentities, but you should then add them in code where you SELECT and display these values.
Separate view from model
This is a topic on its own, but you should avoid echo statements that are inter-weaved with your database access code. Putting status in variables instead of displaying them on the spot might be a first step in the right direction.
Suggested code
Here is some (untested) code which implements most of the above mentioned issues.
// Calls to htmlentities removed:
$about_me = trim(strip_tags(#$_POST['biotextarea']));
$new_studying = trim(strip_tags(#$_POST['studying']));
$new_lang = trim(strip_tags(#$_POST['lang']));
$new_rel = trim(strip_tags(#$_POST['rel']));
// Set the error reporting options at the start
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
if (isset($_POST['update_data'])) {
// Do not query for existence, but make the update immediately
$update_stmt = mysqli_prepare ($connect,
"UPDATE user_bio
SET studying = ?,
language = ?,
relationship_status = ?,
about_me = ?
WHERE username = ?");
mysqli_stmt_bind_param($update_stmt, "sssss",
$new_studying, $new_lang, $new_rel, $about_me, $username);
mysqli_stmt_execute($update_stmt);
$num_updated_rows = mysqli_stmt_affected_rows($update_stmt);
mysqli_stmt_close($update_stmt);
if ($num_updated_rows === 0) {
$insert_stmt = mysqli_prepare ($connect,
"INSERT INTO user_bio(age, studying, language,
relationship_status, username, about_me)
VALUES (?, ?, ?, ?, ?, ?)");
mysqli_stmt_bind_param($insert_stmt, "isssss",
$age, $new_studying, $new_lang, $new_rel, $username, $about_me);
mysqli_stmt_execute($insert_stmt);
mysqli_stmt_close($insert_stmt);
}
// Separate section for output
$result = $num_updated_rows ? "Details updated successfully!"
: "Details added successfully!";
echo " <div class='details_updated'><p>$result</p></div>";
}
Aside from security issues and bad coding practices, here are a couple things you can do.
You don't need to compare the name to check if the bio already exists. You can just count the number of rows returned. If it is more than zero, then the user bio already exists.
When comparing strings, === is preferred over ==. You can read about it and find out why but here is an example (2nd answer)
You should really look into either REPLACE INTO or ON DUPLICATE KEY UPDATE. Just using either of there 2, depending on your use case pick one, you can pretty much eliminate more than half of your currently displayed code. Basically, both will insert and if the record already exists, they updates. Thus, you wouldn't even need to check if the record already exists.
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.
If we can't use PDO or mysqli (for any reason), is this method safe for INSERT and SELECT?
<?php
if (!empty($_POST[id]) && !empty($_POST[name])) {
require_once ( 'config.php' );
// SAFE INTVAL ID
$id = intval($_POST[id]);
$connect = mysql_connect("$server", "$user", "$password")
OR die(mysql_error());
mysql_select_db("$database", $connect);
// ESCAPING NAME
$name = mysql_real_escape_string($_POST[name]);
$query = "INSERT INTO table (id, name) VALUES ('$id', '$name')";
$result = mysql_query($query, $connect);
if (!$result) { echo 'success'; } else { echo 'fail'; }
}
?>
cause i've read many times never to use mysql_query,
is it dangerous even if we are careful and escape in time?
As per my knowledge, your query is perfectly fine.
You are escaping the SQL with
mysql_real_escape_string($_POST[name])
This adds additional security to your code.
The only suggestion is that use:
$_POST['name']
instead of
$_POST[name]
As it will generate PHP warning.
Thanks.
To add to the other answers, it's "safe", as in the query can't be exploited. The one thing to watch out for though is that you're trusting your users to provide you with an ID (which I assume here is your primary key). Of course, this means that your users can overwrite other records.
A better way would be to omit the id column (and its value) from your query, and mark the column as AUTO_INCREMENT when creating the table. Any omitted value from a query becomes its default value, which in this case will normally be the last value of id+1.
Even though you say you can't use them, possibly because they're too complicated (?), you really should doing a little research and understanding how to use them. I promise that once you do, you won't even want to go back! :) I recommend using PDO / MySQLi because PHP 5.5 is depreciating MySQL and you'll get E_DEPRECIATED notices.
Prepared statements using MySQLi or PDO mean that you don't have to escape any strings, you simply refer to each variable with a ?, and then state later on what datatype the ? has s being string, for example.
You wouldn't need to use mysql_real_escape_string() then. Future proof your code! :)
I'm trying to write a User Login System for my website. I'm running WAMP on my laptop and using Aptana for development. I've been stuck trying to get a User Creation function to work for about 3 days now. This function here:
function create_new_user($email, $pass, $level){
$db = new PDO('mysql:host=localhost;dbname=jadams', 'root', '');
$insertQuery = "INSERT INTO jadams.user VALUES ($email, $pass, $level);";
$db->query($insertQuery);
return TRUE;
}
I have rewritten this function several times, using prepared statements and various forms of conditional checks, this is just the simplest one in the hopes of figuring it out. No matter what I try I cannot get the insertion into the database to work. I have gotten this login function working by forcibly inserting users through phpMyAdmin:
function is_pass_correct($email, $pass){
$db = new PDO('mysql:host=localhost;dbname=jadams', 'root', '');
$email = $db->quote($email);
$selectQuery = "SELECT password FROM jadams.user WHERE email = $email;";
$rows = $db->query($selectQuery);
$db=NULL;
if($rows) {
foreach ($rows as $row) {
if($pass === $row["password"]) {return TRUE;} //MD5
}
}
return FALSE;
}
The structure of my Database is email varchar(256) not null primary, password varchar(256) not null, access int; I have also tried the query with and without a semicolon.
You're missing the column names in which to insert the values.
"INSERT INTO jadams.user (email, password, level) VALUES ($email, $pass, $level);"
Also, since you're using the PDO library consider using prepared statements to escape untrusted data.
$insertQuery = "INSERT INTO jadams.user (email, password, level)
VALUES (:email, :password, :level)";
$db = $conn->prepare($insertQuery);
$db->execute(array(':email'=>$email,
':password'=>$pass,
':level'=>$level));
Are you getting an error?
It's hard to diagnose without knowing the full DB structure, but at first blush it looks like maybe the columns in that table do not match up with the values you provide.
Technically, the column names are not required, but if you do not supply them then you must have appropriate values for each column in order. If there is a userID or other field that you are not setting, that could be the issue.
From the manual:
If you do not specify a list of column names for INSERT ... VALUES or INSERT ... SELECT, values for every column in the table must be provided by the VALUES list or the SELECT statement.
To be on the safe side, I would suggest explicitly setting the column names like so:
INSERT INTO
jadams.user (email, password, level)
VALUES ($email, $pass, $level)
Personally I prefer the INSERT INTO ... SET syntax. It feels more readable and less prone to mixing up columns.
INSERT INTO
jadams.user
SET
email = $email,
password = $password
level = $level
Of course, this doesn't get into parameter binding, password storage, and a whole host of other issues you'll also want to be thinking about.
It's also possible that the semicolon at the end of your query is causing an issue. I know for the old mysql_* functions the manual explicitly stated that you should not have a semicolon at the end of a query. However, there's nothing about that in PDO. I assume it's fine to have a semicolon at the end now but I would try removing it and see what happens. (Probably nothing).
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.