Prevent session hijacking, fixation, injection, etc
(Note: I posted this on Stack Overflow and was referred here, where I will repost)
I'm creating a login system and I have been reading a lot about the security measures needed to prevent session hijacking, fixation, and injection attacks, etc. I'm definitely not a security expert - I pieced together a lot of this with help from posts on this site and various others. These two Stack Overflow posts were particularly helpful: Link Link 2
After reading those, I ended up making a lot of changes and improvements to my system, but I'm not sure I'm protected against everything. Below is what I've come up with so far. I suspect some of it will be right, some of it will be wrong, and some may be superfluous. Here's the basic flow of what happens:
All database queries use PDO prepared statements.
When a new user is created, I use PHP's password_hash() to hash it then store that value in the database.
When a user logs in, I use password_verify() to compare the submitted password to the one stored in the database.
If the password authenticates, I create the session and define some session variables like this:
ini_set('session.use_trans_sid', FALSE);
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.hash_function', 'whirlpool');
ini_set('session.use_only_cookies', TRUE);
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_lifetime', 1200);
session_start();
$_SESSION['user_ip'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
I then use password_hash() to make a hash of $_SESSION['HTTP_USER_AGENT']. That value, along with the user's IP address are stored in the database. These are used in #7 below.
The user is now logged in. This function protects pages that require you to be logged in to access:
function page_protect() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(!isset($_SESSION['HTTP_USER_AGENT']) ||
$_SESSION['HTTP_USER_AGENT'] != $_SERVER['HTTP_USER_AGENT']) {
logout();
exit;
}
if(!isset($_SESSION['user_ip']) || $_SESSION['user_ip'] !=
$_SERVER['REMOTE_ADDR']) {
logout();
exit;
}
}
Next, I retrieve the IP address ($user_ip) and hashed $_SESSION['HTTP_USER_AGENT'] ($useragent_hash) stored in the database and compare them with the current session variables.
if($_SESSION['user_ip'] != $user_ip ||
!password_verify($_SESSION['HTTP_USER_AGENT'], $useragent_hash)) {
logout();
exit;
}
Lastly, the logout() function referenced above looks like this:
function logout() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
unset($_SESSION);
session_unset();
session_destroy();
header("Location: index.php");
exit();
}
1) Am I missing anything or does anything look wrong?
2) I'm not using session_regenerate_id() anywhere. I think this needs to be done periodically, but I'm not sure where it fits in. Maybe put it in page_protect() so it's called every time the user visits a new page?
3) All of the ini_set's in #4 only appear before the session_start() in the login function. However, the top of each page protected with page_protect() has a session_start(). Should it be preceded by all of the same ini_set's on every page, or once they're set during the initial login, they stay set?
4) Should I remove this line? ini_set('session.cookie_lifetime', 1200); That would require the user to login again after 20 minutes. I think it would be good to log the user out after 20 minutes of inactivity, but not after 20 minutes of moving around the site.
5) In step #7, would it be better to use && instead of ||?
Any help is greatly appreciated.
php security
New contributor
add a comment |
(Note: I posted this on Stack Overflow and was referred here, where I will repost)
I'm creating a login system and I have been reading a lot about the security measures needed to prevent session hijacking, fixation, and injection attacks, etc. I'm definitely not a security expert - I pieced together a lot of this with help from posts on this site and various others. These two Stack Overflow posts were particularly helpful: Link Link 2
After reading those, I ended up making a lot of changes and improvements to my system, but I'm not sure I'm protected against everything. Below is what I've come up with so far. I suspect some of it will be right, some of it will be wrong, and some may be superfluous. Here's the basic flow of what happens:
All database queries use PDO prepared statements.
When a new user is created, I use PHP's password_hash() to hash it then store that value in the database.
When a user logs in, I use password_verify() to compare the submitted password to the one stored in the database.
If the password authenticates, I create the session and define some session variables like this:
ini_set('session.use_trans_sid', FALSE);
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.hash_function', 'whirlpool');
ini_set('session.use_only_cookies', TRUE);
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_lifetime', 1200);
session_start();
$_SESSION['user_ip'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
I then use password_hash() to make a hash of $_SESSION['HTTP_USER_AGENT']. That value, along with the user's IP address are stored in the database. These are used in #7 below.
The user is now logged in. This function protects pages that require you to be logged in to access:
function page_protect() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(!isset($_SESSION['HTTP_USER_AGENT']) ||
$_SESSION['HTTP_USER_AGENT'] != $_SERVER['HTTP_USER_AGENT']) {
logout();
exit;
}
if(!isset($_SESSION['user_ip']) || $_SESSION['user_ip'] !=
$_SERVER['REMOTE_ADDR']) {
logout();
exit;
}
}
Next, I retrieve the IP address ($user_ip) and hashed $_SESSION['HTTP_USER_AGENT'] ($useragent_hash) stored in the database and compare them with the current session variables.
if($_SESSION['user_ip'] != $user_ip ||
!password_verify($_SESSION['HTTP_USER_AGENT'], $useragent_hash)) {
logout();
exit;
}
Lastly, the logout() function referenced above looks like this:
function logout() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
unset($_SESSION);
session_unset();
session_destroy();
header("Location: index.php");
exit();
}
1) Am I missing anything or does anything look wrong?
2) I'm not using session_regenerate_id() anywhere. I think this needs to be done periodically, but I'm not sure where it fits in. Maybe put it in page_protect() so it's called every time the user visits a new page?
3) All of the ini_set's in #4 only appear before the session_start() in the login function. However, the top of each page protected with page_protect() has a session_start(). Should it be preceded by all of the same ini_set's on every page, or once they're set during the initial login, they stay set?
4) Should I remove this line? ini_set('session.cookie_lifetime', 1200); That would require the user to login again after 20 minutes. I think it would be good to log the user out after 20 minutes of inactivity, but not after 20 minutes of moving around the site.
5) In step #7, would it be better to use && instead of ||?
Any help is greatly appreciated.
php security
New contributor
add a comment |
(Note: I posted this on Stack Overflow and was referred here, where I will repost)
I'm creating a login system and I have been reading a lot about the security measures needed to prevent session hijacking, fixation, and injection attacks, etc. I'm definitely not a security expert - I pieced together a lot of this with help from posts on this site and various others. These two Stack Overflow posts were particularly helpful: Link Link 2
After reading those, I ended up making a lot of changes and improvements to my system, but I'm not sure I'm protected against everything. Below is what I've come up with so far. I suspect some of it will be right, some of it will be wrong, and some may be superfluous. Here's the basic flow of what happens:
All database queries use PDO prepared statements.
When a new user is created, I use PHP's password_hash() to hash it then store that value in the database.
When a user logs in, I use password_verify() to compare the submitted password to the one stored in the database.
If the password authenticates, I create the session and define some session variables like this:
ini_set('session.use_trans_sid', FALSE);
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.hash_function', 'whirlpool');
ini_set('session.use_only_cookies', TRUE);
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_lifetime', 1200);
session_start();
$_SESSION['user_ip'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
I then use password_hash() to make a hash of $_SESSION['HTTP_USER_AGENT']. That value, along with the user's IP address are stored in the database. These are used in #7 below.
The user is now logged in. This function protects pages that require you to be logged in to access:
function page_protect() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(!isset($_SESSION['HTTP_USER_AGENT']) ||
$_SESSION['HTTP_USER_AGENT'] != $_SERVER['HTTP_USER_AGENT']) {
logout();
exit;
}
if(!isset($_SESSION['user_ip']) || $_SESSION['user_ip'] !=
$_SERVER['REMOTE_ADDR']) {
logout();
exit;
}
}
Next, I retrieve the IP address ($user_ip) and hashed $_SESSION['HTTP_USER_AGENT'] ($useragent_hash) stored in the database and compare them with the current session variables.
if($_SESSION['user_ip'] != $user_ip ||
!password_verify($_SESSION['HTTP_USER_AGENT'], $useragent_hash)) {
logout();
exit;
}
Lastly, the logout() function referenced above looks like this:
function logout() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
unset($_SESSION);
session_unset();
session_destroy();
header("Location: index.php");
exit();
}
1) Am I missing anything or does anything look wrong?
2) I'm not using session_regenerate_id() anywhere. I think this needs to be done periodically, but I'm not sure where it fits in. Maybe put it in page_protect() so it's called every time the user visits a new page?
3) All of the ini_set's in #4 only appear before the session_start() in the login function. However, the top of each page protected with page_protect() has a session_start(). Should it be preceded by all of the same ini_set's on every page, or once they're set during the initial login, they stay set?
4) Should I remove this line? ini_set('session.cookie_lifetime', 1200); That would require the user to login again after 20 minutes. I think it would be good to log the user out after 20 minutes of inactivity, but not after 20 minutes of moving around the site.
5) In step #7, would it be better to use && instead of ||?
Any help is greatly appreciated.
php security
New contributor
(Note: I posted this on Stack Overflow and was referred here, where I will repost)
I'm creating a login system and I have been reading a lot about the security measures needed to prevent session hijacking, fixation, and injection attacks, etc. I'm definitely not a security expert - I pieced together a lot of this with help from posts on this site and various others. These two Stack Overflow posts were particularly helpful: Link Link 2
After reading those, I ended up making a lot of changes and improvements to my system, but I'm not sure I'm protected against everything. Below is what I've come up with so far. I suspect some of it will be right, some of it will be wrong, and some may be superfluous. Here's the basic flow of what happens:
All database queries use PDO prepared statements.
When a new user is created, I use PHP's password_hash() to hash it then store that value in the database.
When a user logs in, I use password_verify() to compare the submitted password to the one stored in the database.
If the password authenticates, I create the session and define some session variables like this:
ini_set('session.use_trans_sid', FALSE);
ini_set('session.entropy_file', '/dev/urandom');
ini_set('session.hash_function', 'whirlpool');
ini_set('session.use_only_cookies', TRUE);
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_lifetime', 1200);
session_start();
$_SESSION['user_ip'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
I then use password_hash() to make a hash of $_SESSION['HTTP_USER_AGENT']. That value, along with the user's IP address are stored in the database. These are used in #7 below.
The user is now logged in. This function protects pages that require you to be logged in to access:
function page_protect() {
if(session_status() == PHP_SESSION_NONE) {
session_start();
}
if(!isset($_SESSION['HTTP_USER_AGENT']) ||
$_SESSION['HTTP_USER_AGENT'] != $_SERVER['HTTP_USER_AGENT']) {
logout();
exit;
}
if(!isset($_SESSION['user_ip']) || $_SESSION['user_ip'] !=
$_SERVER['REMOTE_ADDR']) {
logout();
exit;
}
}
Next, I retrieve the IP address ($user_ip) and hashed $_SESSION['HTTP_USER_AGENT'] ($useragent_hash) stored in the database and compare them with the current session variables.
if($_SESSION['user_ip'] != $user_ip ||
!password_verify($_SESSION['HTTP_USER_AGENT'], $useragent_hash)) {
logout();
exit;
}
Lastly, the logout() function referenced above looks like this:
function logout() {
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
unset($_SESSION);
session_unset();
session_destroy();
header("Location: index.php");
exit();
}
1) Am I missing anything or does anything look wrong?
2) I'm not using session_regenerate_id() anywhere. I think this needs to be done periodically, but I'm not sure where it fits in. Maybe put it in page_protect() so it's called every time the user visits a new page?
3) All of the ini_set's in #4 only appear before the session_start() in the login function. However, the top of each page protected with page_protect() has a session_start(). Should it be preceded by all of the same ini_set's on every page, or once they're set during the initial login, they stay set?
4) Should I remove this line? ini_set('session.cookie_lifetime', 1200); That would require the user to login again after 20 minutes. I think it would be good to log the user out after 20 minutes of inactivity, but not after 20 minutes of moving around the site.
5) In step #7, would it be better to use && instead of ||?
Any help is greatly appreciated.
php security
php security
New contributor
New contributor
New contributor
asked 3 mins ago
John
1
1
New contributor
New contributor
add a comment |
add a comment |
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
John is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210584%2fprevent-session-hijacking-fixation-injection-etc%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
John is a new contributor. Be nice, and check out our Code of Conduct.
John is a new contributor. Be nice, and check out our Code of Conduct.
John is a new contributor. Be nice, and check out our Code of Conduct.
John is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210584%2fprevent-session-hijacking-fixation-injection-etc%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown