2
$user = $_COOKIE["username"];
$admin = $db->query("
    SELECT *
        FROM users
        WHERE username = '$user'
        AND admin = '1' -- if 1, user is an administrator
");
if ($admin->rowCount()==1) {
    //stuff related to admin controls/admin specific pages
}

How safe is this for validating a user is an administrator? Can an end user edit their cookie information, and if they know the administrator's name they can gain access somehow? If this is a case, should $user also be sanitized to prevent malicious behavior?

gator
  • 3,465
  • 8
  • 36
  • 76
  • 3
    That's definitely not safe. You can edit cookies with JavaScript or browser plugins. – elclanrs Aug 31 '13 at 00:03
  • How would I go about this, then? I'll admit my PHP knowledge is moderate at best. I'm not sure how to grab `$user` anywhere besides `$_COOKIE[]` to maintain some online presence. – gator Aug 31 '13 at 00:04
  • In Chrome you can view the cookie in the developer tools – Matthew Daly Aug 31 '13 at 00:05
  • 1
    @riista In that case, you should start from the basics before going into cookies,sessions and database – Daryl Gill Aug 31 '13 at 00:05
  • @DarylGill, I had taken a course at university in strictly PHP and SQL and in all of my assignments this was never marked off. If both `$_COOKIE[]` and `$_SESSION[]` data can be fooled around with, that's the limit of what I was taught how to handle logging in and verifying users. – gator Aug 31 '13 at 00:07
  • @riista Thats why you learn from your mistakes. I have a complex method, and probably too over the top for my sessions and cookies using `mpack` – Daryl Gill Aug 31 '13 at 00:08
  • 2
    you can't assume a course in php an sql will cover all bases. experience - and sites like this - will also give you hints to improve your methods – Kai Qing Aug 31 '13 at 00:09
  • @riista Sessions can't be fooled with (server assigned), unless someone hacks into "your" computer. To have more security, you can use sessions along with tokens. Have a look at the following example: http://phpro.org/tutorials/Preventing-Multiple-Submits.html – Funk Forty Niner Aug 31 '13 at 00:31

3 Answers3

2

I think you're misunderstanding the concept of logging in and what happens after that.

In your code, how do they get the cookie in the first place so you can log them in? It's the other way around.

You first register someone by taking their chosen username and password and saving those to the database. Later, to log them in, they use login form and you take their inputted username and password and verify it matches those stored in the database. No use of sessions or cookies so far.

So if the username and password matches together in the db then they have confirmed credentials (note in this scenario you will need unique usernames, otherwise, however unlikely, if two people had the same username/password they would both access the first account in the db)

Now, if you want to check throughout your site they are logged in or not (of course) you need to set a session when you verify their credentials are good. This will store some data about them, which on each page you can check and verify they are logged in. This can be their username, whatever.

You can go to any length here, and check their IP still matches on each page etc. Ssessions are hard to hack, and likely if someone has hacked a session on a server they have access to all manner of other things anyway (it's safe to use sessions).

By default on most servers, a session will actually use a cookie as well, it's the session ID for the session stored on the server. So when you get session data for a user it gets the ID from that users sores cookie and access the relevant info stored on the server.

Don't store sensitive info in the session/cookie.

For storing their password, you want to use crypt (not md5 or sha) and use a salting method with it, such as blowfish.

James
  • 4,644
  • 5
  • 37
  • 48
  • The way I had done it was a) users goes to register.php, fills out forms, data is stored into SQL (although password was md5'd), and then b) goes to login.php, fills out forms, and if user and password exists in the database, I `setcookie("username",$username,$expire)`, where `$username` is the `$_POST`ed form value. Maybe I misunderstood `$_SESSION` in class, but I was under the assumption they are temporary for as long as the site (and session) is open. I wanted to maintain login autonomy, even if they had left the site. – gator Aug 31 '13 at 00:21
  • 1
    Sessions (nonce cookie) + SSL (prevent MTM) + Session timeout is generally very secure; of course assumes that clients are not compromised, but I think it's very acceptable. Even without SSL (i.e. SSL only for login), it is still the common web idiom for securely maintain this sort of data although encryption+MAC is also an option to "store the data on the client" (i.e. in a cookie); but that's a different can of worms. – user2246674 Aug 31 '13 at 00:24
  • 1
    so I go to your site, set my self a cookie (which I can do locally easily) with a username I believe exists (or have found to exist) and it lets me use your site as if I'm that user. MD5 is better than plain text, but not great now - http://www.php.net/manual/en/faq.passwords.php#faq.passwords.fasthash – James Aug 31 '13 at 00:26
  • I don't get "Don't store sensitive info the *session*" though. That is why sessions *are* used - to store "sensitive" transient request-oriented data - as the data in question is never transmitted (only the nonce cookie is). – user2246674 Aug 31 '13 at 00:26
  • you just need an ID to verify they are logged in. what sensitive info do you need to store? All their data is presumably already in the DB, so they for example go to their account page to edit their email or whatever, you use mysqli or pdo to select their data there and then, per script. – James Aug 31 '13 at 00:27
  • @James The security issues are related to "stealing" the session nonce - i.e. MTM including malicious JS - or by "not really using a session". – user2246674 Aug 31 '13 at 00:39
  • Just to clarify: I should be switching all of my `$_COOKIE` parts of code with `$_SESSION` and it is immediately more secure? ie. login.php after verifying information just sets `$_SESSION["username"] = $username;`, etc? – gator Aug 31 '13 at 00:46
  • 1
    with your current code yes, as you are storing their username in the cookie and using that as verification. As I can make my own cookie, there is no confirmation from you that I set it or you did, sessions take this away as I do not (under normal circumstances) have access to create a session on your server, meaning you created it as a result of pass/username verification first (read about sessions security http://stackoverflow.com/questions/328/php-session-security) – James Aug 31 '13 at 00:51
  • Thank you. I still have a ways to go by learning, but I'm getting there. – gator Aug 31 '13 at 01:00
1

You can manipulate cookies on your own machine. A better approach would be to assign the logged in user a random token that you can cross reference with the database to pull the user's level.

You should always sanitize input before using it in a query.

So create a table for logins and generate a random string to use as an access token. Look into the php crypt function to help generate the string and store that token and that token alone in the cookie. You can store just the user id and the token in the login table, then join with the user table to fetch level from there.

You can choose to cycle the tokens per page use or something to increase the improbability of someone else guessing a token and hijacking an admin session.

To increase the security on it, you can store a timestamp and invalidate the token after a certain time frame.

Kai Qing
  • 18,793
  • 5
  • 39
  • 57
  • Nit: All of this *should* be handled transparently with an appropriate session-state and auth provider. There isn't a point (here) to using crypt for cookies here though - the session cookie should generally be a pure nonce (no cycling, etc, needed). I believe this is correctly done with standard PHP sessions, but I don't use PHP. A MAC can add additional security against tampering, if the "username" data is ever directly accepted from the client. – user2246674 Aug 31 '13 at 00:17
  • True but maybe the OP is not using sessions for whatever reason. If the intention is strictly to use a cookie to verify anything then crypt makes some sense so long as the rule of minimal, relational info only is maintained. crypt is just to generate a string in this case so the user doesn't just do something like md5(time()) or some other generic random string thing that may be easily guessed. – Kai Qing Aug 31 '13 at 00:32
  • The user must use session of a sort (don't send/accept data outside the nonce) or use an [HMAC](http://en.wikipedia.org/wiki/Hash-based_message_authentication_code), preferably with encryption, for security. Sessions are easier in plain PHP (or rather, I've only seen encryption+MAC used in ASP.NET View State). Storing an arbitrary nonce in the cookie (which should/need not be based on crypt) and then manually loading some SQL is effectively a manually-rolled session. – user2246674 Aug 31 '13 at 00:34
  • It's his choice for level of security. You can handle a login system without the use of sessions. By the way, what does Nit mean? – Kai Qing Aug 31 '13 at 00:37
  • Nit[pick] :) I accept the answer as being generally valid, but there are some points I am challenging. – user2246674 Aug 31 '13 at 00:37
  • Aah. Well, if it helps to know, I agree with you. I don't know why people avoid sessions or why major packages like wordpress chose to be "stateless" and absent of session, but it is programatically possible and can be considered secure. Much more sensitive sites may choose to take it to the next level, but I don't really get a sense that this is the case here. – Kai Qing Aug 31 '13 at 00:39
0

Absolutely zero security! Never, never use the method you described above.

Instead, consider the following alternative with sessions

session_start();

// Set this when the user logs in.
// Then it can be read on subsequent requests and the data stored
// on the server and never accepted from the client.
$_SESSION["username"] = "Bob";

$user = $_SESSION["username"];
$admin = $db->query("
    SELECT *
        FROM users
        WHERE username = '$user'
        AND admin = '1' -- if 1, user is an administrator
");
if ($admin->rowCount()==1) {
    //stuff related to admin controls/admin specific pages
}
user2246674
  • 7,621
  • 25
  • 28
Exwolf
  • 157
  • 1
  • 12
  • 2
    don't forget to sanitize that variable. While this is still safer than the original method, session or not, $user should not just be taken as is – Kai Qing Aug 31 '13 at 00:11
  • This does show the very basic usage of using PHP sessions which are usually "secure enough" for this task - the point is that, *with sessions*, the "sensitive" data is never sent (or accepted) from the client itself and this *addresses the fundamental flaw with the original code*. I would recommend showing the use of *placeholders* for the SQL, just as good practice. – user2246674 Aug 31 '13 at 00:28