Dynamic Website Security Questions (PHP+MySQL)
I'm writing a dynamic site that is powered by PHP and MySQL (to be run on a WAMP server). My current concerns are about the security of the site, however it does not feature any user input being saved and then later output for any users (except by admins), so I'm not really worried about XSS. My main concerns are against SQL injection attacks and protecting an admin login portal against rainbow tables/brute forcing.
1) Does using mysql_real_escape_string in conjunction with sprintf() protect you from SQL injection? e.g.,
$thing = mysql_real_escape_string($_REQUEST['thing'])
$query = sprintf("SELECT * FROM table WHERE thing='%s'", $thing);
$result = mysql_query($query);
Is this sufficiently safe? Of course, no system is perfectly safe, but I know that prepared statement are supposed to be the best way to protect against SQL injection. However, if my code is "safe enough" then I see no reason to make the change over. I read somewhere that mysql_query only allows one My开发者_如何转开发SQL query per call for security reason by default, is that correct? If so, I don't see how any injections could be done on my code, but please let me know if there is a flaw in my logic here.
2) I am writing an admin portal for the site so that the owners of it can manipulate the MySQL database in an easy, user-friendly way on the website (from a login HTML file that isn't linked to from anywhere on the site). My concern for security here is the login process, which consists of two pages. First, gathering the user login information:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>Admin Portal</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="robots" content="noindex, nofollow">
<link rel="stylesheet" type="text/css" href="http://www.anotherdomain.com/my.css">
<script type="text/javascript" src="http://www.anotherdomain.com/my.js"></script>
</head>
<form method="post" action="admin/login.php">
<table align="center">
<tr><th>Admin Login Form</th></tr>
<tr><td>Name</td><td><input type="text" name="Name" size="30" onKeyPress="return aJSFunctionToStopEnterKeyFromWorking(event)"></td></tr>
<tr><td>Password</td><td><input type="password" name="Password" size="30" onKeyPress="return aJSFunctionToStopEnterKeyFromWorking(event)"></td></tr>
<tr><td></td><td><input type="reset" value="Clear Form"> <input type="submit" value="Login"></td></tr>
</table>
</form>
Second, the actual login script:
<?php
$inputusername = $_POST['Name'];
$inputpassword = $_POST['Password'];
$username = "a username that is not obvious";
$password = "a password that is at least 10 characters long";
$salt = hash('sha512', "a messed up string with weird characters that I wrote");
$hashword = hash('sha512', $password . $salt);
$inputhashword = hash('sha512', $inputpassword . $salt);
if($username == $inputusername && $hashword == $inputhashword) {
session_start();
$_SESSION['valid'] = 1;
header('Location: portal.php');
exit;
}
else {echo "Invalid username or password";}
?>
And then after the login process, each page will have the following to ensure that the admin is logged in:
<?php
session_start();
if(!$_SESSION['valid']) {header('Location: ../admin.html');}
?>
Portal page goes here
Since there is no creation of new users, there will only ever be one user for the portal. I am just wondering how safe this method of login is against attacks such as rainbow tables and brute forcing? I assume since I made the hashword and salt both very large it should be quite safe from these sorts of attacks, even if the username were somehow a known.
I am also wondering if this is safe from session hijacking, as that is a term I have heard thrown around but I don't know much about... I know that I'm not ever throwing a session ID around or anything like that, so it seems pretty secure.
3) Any other security concerns I should know/think about?
I really appreciate any help I get with this!
Other points to think about:
1. You are vulnerable to bruteforce
A dictionary attack would crack your password. As the vast majority of users have an insecure password, it's only matter of time. Use captcha, or log invalid entries. Or add some delay when the password is incorrect.
As Col. Shrapnel said, a rainbow table isn't a concern to you because they are used when someone have a bunch of hashes and want to crack them. Salt is used to gain some protection against a rainbow table, and this is not your case.
2. You are sending passwords in clear text
If someone sniff your login (wifi, for example), you are doomed. There's some javascript libs which can encrypt anything using public keys. If you don't want to use SSL, encrypt the login/password, send to server, decrypt using the private key, and you are safer.
3. Consider using prepared statements on MySQL
Using prepared statements helps against SQL injection, as it can safely run even with malicious input:
$dbc = new mysqli("mysql_server_ip", "mysqluser", "mysqlpass", "dbname");
$statement = $db_connection->prepare("SELECT * FROM table WHERE thing='?'");
$statement->bind_param("i", $thing);
$statement->execute();
4. Don't relay on client-side validation
On your login form, you relay on a javascript funcion preventing Enter-key to function. What if I disable Javascript? You could use a hidden field (e.g. < input type='hidden' name='FormIsValid' value='0' >), use your function to prevent Enter-key, AND use an onSubmit() function to change FormIsValid to 1 before sending the form. In your server, verify FormIsValid.
5. You are vulnerable to session hijacking
Your session are saved on a cookie, by default named PHPSESSID. If an attacker can get that cookie, it could send it to your server and steal your session. To prevent it, you can save the user IP address and user agent in the session, and compare the value received from the session on every request. If the values doesn't match, the user IP may have changed or the session may have been hijacked.
6. You can be vulnerable to session fixation
As stated above, if someone convinces your admin to access some site, and this site sends a request to your site with a PHPSESSID on the request, your site would create the session, process the login/password, and state that the credentials are wrong. Not bad until now.
Later, your admin logs into your portal, the session already exists, the login and password matches, and the session is UPDATED. The variable valid now is 1.
As soon as the variable are updated, the attacker have full access to your portal, as he knows the PHPSESSID, your site doesn't prevent session hijacking, or session fixation.
To avoid session fixation and hijacking, see #5.
1) Does using mysql_real_escape_string in conjunction with sprintf() protect you from SQL injection?
As long as 1)you're applying this function to strings only and 2) proper encoding using mysql_set_charset()
is set.
For the numbers you could simply use %d
I read somewhere that mysql_query only allows one MySQL query per call for security reason by default, is that correct?
You are right. Not much for the security but more likely by design.
If so, I don't see how any injections could be done on my code, but please let me know if there is a flaw in my logic here.
You are wrong. SQL injection stands for injecting SQL code in your query, not just single "bobby drop tables" query.
I am just wondering how safe this method of login is against attacks such as rainbow tables and brute forcing?
Rainbow tables has nothing to do here while brute-forcing still is a danger.
I don't think it's a big deal though. Not bigger than sending password in plain text.
I assume since I made the hashword and salt both very large it should be quite safe
Interesting point here.
In fact, all that hashing/salting mess is totally useless here. It's no more secure than just comparing plain passwords.
I am also wondering if this is safe from session hijacking,
there is always a possibility. If you're concerned that much, employ an SSL connection, like google mail does.
so I'm not really worried about XSS.
That's quite false feeling.
as simple as echo "Requested article: ".$_GET['id']'
thing is already vulnerable.
Looks like others have picked this apart. But i have one more to add:
Authentication bypass:
<?php
session_start();
if(!$_SESSION['valid']) {header('Location: ../admin.html');}
?>
When you do a header("location: ...")
The script still executes, you still have to exit
or die()
. So in fact by this bit of code I know that all of your pages can be accessed by an unauthenticated user.
More improvements:
Check the type of your parameters before you are quoting them into something. (Except for text search. This requires a little more). A missing value could raise a SQL error where attackers can learn from ;)
Put an anti csrf token to your login page and put a sleep after a bad login try to prevent bruteforce attacks.
Login over https. Over http you push clear passwords through the net. Perfect man in the middle attack scenario (but it's quite difficult to do :).
Check for correct input encoding. There are some UTF-7 attacks which are using japanese multibyte chars which have a singlequote as second byte. (This possible breaks mysql_real_escape_string) Veeery tricky stuff.
Use prepared statements
精彩评论