在PHP中安全地创建和销毁登录会话

时间:2013-03-19 20:46:05

标签: php security authentication

这是我控制网站身份验证的代码。我不确定我的逻辑是否正确。如果用户名和密码正确,则会发生以下情况:

if(session_start())
{
        session_regenerate_id(true);//without this the session ID will always be the same
        $_SESSION['loggedInUser'] = $uName;
        echo 'You are now logged in';
}
else echo 'Right password/username but session failed to start';

后续页面检查用户是否以

登录
session_start();
if(isset($_SESSION['loggedInUser'])
{
 //rest of page
}
else echo 'you must log in';

退出时我有

session_start();//if I don't have this the next line produces an error
session_unset();//destroys session variables
session_destroy();//ends session

我没有在注销时调用session_start(),但如果我没有注册,我会收到消息Trying to destroy uninitialized session。我该如何解决这个问题?

是否建议根据IP地址和用户代理创建指纹?我认为它很糟糕,因为多台计算机可以共享相同的IP地址(例如计算机实验室),并且所有流量都通过代理,同一台计算机可以更改它的IP地址(如果它是动态的)。另一方面,这种情况多久发生一次?可能值得阻止所有会话劫持的少数阻止有效用途。

即使您可以推荐信誉良好的文章,我也应该阅读以了解这个非常棒的主题,谢谢。

5/6答案的票数小于0 :(可能会对选民发表评论,所以我知道该注意什么?

5 个答案:

答案 0 :(得分:26)

首先,您应该阅读Mozilla WebAppSec Security Coding Guideline - Session ManagementOWASP A3-Broken Authentication and Session Management。您可configure PHP's session handler满足这些要求。

你要防止的第一个缺陷是A9-Insufficient Transport Layer Protection。简而言之,您不希望有人使用a tool like Firesheep劫持会话。可以通过强制浏览器仅通过https:

发送会话ID来防止此攻击
session.cookie_secure=1

您可以通过设置httponly flag来阻止攻击者使用XSS获取会话ID:

session.cookie_httponly=1

始终想要使用Cookie来存储您的会话ID。如果可以使用GET或POST变量传递会话ID,则攻击者可以使用Session Fixation attack来劫持会话。另一种思考此攻击的方法是,您不希望攻击者为其他用户创建会话:

session.use_cookies=1
session.use_only_cookies=1

接下来,您要确保从CSPRNG获得至少128位的熵。在* nix系统下,您可以使用/dev/urandom

session.entropy_file="/dev/urandom"
session.entropy_length=16

会话处理程序不是一切。您仍然需要担心Cross-Site Request Forgery attacks(又称CSRF或“会话骑行”)和跨站点脚本(XSS)。 XSS可用于破坏CSRF保护(即使使用http_only cookie!)。攻击者也可以使用Clickjacking来执行未经授权的操作。

设置完这些配置选项后,只需拨打session_start()即可。至于在用户注销时销毁会话调用session_destroy(),就这么简单!

答案 1 :(得分:0)

为了安全地销毁会话,我将使用以下代码:

session_start();
// Unset all session values
$_SESSION = array();
// get session parameters 
$params = session_get_cookie_params();
// Delete the actual cookie.
setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"]);
// Destroy session
session_destroy();

为了销毁一个会话,你需要先启动它,因为你发现如果你不包含session_start();它就不起作用。

session_regenerate_id();函数为用户生成新的会话ID。如果与true一起使用(session_regenerate_id(true);)则会在生成新会话ID时从服务器中删除旧会话ID。在每个页面上生成新会话ID的原因是,由于用户不断更改会话ID,会使会话劫持更​​难(几乎不可能?)。

(查看session_regenerate_id();上的PHP.net手册)

在对用户进行身份验证时,您应该始终检查IP地址或浏览器之类的内容,这些是在请求中发送给服务器的常量内容,这些内容在会话的生命周期内不会发生变化,如果他们这样做,那么您就会知道某些内容它发生了狡猾。我总是创建两个会话变量,它存储用户ID,因此我可以在数据库中查询数据,另一个在一个哈希值中存储用户密码,IP地址和浏览器字符串(sha512)。

$user_id = $_SESSION['user_id'];
$login_string = $_SESSION['login_string'];

// Query Database and get hashed password

$login_check = hash('sha512', $password.$ip_address.$user_browser);

if($login_check == $login_string) {
     // Logged In!!!!
     return true;
} else {
     // Not logged in
     return false;
}

即使密码存储在会话中,密码也是安全的。这是因为密码是哈希的(在这种情况下是两次),并且因为会话数据没有存储在用户计算机上(如cookie),所以它存储在会话文件中。

我在wikihow.com上写了一篇关于安全登录和身份验证的文章,可以找到here

答案 2 :(得分:0)

你可以写:

session_start(); // session should be started before it can be used.

您可以指定已登录成员的用户ID。为此,您可以从用户输入中获取用户名和密码,并在您的数据库中检查它并返回用户ID。为了更安全,您可以使用例如字符串。 “demo”和“test”只是md5,并以下列方式与userid混合。

$userid=md5("demo").$userid.md5("test");// you can set any string instead of demo and test.

$_SESSION['userid']=$userid;

在其他页面中使用它时,

session_start(); // If you are have not started it or included above code file in it.

正如您所知,字符串在使用时只是匹配它并从中找到确切的用户ID并在您的代码中使用它。

为了摧毁它,只需使用:

session_unset($_SESSION['userid']); // It will only unset the session userid completely. 

确保在使用任何会话之前需要启动它。更好的方法是,您可以在一个文件中启动会话,例如init.php,并将其包含在您希望使用会话的每个位置

答案 3 :(得分:0)

您可以先使用session_id()来确定用户是否已有会话,如果没有,则使用session_start()

来自Lithium framewrok的示例代码:

/**
 * Starts the session.
 *
 * @return boolean True if session successfully started (or has already been started),
 *         false otherwise.
 */
protected static function _start() {
    if (session_id()) {
        return true;
    }
    ...
    return session_start();
}

致电_start()后,您可以安全地拨打session_destroy()

答案 4 :(得分:0)

要在不使用“start_session()”的情况下销毁会话,请首先验证是否存在不像下面的活动会话

$existingSessionId = session_id();
if ($existingSessionId != "")
{
     // Initialize the session.
     session_start();

     // Unset all of the session variables.
     $_SESSION = array();

     // If it's desired to kill the session, also delete the session cookie.
     // Note: This will destroy the session, and not just the session data!
     if (ini_get("session.use_cookies")) {
         $params = session_get_cookie_params();
         setcookie(session_name(), '', time() - 42000,
             $params["path"], $params["domain"],
             $params["secure"], $params["httponly"]
         );
     }

     // Finally, destroy the session.
     session_destroy();
}
else
{
     // No Active sessions
}

session_regenerate_id(true),只需将旧会话ID替换为新会话ID,但不会取消设置旧会话ID。这需要由session_destroy和删除会话cookie来处理。

浏览器会将会话cookie发送到服务器,会话被销毁。 PHP将获取此会话ID,当您执行start_session()时,它将使用浏览器发送的会话ID。如果删除会话cookie,session_start将生成新的会话ID,您不需要调用session_regenerate_id()