I'm developing my own PHP framework. It seems all the security articles I have read use vastly different methods for user authentication than I do so I could use some help in finding security holes.
Some information that might be useful before I start. I use mod_rewrite for my MVC url's. Passwords are encrypted with 24 character salt unique to each user. mysql_real_escape_string and/or variable typecasting on everything going in, and htmlspecialchars on everything coming out.
Step-by step process:
Top of every page:
session_start();
session_regenerate_id();
If user logs in via login form, generate new random token to put in user's MySQL row. Hash is generated based on user's salt (from when they first registered) and the new token. Store the hash and plaintext username in session variables, and duplicate in cookies if 'Remember me' is checked.
On every page, check for cookies. If cookies set, copy their values into session variables. Then compare $_SESSION['name'] and $_SESSION['hash'] against MySQL database. Destroy all cookies and session variables if they don't match so they have to log in again.
If login is valid, some of the user's information from the MySQL database is stored in an array for easy access. So far, I've assumed that this array is clean so when limiting user access I refer to user.rank and deny access if it's below what's required for that page.
I've tried to test all the common attacks like XSS and CSRF, but maybe I'm just not good enough at hacking my own site! My system seems way too simple for it to actually be secure (the security code is only 100 lines long). What am I missing?
Edit: For calling functions from the controller, anything that uses anything other than SELECT queries will require $_POST data to confirm a delete, for example, in addition to the user rank requirements.
I've also spent alot of time searching for the vulnerabilities with mysql_real_escape string but I haven't found any information that is up-to-date (everything is from several years ago at least and has apparently been fixed). All I know is that the problem was something to do with encoding. If that problem still exists today, how can I avoid it?
The encrypt function I borrowed from somewhere and modified:
public function encrypt($str, $salt = NULL) {
if ($salt == NULL) $salt = substr(md5(uniqid(rand(), true)), 0, 24);
else $salt = substr($salt, 0, 24);
return $salt.sha1($salt.$str);
}
Do yourself a favour and use a standard library for hashing your passwords.
Because security tends to be a lot more complicated and with more invisible screw up possibilities than most programmers could tackle alone, using a standard library is almost always easiest and most secure (if not the only) available option.
The standard library:
Take a look at: Portable PHP password hashing framework: phpass and make sure you use the CRYPT_BLOWFISH algorithm if at all possible.
example of code using phpass (v0.2):
require('PasswordHash.php');
$pwdHasher = new PasswordHash(8, FALSE);
// $hash is what you would store in your database
$hash = $pwdHasher->HashPassword( $password );
// $hash would be the $hashed stored in your database for this user
$checked = $pwdHasher->CheckPassword($password, $hash);
if ($checked) {
echo 'password correct';
} else {
echo 'wrong credentials';
}
PHPass has been implemented in some quite well known projects:
phpBB3
WordPress 2.5+ as well as bbPress
the Drupal 7 release, (module available for Drupal 5 & 6)
others
The good thing is that you do not need to worry about the details, those details have been programmed by people with experience and reviewed by many folks on the internet.
Whatever you do if you go for the 'I'll do it myself, thank you' approach, do not use MD5 anymore. It is a nice hashing algorithm, but utterly broken for security purposes.
Currently, using crypt, with CRYPT_BLOWFISH is the best practice.
CRYPT_BLOWFISH in PHP is an implementation of the Bcrypt hash. Bcrypt is based on the Blowfish block cipher, making use of it's expensive key setup to slow the algorithm down.
For more information on password storage schemes, you could also read Jeff`s blog post about it: You're Probably Storing Passwords Incorrectly
I don't see any real prevention for CSRF attacks in there. CSRF is (from my understanding) the most commonly overlooked/screwed up vulnerability in websites. Many of the big guys have been hacked via CSRF vulnerabilities.
The current best solution to this problem is using synchronizer tokens.
You had also better be very religious about escaping/encoding user input properly. Things that are inserted into the database must be escaped, and things output back to the user have to be encoded based on their context: CSS, HTML, JavaScript, etc all have to be encoded differently.
Furthermore, you shouldn't combine SHA-1 with MD5. While SHA-1 is stronger than MD5, you are essentially weakening it by combining it with MD5. Additionally, neither of these algorithms are recommended for use in new applications. For the time being you should be using SHA-256 (it is available out-of-the-box in PHP).
By trying to over-engineer the approach you may be undermining the security.
What session handler are you using? Have you checked that session_regenerate_id() renames an existing session rather than making a copy of it? If the latter then you are creating lots of session files that are much easier to find with a random search.
How do you deal with users splitting sessions? This line of code is going to break depending on the browser and how the user splits the session.
The only place you should be using session_regenerate_id() is when you authenticate the user and when the user logs out.
mysql_real_escape_string and/or variable typecasting on everything going in
NO, no, no. This keeps coming up here.
You don't change the representation of incoming data. By all means /validate/ input, and you should always /sanitize/ output - you don't /sanitize/ input.
If user logs in via login form, generate new random token to put in user's MySQL row. Hash is generated based on user's salt (from when they first registered) and the new token. Store the hash and plaintext username in session variables,
Nothing here which improves your security
Store the hash and plaintext username in session variables, and duplicate in cookies if 'Remember me' is checked
So you store the plaintext password on the users machine! This is so not secure. If you want a remember me function, then encrypt the password and an expiry date with a salt and store that.
On every page, check for cookies. If cookies set, copy their values into session variables.
So users can manipulate any value stored in the session by returning a cookie with the same name? OK so they probably can't predict the hash so it'd be tricky to fake the user - but there are lots of other ways this could be used to compromise your system.
You've tried very hard to make your system secure - but at best all you've actually done is obfuscated the parts of your system that work properly.
C.
You may want to check the user.rank against a whitelist, to make sure that the value exists in the lookup table and is valid (active, not deleted etc), and also to force typecasting and range.
As for tokens, you will want to generate a new token for each page that expects to receive GET/POST data from a form. If a session is hijacked then a token for the session could be insufficient. Setting a shorter timeout for the session could mitigate.
Don't trust anything from a cookie right away, consider using http://php.net/manual/en/book.filter.php before stashing in the Session.
If you plan to have an admin panel for the system it's ideal to enforce password strength, manual admin login name selection (in preference to admin/password stock logins upon setup).
Blacklist the usual login names such as "a';DROP DATABASE..." and "Administrator", you know the drill.
Use least-privilege accounts when setting up your database, you don't want your visitors to be playing havoc with your tables.
"Passwords are sha1 and md5 encrypted"
Why would there be a need to use two hash functions? I guess you do it due to a lack of understanding (no offense!). You should read this article on secure password schemes (hint: use bcrypt).
with session_regenerate_id(); you'll make the "back" button in your browser unusable. But still combined with "remember me" feature it become useless.
I can't see the CSRF defence in your description
There is not a single problem with mysql_real_escape string
Related
I am working on a new authentication system for my PHP website, using "password_hash" and "password_verify" functions.
However, many posts recommend using a 'token' system for persistent authentication.
Is it unsafe to store the password_hash in a Cookie? If yes, why? (so far as I understand, it is impossible to 'decode' a password hashed with this PHP function)
If the previous solution is acceptable, is there any way to compare 2 hashes of the same string? ("password_hash" function will generate a new string every time it is called)
I would also like to auto-login users when clicking on the "Dashboard" button in my e-mails - through a link like "dashboard.php?email=me#domain.com&pass_hash=XXX"
Is it unsafe? Why?
Thanks a lot for your enlightened help! :-)
Is it unsafe to store the password_hash in a Cookie? If yes, why? (so
far as I understand, it is impossible to 'decode' a password hashed
with this PHP function)
Yes, it's unsafe. Or at least, less safe than not doing it. If you expose the hash to the user, it enables out-of-band brute forcing. I.e., the user can attack the password offline. In addition, if the password doesn't change often, it allows the contents of the cookie to be copied and authenticated with.
is there any way to compare 2 hashes of the same string?
You shouldn't ever be directly comparing two hashes to authenticate, you should only be using the return value of password_verify().
If you want to "keep the user logged in", you'd generally authenticate on the first request, start a session, and then use the existence of the session as authentication for subsequent requests. (And then auto-expire the session after some amount of inactivity.)
I would also like to auto-login users when clicking on the "Dashboard"
button in my e-mails - through a link like
"dashboard.php?email=me#domain.com&pass_hash=XXX"
Is it unsafe? Why?
Yes, it's very unsafe. In this design, the hash is essentially no different than the password. Anybody in the world can click the link and they're logged in as the user, without having to know the password. And email can't be guaranteed to be secure, so this design is just as insecure as just emailing somebody a link with the plain password in it.
Anything that automatically logs in is insecure. Just embed a bare link to the target page and have the page make the user log in.
I need the authentication to last for few months.
This is a code smell. Nobody realistically expects an idle session to last a few months, especially with modern password managers that fill in the authentication for you.
I've started learning PHP by myself, and in the beginning, I would often choose the simplest way to do a task instead of the best way. Now that I'm developing important websites that need to be 100% secure, I hit this dillema,
I'm using cookies on my main page, to store the login session. Basically, the username and the hashed password is stored in a cookie and is loaded and checked against the database any time the user visits a mustbeloggedin page. For my main page, I'm using md5. Not because I want to, but because I have to. I know that poses a great security risk for the user because a keylog attack can basically freely take his password.
On this new website, I'm gonna use sha256, so that shouldn't be an issue.
Here's my question: what other security issues does storing this kind of data in a cookie and not in a session pose?
Here's mine:
Anyone with physical access to the computer can get the user's hash and store it for later use, by manually setting his cookie.
Any infected computer does the same as the above
Data is loaded, parsed, checked every load (not a security issue but still optimization-wise, it's not very good, but I don't mind that)
Anything else?
Does the domain variable inside the cookie make it secure enough not to be read by any other site?
Edit:: I'm also reading about someone intercepting the data being sent from a client to the server. How are sessions different than this? If I store a session , can't the identifier cookie still be hijacked and used by someone else? Would also adding an ip address to the cookie, then when validating the cookie, also check the IP address and if it's different then print the login form again help?
It seems you are trying to make some improvements, but not enough really.
There should never be a need to store passwords in a cookie, session, array, or anything else.
The password should be in the database and not be taken out to chance further access to it, or manipulation of the data holder in some way.
Otherwise, your highly secured database with hashes and salts on passwords, is only as secure as the framework/scripts and variable or cookie you store the password in (which is less secure than the aforementioned DB setup)!
From your comment:
Your question and statement makes no sense, you're describing a login
page and I'm describing about how the website knows you're logged in.
The cookie has the username and the hashed password, not plain text
password
So you store Bob's password in a cookie, with hash etc.
I steal Bob's password cookie. It's hashed, so safe right?
Ok, so I (James) use it on your site. How does you site know I am James, not Bob? It cannot.
It checks the cookie I stole, and password hash/salt/whatever you do match in your checks (otherwise it wouldn't for Bob either so would be useless).
It thinks I am Bob.
So now you start to check other things, if I have another cookie, perhaps username.
I have already stolen that.
So now your site looks at my cookies, sees a username and password, checks them, and says "welcome Bob, here's your personal/sensitive details, do as you wish...".
Passwords stay in the database!
You could try checking user agent, IP, and a load of other arguably less than useful/sometimes useful things etc, but these are things you can do "as well" as password+has+salt, and at the same time not store passwords in cookies or Sessions.
If your only methods to stop a hacker from using that stolen golden password cookie (hashed or not) is to check user agent, IP, and something else that can easily be faked, then your site is not secure.
Also, anytime the user needs to do something like change their password or email address, or check their whatever sensitive data on your site, you ask them to re-type their password.
Possibly resetting their cookies/hash/hash+salt stored in the DB, but depends on scenario really.
EDIT {
Use a cookie to store the Session reference, and any sensitive data in the Session.
Again, what you should store in the session depends on what data it is, if you run your own server, or shared, etc. Shared hosting can have bad config, opening up other security issues, even extending Session security issues.
(Info is in the links below - as said in comments, reading is your friend ATM - and then some evaluating and considerations of your specific needs)
}
Here is some serious reading for you:
First, your MD5 and even SHA256 are not secure:
http://php.net/manual/en/faq.passwords.php#faq.passwords.fasthash
Hashing algorithms such as MD5, SHA1 and SHA256 are designed to be
very fast and efficient. With modern techniques and computer
equipment, it has become trivial to "brute force" the output of these
algorithms, in order to determine the original input.
Because of how quickly a modern computer can "reverse" these hashing
algorithms, many security professionals strongly suggest against their
use for password hashing.
Also read the link for that quote - the bit about how you should hash, and the bit about salts.
Also, importantly, read about how to correctly store salts and hashes. There is a LOT of BAD advice out there which is misleading to the point you end up with barely any more security than if you just used MD5.
Storing the salt in the DB with the hashed password is fine, just also use unique salts etc (it's all there in the link, about mcrypt/blowfish etc)
A must read, even if you only take bits from it (and even if you ignore the rest of my answer):
The definitive guide to form-based website authentication
Faking Session/Cookies?
More reading:
What is the best way to prevent session hijacking?
Also read about:
Session fixation; Session sidejacking; Cross-site scripting;
And again, given you stated this:
Now that I'm developing important websites that need to be 100% secure
You should really spend a lot of time reading about all these things.
Cookie/session hijacking is real, and generally simple (script kiddie stuff).
If you want to produce secure websites and applications, you really need to learn about quite a few attack methods, preventions, etc.
Best way is read the links I've given, then any "branches" which stem from that read about them too.
Eventually you'll have a larger picture of the vast range of security concerns and resolves to them.
Some takeaways for cookies.
You want to limit any sensitive information saved within as it is not secure.
Cookies are perfect for session ids which you can then use to query your database and check if it is expired, matches an ip, matches user-agent and any other security/validation checks you want to do before you route to relogin or resume session.
http://php.net/manual/en/features.cookies.php
You mentioned user authentication. Most encryption protocols can be broken by using and md5 is considered 'broken' at this point due to completeness of lookup tables with all the hashes and the slight variations between hashes.
How can I make MD5 more secure? Or is it really necessary?
Salting your hash is crucial which adds another layer of security as is additional cdn/server restrictions to block/restrict brute force attacks:
https://crackstation.net/hashing-security.htm
http://www.cs.virginia.edu/~csadmin/gen_support/brute_force.php
If one is overly paranoid you can implement two factor authentication ( expensive? ):
https://isc.sans.edu/forums/diary/Implementing+two+Factor+Authentication+on+the+Cheap/9580/
http://www.twilio.com/docs/howto/two-factor-authentication
Don't store any credentials in cookies. There is session cookie and that is enough. In your database you can create a table where you will store PHP session ID together with user id. It is enough to check user's login and password once, at the logging, to establish a session.
I was doing the same as you do: storing login, password and session id in cookies and had many problems - occasionally for unknown reasons the browser was not deleting one of those cookies, or I had problems with paths of those cookies. I had to develop very complicated methodology for assuring that those cookies are properly set and that all of them are present in a given moment - I tinkered with removing and adding those cookies manually in the browser and had to come up with new ways of preventing the problems arising from such activities, but I was always able to make up new way of breaking that down and had to come up with new mechanism for preventing that.
All of this mess stopped when I finally decided to leave only one cookie - session ID, which I authenticate before every session_start() call - you can check if such a session exists and even compare current browser footprint with previously saved one. It is then very simple to foresee bad scenarios - when somebody deletes this cookie, session is over, garbage collection will clean it up. If somebody changes it or adds fake one - you can compare it against your sessions table and not start a session. To have better control over the sessions, use session_set_save_handler functionality.
There is a lot wrong with your chosen implementation.
the username and the hashed password is stored in a cookie
Don't do that. You should consider the content of cookies insecure.
and is loaded and checked against the database any time the user visits a mustbeloggedin page
There is no need to do that at all, if you know the user is already logged in (session).
I'm using md5
Using md5 at all precludes any semblance of security.
On this new website, I'm gonna use sha256
That will make almost no difference if credentials are still stored in a cookie.
So what should you do?
When a user authenticates themselves store their user info in the session. Any time you need to check if the current visitor has already authenticated check the session data. The session's data is stored on the server - it is secure. There's no need to call the db to find out who the user is on each page load, if the user's data is stored in the session.
Do not use cookies to store user credentials, especially if you're storing the password hash as stored in the db.
Don't use md5 - and if you're "forced" to do so change it at the very first opportunity.
As part of my web app. This is some code I am considering (I'm not the best of PHP programmers but I programming my own app for a project):
// Start session
session_start();
// Is the user already logged in?
if (isset($_SESSION['username'])) {
header('Location: members-only-page.php');
}
I want to know, if my login structure is like this, is this secure.
http://site.com/
https://site.com/login.php
https://site.com/login-action.php (MySQL login check, with md5 password check)
http://site.com/cp/members-only-page.php
I am using MD5(); but I am not entirely happy with the whole $_session["user"]="1" approach that scripts use; surely the likes of vBulletin wouldn't do this?
Appreciate a reply. I've not even touched on the idea of this being Ajax ha!
UPDATE - Psuedo code of my approach. Everything on SSL.
// vars
login string post
password string post
// validation aside from ajax now
login string is empty
redirect to login form with error
password string is empty
redirect to login form with error
// mysql
escape strings
clean html strings
mysql connect external mysql server
if login string is user
if password md5 match with database md5
session logged in
else
session failed password invalid
redirect to login form user/pass error
end if
else
session failed username invalid
redirect to login form user/pass error
end if
if file called direct
redirect 404
alert_admin function type hacking attempt login page
end if
mysql_real_escape_string() does not safeguard you from all forms of SQL Injection, or other types of attack for that matter. You should use a system in which incorperates code to guard against many safeguards individually, an example of such I use on my testing server (not strong enough for production):
function sanitize($str)
{
$str = trim($str);
if (get_magic_quotes_gpc())
$str = stripslashes($str);
return htmlentities(mysql_real_escape_string($str));
}
Please read the accepted answer for this question to see why any way you filter user input is never full-proof.
--
As far as information about securing user logins, please consider the following tips:
Avoid user input whenever possible, and if impossible; sanitize their input.
Do not use only md5 for securing user passwords. It is easy to decrypt.
Consider using a password salt, unique to each individual user.
Keep your own passwords both long, and diverse.
Optionally extend these as suggestions to your users' passwords. Example:
Must be at least six characters in length.
Must consist of a mixed case of characters.
Must contain at least one number.
(Secure) Must contain at least one symbol.
Rationale and statistics about password strength:
I, (with a nVidia NVS 3100M mobile graphics card), can crack or "brute force" an MD5 or SHA1 hash at a speed of 56,900,000 passwords per second. This means I can complete all passwords of lengths 1 - 6 characters, with a full (a-zA-Z0-9 + symbols) character set; in less than four minutes. Imagine what someone with a decent computer (even a gaming one), or a server could do.
The way to safe against this is to salt your passwords. Depending on how you salt your passwords, the "attacker" would need to try many different means of decrypting before they would be able to guess any of your user's passwords. If your password was not salted, they could brute-force it in the way I have described above.
Read more about PHP Session Security:
PHP Security Guide - Session Security
PHP Session Security (StackOverflow)
Notes on Session Security (SitePoint)
Also Worth Nothing:
You need to decide what your website needs to be secured against. If your website is hosted on a shared server or shared hosting (whether it be a VPN, VPS, or some sort of semi-dedicated solution) you will always be at risk of other malicious users on the system having access to your PHP files, and by extension; your MySQL database. Even on a dedicated server, without proper internal network security you are just as screwed.
Looking for a php login script. I've searched stackoverflow and have seen a lot of posts, but can anyone recommend the best method? Also, If I want to use hashing, how do you decode the password when retrieving? My iPhone app uses the same database and currently the passwords are stored in normal text (not very secure, I know).
Also, if I implement a login page that redirects to info.php, how do you stop the user from going directly to the info.php page without logging in, Session control?
Look forward to hearing your input. Thanks very much.
Bit late for my answer, but i'll post anyway and i know this doesn't directly answer the question but it is related non the less. Here are a few points about login security.
Remember Me
You are best to re-hash the hashed password, and store the rehash in the cookie used to automatically log the user in. When you rehash the password, use something specific to the browser as a seed such as the browser type. This will help prevent the cookie being stolen (via people snooping on the network traffic). This helps prevent any chance of using rainbow tables.
Sessions
Be aware of session hijacking: http://en.wikipedia.org/wiki/Session_hijacking
CSRF
Cross Site Request Forgery - Implemented after you login, but something to be aware of since it only affects logged in members: http://en.wikipedia.org/wiki/Cross-site_request_forgery
HTTPS
HTTPS should be used on the page the login request is being sent to - it does NOT have to be on the page you are typing your login details on!
Hashing
You can hash a password client side using javascript which would only server to protect members from having their passwords stolen while transfering them over the network when not using HTTPS. This is good because many people often use the same password for many sites. The disadvantages are: you can't check password length server side & they can't loggin if javascript is disabled (though you can program around that to a degree). Yahoo used to (might still do) do this years ago.
When you recieve the password on the server it is often rehashed using a seed and stored in the database. This is more secure as even if people know the hash, they still can't login! Only the original password will rehash to the same hash letting the user login.
Keyloggers
If you want to get around keyloggers (or most of them) you can do so by adding a JavaScript keypad / keyboard. The user then clicks the letters and numbers to enter their password using the mouse instead of the keyboard meaning the keylogger has a hard time logging the password.
Know Something, Has Something, Is Something
The three levels of security. Something someone knows such as a password, something someone has such as a phone (google completes these first 2 using 2 step verification), and something someone is such as a finger print. The more of these you fullfill the greater your security credentials are - by a long shot!!!
Bots
Computers sometimes try to brute force a fast login page (must be fast as if the password is incorrect and the script pauses for even just 1 second, that greatly reduces the total login attempts a bot can make. To help stop this you can either pause an incorrect login for 1 or 2 seconds (like linux does) or you can produce a capture that bots have a hard time solving after X incorrect logins (like google does).
There the main points, but i'm sure there are others.
This is a great tutorial on login system design. It covers all the major topics in an object oriented manner and is great for learning about the different considerations.
Decodable passwords are not as secure as they could be, but I've had clients insist that they be able to retrieve and change the password at will, no exceptions. So in some cases I opted to salt a base64 encoded string to store in the database, and that seems to work pretty well. A function exists to encode/decode as needed for the admin user.
Indeed, session control (and/or cookies) are the method to control access. Building it with an object oriented pattern would allow you to do that with just a line or two of code per page (or a line in a header if it's common).
My one warning is to consider if you have a common login level or need user-level permissions. It's significantly more work to decide after you've built the site that permissions-based logins are important. It can become a real monster if not planned for in the beginning.
POST to a HTTPS URL.
You never decode the hashed password. Lost passwords need another mechanism to handle.
Yes, session control. Set a flag in the session on login and check for it on the other pages.
Still under development, but secure, fast, clean, good and up-to-date:
[up to date version] https://github.com/Panique/PHP-Login
[old version's website] http://www.php-login.net
I've created this with some designer & coder help. It has been discussed in several forums and got very positive replies. All current issues can be seen on the github issue page. All code is public, free and forkable.
You don't need to decode the password, you will have to store the hashed password in your database, and when the user tries to login, you compare the stored password with the hash of the entered password.
About info.php, yes, if login succeeds you assign a variable in your session, and to test if the user is logged, you just test if that variable is assigned or not.
basically you hash your password so it cannot be retrieved for malicious purpose, the hash is stored in the database instead of the password in clear text, you only compare the 2 hash values.
your client can store the password as they want, but the web application should control at each step the session validity (store some logged in identifier in the session variables with proper expiration or something like that),
so basically you require("session_control.inc") in every "protected" page so you could check for the session validity.
The best course would be to use an MVC framework which could help in defining the logic in that case.
You can use password hashing but there is also php's crypt() function http://php.net/manual/en/function.crypt.php
They essentially do the same thing but crypt is a little neater IMO. Make sure you also get a good salt generation script so when you save the password in the database here is my password encryption function, notice this isn't that secure without the salt function
function crypt_password($password)
{
if($password){
//blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 base 64
$blowfish = '$2a$10$';
//get the random bytes and makes a salt
$salt = $this->get_salt();
//append salt2 data to the password, and crypt using salt, results in a 60 char output
$crypt_pass = crypt($password,$blowfish . $salt);
//blowfish comes out as 60, check
$len = strlen($crypt_pass);
if($len == 60)
{
return $crypt_pass;
}
else {
throw new Exception('encryption failed');
return false;
}
}
else {
throw new Exception('encryption failed, missing password');
return false;
}
}
and then when you want to verify this password you simply query the database for the login email or user id then to verify its as simple as
if (crypt($input_pass, $stored_pass) == $stored_pass) {
return true;
}
I am producing a script that others will put in their websites. It is designed for people with limited knowledge of PHP so that all they have to do is include() the script and set a few configuration variables. This means headers will probably have been sent already and so using sessions may not work. I recommend they call session_start in their own scripts, but I want a fallback option as well.
I already have a hidden input to deal with forms, but I also need links to have a query string appended to their URIs to identify the session. But if the hash is based only on the password+salt then there is a security risk: the logged-in user could click an external link and the owner of the external site could see the URI with the hash in their referrer logs. All they'd have to do is used that hash and they'd be logged in.
Therefore I want to salt the hash in a time-sensitive manner, limiting a session to 10 minutes. I can't figure out how to do this. Of course I can use time() to salt it, but how do I check how old the session is based only on the hash?
Expiring sessions after 10 minutes does not protect your users against session hijacking attacks. It only succeeds in annoying your users by forcing a login every 10 minutes. Your proposed scheme still has all kinds of vulnerabilities. Your salted hashed passwords can still leak to the outside world through many other channels; packet sniffing, intermediate proxies, users' emailing each other links to pages or even the saved html, just to name a few. I advise you not to homegrow you're own security framework without being an expert in the area. Even then, this is a solved problem. Just go with a known trusted solution. There are many subtleties in web security that are easy to mess up.
This sounds like a real bad idea. And complicated too. I would definitely not recommend it. You probably can use something like this:
if (session_id() == "") session_start();
The above will basically check if session has been started or not, and otherwise start session.
Since you planning to distribute to users, the whole approach seems a bit off to me. Am not sure what you are looking to achieve out of this, but you could try using a JS which calls your PHP file per page instead. This will make it easier for you. If you could elaborate on what kind of application you are developing, then I could probably help you out better. I have a lot of experience in mass consumer software apps similar to what you are doing.
This is not necessarily a bad idea, but it is dangerous if not done correctly. In fact, it is a fairly common implementation of multi-domain single sign-on using Hash-based Message Authentication Codes. Some ground rules:
Never ever include the password as part of the hash (even as the salt)
Require a timestamp generation as part of the hash that must be passed ALONG with the hash.
Each site to use this hash should have their own 32 or 64 byte guid to be used as a unique salt.
Pass specific data in the query string such as username, timestamp, anything else, and the HMAC. So it would look something like ?user=steve×tamp=66343532233&otherdata=otherdata&HMAC=AB3445-1234144-AFBBDEDD (you get the idea)
When site authentication is made cross-site, the HTTP_REFERER should be used (if possible) to get the key to generate the comparing HMAC.
Use a solid hashing algorithm (SHA1 is preferred) for generating the HMAC. Generate the the private site keys as randomly as possible. Do not use a standard derivation method, simply make sure that the end result is large enough/unique enough.