sql injection operator explanation [duplicate] - mysql

Just looking at:
(Source: https://xkcd.com/327/)
What does this SQL do:
Robert'); DROP TABLE STUDENTS; --
I know both ' and -- are for comments, but doesn't the word DROP get commented as well since it is part of the same line?

It drops the students table.
The original code in the school's program probably looks something like
q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";
This is the naive way to add text input into a query, and is very bad, as you will see.
After the values from the first name, middle name textbox FNMName.Text (which is Robert'); DROP TABLE STUDENTS; --) and the last name textbox LName.Text (let's call it Derper) are concatenated with the rest of the query, the result is now actually two queries separated by the statement terminator (semicolon). The second query has been injected into the first. When the code executes this query against the database, it will look like this
INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')
which, in plain English, roughly translates to the two queries:
Add a new record to the Students table with a Name value of 'Robert'
and
Delete the Students table
Everything past the second query is marked as a comment: --', 'Derper')
The ' in the student's name is not a comment, it's the closing string delimiter. Since the student's name is a string, it's needed syntactically to complete the hypothetical query. Injection attacks only work when the SQL query they inject results in valid SQL.
Edited again as per dan04's astute comment

Let's say the name was used in a variable, $Name. You then run this query:
INSERT INTO Students VALUES ( '$Name' )
The code is mistakenly placing anything the user supplied as the variable. You wanted the SQL to be:
INSERT INTO Students VALUES ( 'Robert Tables` )
But a clever user can supply whatever they want:
INSERT INTO Students VALUES ( 'Robert'); DROP TABLE Students; --' )
What you get is:
INSERT INTO Students VALUES ( 'Robert' ); DROP TABLE STUDENTS; --' )
The -- only comments the remainder of the line.

As everyone else has pointed out already, the '); closes the original statement and then a second statement follows. Most frameworks, including languages like PHP, have default security settings by now that don't allow multiple statements in one SQL string. In PHP, for example, you can only run multiple statements in one SQL string by using the mysqli_multi_query function.
You can, however, manipulate an existing SQL statement via SQL injection without having to add a second statement. Let's say you have a login system which checks a username and a password with this simple select:
$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);
If you provide peter as the username and secret as the password, the resulting SQL string would look like this:
SELECT * FROM users WHERE username='peter' and (password='secret')
Everything's fine. Now imagine you provide this string as the password:
' OR '1'='1
Then the resulting SQL string would be this:
SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')
That would enable you to log in to any account without knowing the password. So you don't need to be able to use two statements in order to use SQL injection, although you can do more destructive things if you are able to supply multiple statements.

No, ' isn't a comment in SQL, but a delimiter.
Mom supposed the database programmer made a request looking like:
INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');
(for example) to add the new student, where the $xxx variable contents was taken directly out of an HTML form, without checking format nor escaping special characters.
So if $firstName contains Robert'); DROP TABLE students; -- the database program will execute the following request directly on the DB:
INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');
ie. it will terminate early the insert statement, execute whatever malicious code the cracker wants, then comment out whatever remainder of code there might be.
Mmm, I am too slow, I see already 8 answers before mine in the orange band... :-) A popular topic, it seems.

TL;DR
-- The application accepts input, in this case 'Nancy', without attempting to
-- sanitize the input, such as by escaping special characters
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1
-- SQL injection occurs when input into a database command is manipulated to
-- cause the database server to execute arbitrary SQL
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE
-- The student records are now gone - it could have been even worse!
school=> SELECT * FROM students;
ERROR: relation "students" does not exist
LINE 1: SELECT * FROM students;
^
This drops (deletes) the student table.
(All code examples in this answer were run on a PostgreSQL 9.1.2 database server.)
To make it clear what's happening, let's try this with a simple table containing only the name field and add a single row:
school=> CREATE TABLE students (name TEXT PRIMARY KEY);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "students_pkey" for table "students"
CREATE TABLE
school=> INSERT INTO students VALUES ('John');
INSERT 0 1
Let's assume the application uses the following SQL to insert data into the table:
INSERT INTO students VALUES ('foobar');
Replace foobar with the actual name of the student. A normal insert operation would look like this:
-- Input: Nancy
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1
When we query the table, we get this:
school=> SELECT * FROM students;
name
-------
John
Nancy
(2 rows)
What happens when we insert Little Bobby Tables's name into the table?
-- Input: Robert'); DROP TABLE students; --
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE
The SQL injection here is the result of the name of the student terminating the statement and including a separate DROP TABLE command; the two dashes at the end of the input are intended to comment out any leftover code that would otherwise cause an error. The last line of the output confirms that the database server has dropped the table.
It's important to notice that during the INSERT operation the application isn't checking the input for any special characters, and is therefore allowing arbitrary input to be entered into the SQL command. This means that a malicious user can insert, into a field normally intended for user input, special symbols such as quotes along with arbitrary SQL code to cause the database system to execute it, hence SQL injection.
The result?
school=> SELECT * FROM students;
ERROR: relation "students" does not exist
LINE 1: SELECT * FROM students;
^
SQL injection is the database equivalent of a remote arbitrary code execution vulnerability in an operating system or application. The potential impact of a successful SQL injection attack cannot be underestimated--depending on the database system and application configuration, it can be used by an attacker to cause data loss (as in this case), gain unauthorized access to data, or even execute arbitrary code on the host machine itself.
As noted by the XKCD comic, one way of protecting against SQL injection attacks is to sanitize database inputs, such as by escaping special characters, so that they cannot modify the underlying SQL command and therefore cannot cause execution of arbitrary SQL code. This can be done at the application level, and some implementations of parameterized queries operate by sanitizing input.
However, sanitizing inputs at the application level may not stop more advanced SQL injection techniques. For example, there are ways to circumvent the mysql_real_escape_string PHP function. For added protection, many database systems support prepared statements. If properly implemented in the backend, prepared statements can make SQL injection impossible by treating data inputs as semantically separate from the rest of the command.

Say you naively wrote a student creation method like this:
void createStudent(String name) {
database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}
And someone enters the name Robert'); DROP TABLE STUDENTS; --
What gets run on the database is this query:
INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')
The semicolon ends the insert command and starts another; the -- comments out the rest of the line. The DROP TABLE command is executed...
This is why bind parameters are a good thing.

A single quote is the start and end of a string. A semicolon is the end of a statement. So if they were doing a select like this:
Select *
From Students
Where (Name = '<NameGetsInsertedHere>')
The SQL would become:
Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
-- ^-------------------------------^
On some systems, the select would get ran first followed by the drop statement! The message is: DONT EMBED VALUES INTO YOUR SQL. Instead use parameters!

The '); ends the query, it doesn't start a comment. Then it drops the students table and comments the rest of the query that was supposed to be executed.

In this case, ' is not a comment character. It's used to delimit string literals. The comic artist is banking on the idea that the school in question has dynamic sql somewhere that looks something like this:
$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";
So now the ' character ends the string literal before the programmer was expecting it. Combined with the ; character to end the statement, an attacker can now add (inject) whatever sql they want. The -- comment at the end is to make sure any remaining sql in the original statement does not prevent the query from compiling on the server.
FWIW, I also think the comic in question has an important detail wrong: if you sanitize your database inputs, as the comic suggests, you're still doing it wrong. Instead, you should think in terms of quarantining your database inputs, and the correct way to do this is via parameterized queries/prepared statements.

The writer of the database probably did a
sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff";
execute(sql);
If student_name is the one given, that does the selection with the name "Robert" and then drops the table. The "-- " part changes the rest of the given query into a comment.

The ' character in SQL is used for string constants. In this case it is used for ending the string constant and not for comment.

This is how it works:
Lets suppose the administrator is looking for records of student
Robert'); DROP TABLE STUDENTS; --
Since the admin account has high privileges deleting the table from this account is possible.
The code to retrieve user name from request is
Now the query would be something like this (to search the student table)
String query="Select * from student where username='"+student_name+"'";
statement.executeQuery(query); //Rest of the code follows
The resultant query becomes
Select * from student where username='Robert'); DROP TABLE STUDENTS; --
Since the user input is not sanitized, The above query has is manipulated into 2 parts
Select * from student where username='Robert');
DROP TABLE STUDENTS; --
The double dash (--) will just comment out remaining part of the query.
This is dangerous as it can nullify password authentication, if present
The first one will do the normal search.
The second one will drop the table student if the account has sufficient privileges (Generally the school admin account will run such query and will have the privileges talked about above).

You don't need to input form data to make SQL injection.
No one pointed this out before so through I might alert some of you.
Mostly we will try to patch forms input. But this is not the only place where you can get attacked with SQL injection. You can do very simple attack with URL which send data through GET request;
Consider the fallowing example:
show something
Your url would look
http://yoursite.com/show?id=1
Now someone could try something like this
http://yoursite.com/show?id=1;TRUNCATE table_name
Try to replace table_name with the real table name. If he get your table name right they would empty your table! (It is very easy to brut force this URL with simple script)
Your query would look something like this...
"SELECT * FROM page WHERE id = 4;TRUNCATE page"
Example of PHP vulnerable code using PDO:
<?php
...
$id = $_GET['id'];
$pdo = new PDO($database_dsn, $database_user, $database_pass);
$query = "SELECT * FROM page WHERE id = {$id}";
$stmt = $pdo->query($query);
$data = $stmt->fetch();
/************* You have lost your data!!! :( *************/
...
Solution - use PDO prepare() & bindParam() methods:
<?php
...
$id = $_GET['id'];
$query = 'SELECT * FROM page WHERE id = :idVal';
$stmt = $pdo->prepare($query);
$stmt->bindParam('idVal', $id, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetch();
/************* Your data is safe! :) *************/
...

Related

Rails - How to reference model's own column value during update statement?

Is it possible to achieve something like this?
Suppose name and plural_name are fields of Animal's table.
Suppose pluralise_animal is a helper function which takes a string and returns its plural literal.
I cannot loop over the animal records for technical reasons.
This is just an example
Animal.update_all("plural_name = ?", pluralise_animal("I WANT THE ANIMAL NAME HERE, the `name` column's value"))
I want something similar to how you can use functions in MySQL while modifying column values. Is this out-of-scope or possible?
UPDATE animals SET plural_name = CONCAT(name, 's') -- just an example to explain what I mean by referencing a column. I'm aware of the problems in this example.
Thanks in advance
I cannot loop over the animal records for technical reasons.
Sorry, this cannot be done with this restriction.
If your pluralizing helper function is implemented in the client, then you have to fetch data values back to the client, pluralize them, and then post them back to the database.
If you want the UPDATE to run against a set of rows without fetching data values back to the client, then you must implement the pluralization logic in an SQL expression, or a stored function or something.
UPDATE statements run in the database engine. They cannot call functions in the client.
Use a ruby script to generate a SQL script that INSERTS the plural values into a temp table
File.open(filename, 'w') do |file|
file.puts "CREATE TEMPORARY TABLE pluralised_animals(id INT, plural varchar(50));"
file.puts "INSERT INTO pluralised_animals(id, plural) VALUES"
Animal.each.do |animal|
file.puts( "( #{animal.id}, #{pluralise_animal(animal.name)}),"
end
end
Note: replace the trailing comma(,) with a semicolon (;)
Then run the generated SQL script in the database to populate the temp table.
Finally run a SQL update statement in the database that joins the temp table to the main table...
UPDATE animals a
INNER JOIN pluralised_animals pa
ON a.id = pa.id
SET a.plural_name = pa.plural;

PHP Registration to MYSQL Database

I have a problem here..
Im currently building a website(blog) where I want people to be able to register. And I want that information to be sent to my MYSQL
This is some of the code:
<?php
$query="INSERT INTO Medlemmar(namn, epost)
VALUES("$_GET[namn]", "$_GET[epost]")";
if (!mysqli_query($mysql_pekare,$query))
{
die("Error: " . mysqli_error($mysql_pekare));
}
echo "Du har lagt till kunden I databasen";
?>
But for some reason i get error on the "VALUES" part.. That im missing a syntax.. WTF am i missing?! Been stuck with this for 1+ hours.. Just had to turn here, usually a quick response! Thanks!
edit: "Parse error: syntax error, unexpected T_VARIABLE"
There are syntax errors all over the place... This needs some work.
<?php
$query = "INSERT INTO Medlemmar(name, epost) VALUES(\"".$_GET['namn']."\", \"".$_GET['epost']."\")";
That should fix the query... You need to learn how to escape \" double quotes so they can be used in the actual query.
try
VALUES ('".$_GET[a]."', '".$_GET[b]."')
or ' and " exchanged.
You are forgetting the single quotation marks around each value
The way you're managing registration is extremely insecure. If you were to set the namn and epost value to a sql query (like SELECT FIRST (username) FROM user_table) then it would execute that as behalf of the original sql query.
if you set username to SELECT FIRST (username) FROM user_table then it would return the first username in the user_table
To avoid this from happening you can use prepared statements which means that you specifically assign a sql query with a placeholder value and then you apply a value to the placeholder.
This would mean that you force the sql query to only execute what you've told it to do.
E.g. You want to JUST INSERT into a table and only do that and nothing else, no SELECT and no table DROP well in that case you create the prepared INSERT query with a placeholder value like this.
$db = new PDO('mysql:host=localhost;dbname=database_name', 'database_user', 'database_user_password');
// Create the register statement for inserting.
// Question mark represent a placeholder for a value
$register = $db->prepare('INSERT INTO users_table (username, password) values (?, ?)');
// Execute the register statement and give it values
// The values need to be parsed over in a array
$register->execute(array("test_user", "test_password"));
I'm not the best at explaining but if you want to understand what EXACTLY is going on here then this is a pretty good article which explains it in more detail.

Show me an Injection Attack for this Stored Procedure

I notice that many people have said it's possible to create an injection attack, but my understanding is that is if someone is creating a query from a string, not parameters. In order to test the statement that Stored Procedures do not protect you against Injection Attacks, I am putting this example up in the hopes someone can show me a vulnerability if there is one.
Please note that I have built the code this way to easily insert a function that calls a procedure and embed it in a SELECT query. That means I cannot create a Prepared Statement. Ideally I'd like to keep my setup this way, as it is dynamic and quick, but if someone can create an injection attack that works, obviously that is not going to happen.
DELIMITER $$
#This procedure searches for an object by a unique name in the table.
#If it is not found, it inserts. Either way, the ID of the object
#is returned.
CREATE PROCEDURE `id_insert_or_find` (in _value char(200), out _id bigint(20))
BEGIN
SET #_value = _value;
SET #id = NULL;
SELECT id INTO _id FROM `table` WHERE name=_value;
IF _id IS NULL THEN
BEGIN
INSERT INTO `table` (`name`) VALUE (_value);
SELECT LAST_INSERT_ID() INTO _id;
END;
END IF;
END$$
CREATE FUNCTION `get_id` (_object_name char(200)) RETURNS INT DETERMINISTIC
BEGIN
SET #id = NULL;
call `id_insert_or_find`(_object_name,#id);
return #id;
END$$
The PHP Code
The PHP code I use here is:
(note, Boann has pointed out the folly of this code, below. I am not editing it for the sake of honoring the answer, but it will certainly not be a straight query in the code. It will be updated using ->prepare, etc. I still welcome any additional comments if new vulnerabilities are spotted.)
function add_relationship($table_name,$table_name_child) {
#This table updates a separate table which has
#parent/child relationships listed.
$db->query("INSERT INTO table_relationships (`table_id`,`tableChild_id`) VALUES (get_id('{$table_name}'),get_id('{$table_name_child}')");
}
The end result is
table `table`
id name
1 oak
2 mahogany
Now if I wanted to make oak the child of mahogany, I could use
add_relationship("mahogany","oak");
And if I wanted to make plastic the child of oak, I could use
add_relationship("oak","plastic");
Hopefully that helps give some framework and context.
It is not necessarily the stored procedure that is unsafe but the way you call it.
For example if you do the following:
mysqli_multi_query("CALL id_insert_or_find(" + $value + ", " + $id + ")");
then the attacker would set $value="'attack'" and id="1); DROP SCHEMA YOUR_DB; --"
then the result would be
mysqli_multi_query("CALL id_insert_or_find('attack', 1); DROP SCHEMA YOUR_DB; --)");
BOOM DEAD
Strictly speaking, that query should be written to escape the table names:
$db->query("INSERT INTO table_relationships (`table_id`,`tableChild_id`) " .
"VALUES (get_id(" . $db->quote($table_name) + ")," .
"get_id(" . $db->quote($table_name_child) . "))");
Otherwise, it would break out of the quotes if one of the parameters contained a single quote. If you only ever call that function using literal strings in code (e.g., add_relationship("mahogany", "oak");) then it is safe to not escape it. If you might ever call add_relationship using data from $_GET/$_POST/$_COOKIE or other database fields or files, etc, it's asking for trouble. I would certainly not let it pass a code review.
If a user could control the table name provided to that function then they could do, for example:
add_relationship("oak", "'+(SELECT CONCAT_WS(',', password_hash, password_salt) FROM users WHERE username='admin')+'");
Now you might say that there's no practical way to then extract that information if the resulting table name doesn't exist, but even then you could still extract information one binary bit at a time using a binary search and separate queries, just by breaking the query. Something like this (exact syntax not tested):
add_relationship("oak", "plastic'+(IF(ORD(SUBSTR(SELECT password_hash FROM users WHERE username='admin'),1,1)>=128, 'foo', ''))+'");
Really, it's easier to just escape the parameters and then you don't have to worry.

Search returns no rows in mysql query using LIKE and values having "\"

I have some problem regarding the search in mysql.
Below is my query.
SELECT * FROM table WHERE name LIKE "%admin\'s%";
When i am executing this query it will return zero data.
actually i have "admin\'s" stored in db. this "\" is to prevent sql injection. i have used mysql_real_escape_string to prevent the sql injection.
but when i use three times addslashes to my variable it works.
So my below query is working.
SELECT * FROM table WHERE name LIKE "%admin\\\\\\\'s%";
My above query will return the data with name like admin's.
I am not getting where i am wrong.
well for one you shouldnt have data like this in your DB admin\'s .. most likely you double escaped your string ( check if you don't have magic_quotes enabled on your server ).
If you only do
INSERT ... username = "admin\'s";
you will have in your db the username value admin's
so my recomandation would be to go ahead and remove the slashes from your database and then your first query should work.

Why does my INSERT sometimes fail with "no such field"?

I've been using the following snippet in developements for years. Now all of a sudden I get a DB Error: no such field warning
$process = "process";
$create = $connection->query
(
"INSERT INTO summery (process) VALUES($process)"
);
if (DB::isError($create)) die($create->getMessage($create));
but it's fine if I use numerics
$process = "12345";
$create = $connection->query
(
"INSERT INTO summery (process) VALUES($process)"
);
if (DB::isError($create)) die($create->getMessage($create));
or write the value directly into the expression
$create = $connection->query
(
"INSERT INTO summery (process) VALUES('process')"
);
if (DB::isError($create)) die($create->getMessage($create));
I'm really confused ... any suggestions?
It's always better to use prepared queries and parameter placeholders. Like this in Perl DBI:
my $process=1234;
my $ins_process = $dbh->prepare("INSERT INTO summary (process) values(?)");
$ins_process->execute($process);
For best performance, prepare all your often-used queries right after opening the database connection. Many database engines will store them on the server during the session, much like small temporary stored procedures.
Its also very good for security. Writing the value into an insert string yourself means that you must write the correct escape code at each SQL statement. Using a prepare and execute style means that only one place (execute) needs to know about escaping, if escaping is even necessary.
Ditto what Zan Lynx said about placeholders. But you may still be wondering why your code failed.
It appears that you forgot a crucial detail from the previous code that worked for you for years: quotes.
This (tested) code works fine:
my $thing = 'abcde';
my $sth = $dbh->prepare("INSERT INTO table1 (id,field1)
VALUES (3,'$thing')");
$sth->execute;
But this next code (lacking the quotation marks in the VALUES field just as your first example does) produces the error you report because VALUES (3,$thing) resolves to VALUES (3,abcde) causing your SQL server to look for a field called abcde and there is no field by that name.
my $thing = 'abcde';
my $sth = $dbh->prepare("INSERT INTO table1 (id,field1)
VALUES (3,$thing)");
$sth->execute;
All of this assumes that your first example is not a direct quote of code that failed as you describe and therefore not what you intended. It resolves to:
"INSERT INTO summery (process) VALUES(process)"
which, as mentioned above causes your SQL server to read the item in the VALUES set as another field name. As given, this actually runs on MySQL without complaint and will fill the field called 'process' with NULL because that's what the field called 'process' contained when MySQL looked there for a value as it created the new record.
I do use this style for quick throw-away hacks involving known, secure data (e.g. a value supplied within the program itself). But for anything involving data that comes from outside the program or that might possibly contain other than [0-9a-zA-Z] it will save you grief to use placeholders.