I am in the process of auditing security tools, and I decided a good approach would be to create my own "insecure area" (with randomly generated fake data), so I set up an area that is susceptible to sql injections. However, I can't actually seem to inject it. I do notice that when I give it a bad query, the "Here are the accounts found" does not print, but the error messages do not print, either.
Can someone tell me if I am doing something incorrect?
<?php
$resultHTML;
if (isset($_POST['email']) || isset($_GET['email'])) {
$conn = mysqli_connect($servername,$username,$password);
if ($conn) {
//this part is insecure (intentionally for testing)
if (isset($_GET['email'])) {
$query = "SELECT * from badSQL.Two WHERE email = '$_GET[email]'";
} else {
$query = "SELECT * from badSQL.Two WHERE email = '$_POST[email]'";
}
//echo $query;
$result = $conn->query($query);
if ($result) {
$resultHTML = "Here are the accounts found: ";
$hasAccount = false;
while ($row = mysqli_fetch_assoc($result)) {
$hasAccount = true;
$resultHTML .= "<br>".print_r($row);
}
if ($hasAccount === false) {
$resultHTML = "No accounts found.";
}
}
} else {
$resultHTML = "DB Connection could not be established: ".$conn->connect_error;
}
}
?>
<html>
<head>
<title>Two BadSQL Test</title>
</head>
<body>
<h1>Two Website!</h1>
<br>
<h3>Forgot Password</h3>
<p>Enter your email below, and click submit:</p>
<form id="forgotForm" method="get">
<input type="text" name="email" />
<input type="submit" value="submit" />
</form>
<br>
<div id="results"><?php echo $resultHTML; ?></div>
</body>
</html>
1' OR '1'='1 should be valid SQLi for your above script, as it would evaluate to:
"SELECT * from badSQL.Two WHERE email = '1' OR '1'='1'"
It doesn't matter that the email address will always be wrong, as it makes use of an OR clause. Considering 1 will always equal 1, the expression as a whole will always hold true. Thus, the above script will 'skip over' all of the email checks, and then attempt to find the first user to satisfy the condition of '1' = '1'. This correlates to the first user in the table (which is usually an administrator as the ID would be 1, leading to further exploitation).
To prevent against this, I'd recommend making use of stored procedures or parameterised queries, and making the first user in each table have the fewest privileges possible. I'd also recommend checking out the OWASP SQLi Prevention Cheat Sheet.
An equivalent non-vulnerable PHP parameterised query would look something like:
$stmt = $dbConnection->prepare('SELECT * FROM badSQL.TwoWHERE name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// Do something with $row
}
Also, I'd recommend against deliberately making your own site more vulnerable, as creating a vulnerability for encapsulated fake data could serve as a vector for legitimate attacks.
Hope this helps :)
Related
I am trying to create a simple search bar that allows the user to search using a user id to return a specific user.
I need to include SQL injection protection.
currently the page loads a blank screen but when I refresh the page I get the "No results found" response.
I have been reading a lot of posts about search bars but nothing is helping me.
here is the code:
<html>
<head>
<title></title>
</head>
<body>
<form action="search.php" method="POST">
<input type="text" name="search" />
<input type="submit" value="Search" />
</form>
</body>
<?php
//search.php
include("DbConnect.php");
$search = $_POST['search'];
$safesearch = mysqli_real_escape_string($search);
$Query = "SELECT user_id
FROM users
WHERE user_id = '$safesearch'";
$Result = mysqli_query($DB,$Query);
$NumResults = mysqli_num_rows($Result);
if ($NumResults==1)
{
echo "<h1>results: ".$Result."</h1>";
}else{
echo "<h1>No Results found</h1>";
}
?>
You should have an if(isset($_POST['submit']{ } around your code so it only fires if they search and not when the page is loaded.
If you're doing any sort of insert,select or update with sql statements that will have variables within them you should use prepared statements.
Use a prepared statement it's a lot more safe than what you're doing:
<?php
//search.php
include("DbConnect.php");
$search = $_POST['search'];
$safesearch = mysql_real_escape_string($search);
$Query = "SELECT user_id
FROM users
WHERE user_id = ?";
$stmt = $connectionVariableName->prepare($query);
$stmt->bind_param("s",$safesearch);
$stmt->execute();
$stmt->bind_result($result);
$stmt->fetch();
$num_rows = $stmt->num_rows;
$stmt->close();
if ($num_rows > 0)
{
echo "<h1>results: ".$result."</h1>";
}else{
echo "<h1>No Results found</h1>";
}
?>
http://php.net/manual/en/mysqli.prepare.php
You should also sanitize the search variable by testing it with regex to make sure it only contains the characters you allow in the userid and not / * ^ etc
You want partial phrase search, I presume.
Then your query must look like this:
$Query = "SELECT user_id
FROM users
WHERE user_id LIKE '%$safesearch%'";
Not very familiar with php, but % symbol seem not being a special character in the language (correct me if I'm wrong), if it by chance is - escape it.
To make it case insensitive -
$safesearch = strtolower($safesearch);
$Query = "SELECT user_id
FROM users
WHERE lowercase(user_id) LIKE '%$safesearch%'";
I am having a pig of a time with my code. I am new to this and am struggling greatly.
I have several issues, firstly one problem is that I am trying to use a login form which is connected to an SQL database, but do not get an error when incorrect data or no data is entered, but it looks to log in.
Secondly, I am trying to show the username on each page when users are logged in, which works, but only for those users that have been manually entered into the database. Any user that has been added via my registration form, do not show, though they are showing in phpmyadmin.
My login page code for the first problem is:
<?php
echo '<h3>Sign in</h3>';
if($_SERVER['REQUEST_METHOD'] != 'POST')
{
/*the form hasn't been posted yet, display it
note that the action="" will cause the form to post to the same page it is on */
echo '<form method="post" action="">
Username: <input type="text" name="Username" />
Password: <input type="password" name="Password" />
<input type="submit" value="Sign in" />
</form>';
}
else
{
//the form has been posted without errors, so save it
//notice the use of mysql_real_escape_string, keep everything safe!
$username = mysql_real_escape_string($_POST['Username']);
$password = mysql_real_escape_string($_POST['Password']);
$sql = "SELECT * FROM Users WHERE Username = '$username' AND Password = '$password'";
$result = mysql_query($sql);
if(!$result)
{
//something went wrong, display the error
echo 'Something went wrong while signing in. Please try again later.';
header("location:index.php");
//echo mysql_error(); //debugging purposes, uncomment when needed
}
else
{
{
{
//set the $_SESSION['signed_in'] variable to TRUE
$_SESSION['signed_in'] = true;
//we also put the user_id and user_name values in the $_SESSION, so we can use it at various pages
while($row = mysql_fetch_assoc($result))
{
$_SESSION['UserID'] = $row['UserID'];
$_SESSION['Username'] = $row['Username'];
}
echo 'Welcome, ' . $_SESSION['Username'] . ' Proceed to the forum Home page.';
}
}
}
}
?>
Thanks for any advice.
Function mysql_query() returns FALSE when there is an actual mysql error. That can be syntax error, invalid constraint insert or input datatype mismatch. If any inserted username and password combination can potentially be valid, that means there won't be any errors.
In your case, if username or password are wrong, it means that 0 rows (no data) is returned which is not an error. So in your code, variable $result is never FALSE, and that is why your code never goes in error loop.
In order to fix this, you will need to change your code to check if number of returned rows is greater than 0 rather than checking if result is TRUE. You can achieve this by using mysql_num_rows() function.Changed code should look like this
$result = mysql_query($sql);
$num_rows = mysql_num_rows($result);
if($num_rows < 0){
//put some code for error
}
Further notes : If you don't want to use PDO you can use mysqli instead of mysql. Also you are vulnerable to sql injection. It would be really good if you take a look at prepared statements and how to make data coming to your server more secure.
<html>
<head>
</head>
<body>
<form action="login_process.php" method="post">
SID: <input type="text" name="sid">
<br />
Password: <input type="text" name="pw">
<br />
<input type="submit" value="Login">
</form>
</body>
</html>
login_process.php FILE
<html>
<head>
</head>
<body>
<?php
include ("connection.php");
$sid = $_POST['sid'];
$pw = $_POST['pw'];
setcookie("username", "$sid". time()+86400);
$result = mysqli_query($con, "SELECT SID FROM student_table WHERE SID='$sid' and password='$pw'") or die('Query failed');
if(!$result){
echo "Failed";
} else {
echo "success". $_COOKIE['username'];
$_SESSION['username'] = $sid;
}
?>
</body>
</html>
I have data in my student_table. But no matter what input i give it says success. even if i click login button without any input it still says success. please help.
You should use quotes when you assign values in sql queries .
SELECT
SID
FROM
student_table
WHERE
SID = '$sid' and password = '$pw'
Also look forward Prepared Statements to protect yourself from sql injections.You should also add the code below to fetch selected row :
while ($row = mysqli_fetch_array($result)) {
$_SESSION['username'] = $row['SID'];
}
Start learning basic debugging:
When a query fails bad, just do this:
Instead of running it (mysql_query, or mysqli_query or whatever you have), ECHO it:
echo "SELECT SID FROM student_table WHERE SID='$sid' and password='$pw'";
After you submit the form, you will be shown the query that runs, go to phpmyadmin or whatever you use and run the query manually. See if there are errors and also see easier what's going on. Advisable when you do work like this, FIRST try the queries in phpmyadmin then apply them in the code, after you are confident they are ok.
This is not really an answer but more about how to find out what is going wrong when you code does not work as you expect.
First, always put you SQL into a variable by itself. Why, so you can 'var_dump' it and see what is happening. So, try this:
$sql = "SELECT SID FROM student_table WHERE SID=$sid and password=$pw";
var_dump($sql, 'my_query'); // you will see what PHP created.
then try the answer provided:
$sql = "SELECT
SID
FROM
student_table
WHERE
SID = '$sid' and password = '$pw'";
At least you will see what is likely to be incorrect.
var_dump($sql, 'the answer_query'); // you will see what PHP created.
$result = mysqli_query($con, $sql) or die('Query failed');
The outer quote marks must be " (double quote) not " ' " (single quote) both of which are perfectly valid PHP. the former allows variables to be replaced in strings.
The latter will treat eveything as a literal string.
As described in the title, I am running into an SQL injection error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1' at line 1
How do I fix this? Provided below is my php code and html code
PHP:
if($_POST['submit']=='Change')
{
$err = array();
if(!$_POST['password1'] || !$_POST['passwordnew1'])
$err[] = 'All the fields must be filled in!';
if(!count($err))
{
$_POST['password1'] = mysql_real_escape_string($_POST['password1']);
$_POST['passwordnew1'] = mysql_real_escape_string($_POST['passwordnew1']);
$row = mysql_fetch_assoc(mysql_query("SELECT id,username FROM members WHERE username='{$_SESSION['username']}' AND pass='".md5($_POST['password1'])."'"));
if($row['username'])
{
$querynewpass = mysql_query("UPDATE members SET pass='".md5($_POST['passwordnew1'])."' WHERE username='{$_SESSION['username']}'");
$result = mysql_query($querynewpass) or die(mysql_error());
}
else $err[]='Wrong Password To Start With!';
}
if($err)
$_SESSION['msg']['passwordchange-err'] = implode('<br />',$err);
header("Location: members.php?id=" . $_SESSION['username']);
exit;
}
HTML:
<form action="" method="post">
<?php
if($_SESSION['msg']['passwordchange-err'])
{
echo '<div class="err">'.$_SESSION['msg']['passwordchange-err'].'</div>';
unset($_SESSION['msg']['passwordchange-err']);
}
if($_SESSION['msg']['passwordchange-success'])
{
echo '<div class="success">'.$_SESSION['msg']['passwordchange-success'].'</div>';
unset($_SESSION['msg']['passwordchange-success']);
}
?>
<label class="grey" for="password1">Current Password:</label>
<input class="field" type="password" name="password1" id="password1" value="" size="23" />
<label class="grey" for="password">New Password:</label>
<input class="field" type="password" name="passwordnew1" id="passwordnew1" size="23" />
<input type="submit" name="submit" value="Change" class="bt_register" style="margin-left: 382px;" />
</form>
I have it working where a user is able to change/update their password, however, when they click the Change button on the form, they are directed to that error message I posted above, and if they click the refresh button, only then they are redirected back to their profile and the changes have been made. So my main question at hand is, how do I get this to fully work without that mysql error message? Any help would be much appreciated!
A few things wrong here, more than can be put in a comment. I'm sorry, I can't see exactly what your error is, but if you follow point #1, it'll go away.
Don't use the mysql library. It is deprecated, and has been removed (finally!) in PHP 5.5. It is only working for you at the moment, because your version of PHP is out of date. You should either be using PDO or MySQLi. Check out this article for information on PDO: http://net.tutsplus.com/tutorials/php/php-database-access-are-you-doing-it-correctly/
Don't put any variable that's not generated in the script you're looking at into your query, this includes SESSION variables. You just need one flaw in your application, and the user can inject data into the SESSION. Treat every variable as dirty. If you know that it isn't - 100% for certain - then treat it as dirty. If you use prepared statements with PDO or MySQLi, this isn't a problem.
You should reference users by their ID, not username. Much faster and safer.
Never ever ever ever store passwords raw or simply encrypted (like with plain md5()) in the database. At the very least, you can encrypt with something like: crypt($password, '$2a$07$sillystring' . sha1($password) . '$') and verify by recrpyting the password and see if it matches. That's a very basic, more secure way of doing it. There are many articles written on password salting that go more in depth and are worth checking out.
Except for what Connor said, you have a serious problem here:
if($row['username'])
{
$querynewpass =
mysql_query("UPDATE members SET pass='".md5($_POST['passwordnew1']).
"' WHERE username='{$_SESSION['username']}'");
$result = mysql_query($querynewpass) or die(mysql_error());
}
The first inner line already performs the mysql_query and returns a resource, which is assigned to $querynewpass.
You're resending the result (a resource) to another query, as if it was a string containing the SQL command you want to perform.
This is the function's specification:
resource mysql_query ( string $query [, resource $link_identifier = NULL ] )
This is the correct usage of mysql_query (which is deprecated as people mentioned):
if($row['username'])
{
$querynewpass =
"UPDATE members SET pass='".md5($_POST['passwordnew1']).
"' WHERE username='{$_SESSION['username']}'";
$result = mysql_query($querynewpass) or die(mysql_error());
}
This code snippet could help to you
$pass1 = md5(mysql_real_escape_string($_POST['password1']));
$newpass = md5(mysql_real_escape_string($_POST['passwordnew1']));
$username = mysql_real_escape_string($_SESSION['username'])
$query = "SELECT id,username FROM members WHERE username = '$username' AND pass = '$pass1'";
$result = mysql_query($query); //that could also use , mysql_query($query,$yourconnection);
if(mysql_num_rows($result)>0)
{
$updatequery = "UPDATE members SET pass='$newpass' WHERE username='$username'";
$updateresult = mysql_query($updatequery) or die(mysql_error());
}
Please note that mysql library is deprecated in after php ver 5.5.0
I've done quite a bit of research and preparation with my code to try and prevent SQL injections, but I wanted to discuss something that I'm not quite sure about.
I understand the the mysqli_real_escape_string does not escape _ (underscore) and % (percent) characters. If I'm not using any LIKE clauses in my SQL statements, does this open me up to any risk?
Below is an example of one the instances I'm interested in talking about. Here is the login script I'm using. I want to make sure that I'm not opening myself up to any injection vulnerabilities here. Your insight and feedback would be greatly appreciated.
// Initiate login process if the mode is set to login
if ($_REQUEST['mode'] == "login") {
// Open shared database connection
$cxn = connectDb();
// Escape characters to help prevent a SQL injection attack
$username = mysqli_real_escape_string($cxn, $_POST['user']);
// Convert submitted password to hashed value using
// custom password hashing function
$password = custompwhash($_POST['pass']);
// Execute SQL statement to determine if the credentials provided
// match a valid user
$sql = "SELECT count(*) as countOK FROM user_def WHERE ".
"username = '$username' AND password = '$password'";
$result = mysqli_query($cxn,$sql);
$row = mysqli_fetch_array($result);
extract($row);
// If the username value submitted is null, throw and error
if ($username == "") {
die2("Please enter your username and try again.<br />");
failedloginalert($username);
}
// If the password value submitted is null, throw and error
else if ($password == "") {
die2("Please enter your password and try again.<br />");
failedloginalert($username);
}
// If the credenetials provided match a valid user in the database,
// initate login
else if ($countOK == '1') {
$sql2 = "INSERT INTO `user_activity` (`username`, `time`, `ip`)".
" VALUES ('$username', NOW(), '{$_SERVER['REMOTE_ADDR']}')";
$result2 = mysqli_query($cxn,$sql2);
$_SESSION['auth'] = 1;
$_SESSION['username'] = $username;
// If the user does not need to be directed to a specific page, direct to home.php
if (empty($_GET['page'])) {
die2("<span style='color:#000;'>You have successfully logged in. <br /><br />
Please click here if you are not automatically redirected.</span>
<meta http-equiv='refresh' content='0;url=home.php'/>");
die();
}
// Otherwise, if the user does need to be directed to a specific page, direct to the requested page
else {
$loginredirectpage = $_GET['page'];
die2("<span style='color:#000;'>You have successfully logged in. <br /><br />
Please click here if you are not automatically redirected.</span>
<meta http-equiv='refresh' content='0;url=".$loginredirectpage."'/>");
die();
}
}
// Since the credenetials provided do not match a valid user in the database, throw an error
else {
die2("The username or password you entered is invalid. Please try again. <br/><br/>If the problem persists, reset your password.");
failedloginalert($username);
}
}
Start using PDO and Prepared Statements and your issues with SQL injection will go away.
If I'm not using any LIKE clauses in
my SQL statements, does this open me
up to any risk?
No it doesn't. = just gives you more exact answer than LIKE
SQL Injection is more to do with quote sign ' " since the technique tries to append on the query string. So I'd say % and _ won't open up any security risk if you filtered all the possible quotes. In this case, mysqli_real_escape_string() can help.
PDO is an abstraction layer that assists you with dealing with databases more efficiently. It can help you with SQL injection, but I don't recommend using it unless you're building something big that requires a lot of interaction with database.
So final my point is your code looks ok (: