4

I'm making a bigger site for the first time so security really matters, unlike for my really-bad boards with maybe 10 users. So I wanted to ask if the way I'm doing it is secure enough for a semi-big application where money is involved.

The site is written in PHP, database is MySql. This is the current login.

First off, I have a 128 sign long salt and session key saved in the local config.
On every site that interacts with sessions (e.g. the user control panel) I have this snippet at the start of the code:

session_start();
if (empty($_SESSION)) {
    session_regenerate_id(true);
}

After that I connect to the database, escape all the strings and read out the data.
After that, I compare the two passwords which got hashed with SHA-512 and the salt from before:

public static function hashValue($str) {
    for ($x = 0; $x < 10000; $x++) {
        $str = hash('sha512', $str . self::$salt);
    }

    return $str;
}

If everything is correct, $_SESSION[$sessionkey] gets set with the user ID from the database (altough the long session key shouldn't be neccessary because the client can't change the $_SESSION vars locally anyway, IIRC. Right?)
If the site is secured (as in, users not logged in don't have access) this code is called to redirect them back to the index if they aren't logged in:

if (!isset($_SESSION[$sessionkey])) {
    header("Location: ?view=default");
    exit;
}

Is this application secure? I've read somewhere that its better to have a randomly generated salt for each user, saved in the database. Is that really neccessary, considering it only really prevents brute forcing a bit better, which shouldnt be a problem with a captcha anyway?

9
  • 17
    What is "Secure Enough?".. The grand question..
    – Pogrindis
    Feb 12, 2016 at 10:05
  • I'd define it in my case as that the user can't take advantage of obvious security openings, e.g. I don't need to worry that I go online after my site is online for one day and someone is able to login as another user, change database values or anything else that breaks important stuff.
    – nn3112337
    Feb 12, 2016 at 10:08
  • We have several questions about hardening; PHP issues, MySQL hardening, and more. You may find a lot of things you'll find interesting under the hardening tag. Feb 12, 2016 at 10:24
  • 1
    As a remark, the Location header should properly take a full URL, including protocol and domain name. Most browsers understand it without, but this is not technically compliant with the standard.
    – TRiG
    Feb 12, 2016 at 17:05
  • 2
    Any reason you to write your own code for this instead of an existing library?
    – corsiKa
    Feb 12, 2016 at 17:11

1 Answer 1

22

The worst case scenario you have to protect against is an attacker which completely compromises your server and obtains your sourcecode, your config and a dump of your database. Keep that in mind.

This is the scenario in which a salt is supposed to protect your passwords. Remote brute-forcing is not impaired by a salt at all (which is pointless anyway on everything but the most trivial passwords due to limited network bandwidth).

A few problems:

  • A "salt" which is the same for all passwords is called a pepper and doesn't increase security much when the attacker knows it.
  • SHA512 is a message digest algorithm, not an algorithm for hashing passwords. The problem with SHA512 is that it is too fast, allowing the attacker to brute-force the stolen password database very quickly. What you want is an intentionally slow algorithm like bcrypt. PHP has a very easy to use default implementation which can automatically generate, store and apply the salt for you.
  • Long, unique and impossible to guess session IDs are necessary, because when an attacker can guess the session ID of a logged in user they can hijack their session. PHP's session handling system assumes that any request which includes the session ID of a previously authenticated user comes from that authenticated user. In general you should rely on the session IDs which are auto-generated by PHP and only replace them with your own when you are sure you know what you are doing.
  • Minor point: "After that I connect to the database, escape all the strings and read out the data." Escaping all user inputs with mysqli_real_escape_string would be sufficient in theory, but in practice it is easy to forget about some rare edge-case where user-supplied strings end up in database queries. A better practice is to do all database queries with prepared statements which use the ? placeholder and bind for all variables. In that case the mysqli API does the escaping for you which is far more reliable, especially because it is aware of the context in which the escaped strings will be used. As an additional benefit it also has some performance and readability advantages.
3
  • Thanks for the answer! Good to know about all this, I'll use bcrypt from now on. If I understand password_hash and your answer correctly I don't need to generate an extra salt, right? Well, im not replacing the session ID with my own, im simply regenerating it. I also meant the session key, eg $_SESSION[keyhere], which shouldnt impact anything right? Also thanks for the prepared statement stuff, didnt know about that!
    – nn3112337
    Feb 12, 2016 at 11:17
  • @nn3112337 correct - password_hash() generates a random salt every time it is used, and you should leave it at that.
    – ROAL
    Feb 12, 2016 at 13:11
  • 2
    The OP iterates SHA512 10000 times, so it's comparable to PBKDF2-HMAC-SHA512 security wise. Feb 12, 2016 at 16:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .