我想弄清楚 cookie/会话变量如何交互以验证用户会话
I want to figure out how cookies/ session variables interact to validate a user session
---我一直在试验 cookies/session ids,但在理解这些概念时有点困难。
我正在开发 Debian 发行版。使用 Burp 来 capture/alter 请求/响应。我所知道的如下。
一个。 Cookie 存储在 /root/.mozilla/firefox/pya18ecc.default/cookies.sqlite 的客户端计算机数据库中。在moz_cookiestable。我正在使用 sqlite3 访问数据库。
b。会话变量正在存储在服务器上的 /var/lib/PHP5 中。
服务器上的PHP代码如下
<?php
require_once 'login.php';
$connection = new mysqli($db_hostname,$db_username,$db_password,$db_database);
if($connection->connect_error) die ($connect->connect_error);
if (isset($_SERVER['PHP_AUTH_USER']) &&
isset($_SERVER['PHP_AUTH_PW']))
{
$username = mysql_entities_fix($connection,$_SERVER['PHP_AUTH_USER']);
$password = mysql_entities_fix($connection,$_SERVER['PHP_AUTH_PW']);
$query = "SELECT * FROM user WHERE username = '$username'";
$result = $connection->query($query);
if(!$result) die ($connection->error);
elseif ($result->num_rows)
{
$row = $result->fetch_array(MYSQLI_NUM);
$result->close();
$salt1="!@#$";
$salt2="$#@!";
$token = hash('ripemd128',"$salt1$password$salt2");
if($token == $row[3])
{
session_start();
$_SESSION['username'] = $username;
$_SESSION['password'] = $password;
$_SESSION['forename'] = $row[0];
$_SESSION['surname'] = $row[1];
echo "$row[0] $row[1] : Hi '$row[0]' you are logged
in as '$row[2]'";
die("<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>");
}
else {die("Invalid Username/ Password Combination");}
}
else
{
die("Invalid Username/ Password Combination");
}
}
else
{
header('WWW-Authenticate: Basic realm="Restricted Section"');
header('HTTP/1.0 401 Unauthorized');
die("Please enter your username and password to Login");
}
$connection->close();
function mysql_entities_fix($connection,$var)
{
return htmlentities(mysql_entities_string($connection,$var));
}
function mysql_entities_string($connection,$var)
{
if (get_magic_quotes_gpc()) $var = stripslahes($var);
return $connection->real_escape_string($var);
}
?>
- 当我发送请求(1)时,它看起来像这样。
请求(1)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
- 响应(1)是登录页面(基本HTTP认证)
回复(1)
HTTP/1.0 401 Unauthorized
Date: Sat, 28 Mar 2015 07:27:44 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
WWW-Authenticate: Basic realm="Restricted Section"
Vary: Accept-Encoding
Content-Length: 48
Connection: close
Content-Type: text/html
Please enter your username and password to Login
----到这里为止还没有会话 ID 或 cookie 的交换。 (如果我错了,提示我?)
- 我回复了用户名和密码,请求(2)是
请求(2)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Authorization: Basic YnNtaXRoOm15c2VjcmV0
---- 在转发上述请求时,我观察到我在 /var/lib/PHP5 中收到了会话 ID:cl5mi7tbhdnobpv8kkau7thjo6,甚至在转发响应 (2) 之前。那是因为服务器
已经创建了相同的并准备在响应中转发它 (2)
- 响应(2)是
回复(2)
HTTP/1.1 200 OK
Date: Sat, 28 Mar 2015 07:36:13 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
Set-Cookie: PHPSESSID=cl5mi7tbhdnobpv8kkau7thjo6; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must- revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 117
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
Bill Smith : Hi 'Bill' you are logged
in as 'bsmith'<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>
----现在检查 SELECT * FROM moz_cookies,我没有看到任何保存在客户端 m/c 上的 cookie。那么他们在哪里得救呢? (这是我的第一个问题)
- 接下来,我从服务器中删除了会话变量 "cl5mi7tbhdnobpv8kkau7thjo6",然后再次点击
客户端浏览器上的刷新按钮。请求(3)/响应(3)低于
请求(3)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=cl5mi7tbhdnobpv8kkau7thjo6
Authorization: Basic YnNtaXRoOm15c2VjcmV0
Connection: keep-alive
Cache-Control: max-age=0
响应(3)
HTTP/1.1 200 OK
Date: Sat, 28 Mar 2015 07:50:01 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 117
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
Bill Smith : Hi 'Bill' you are logged
in as 'bsmith'<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>
-----现在服务器再次在 /var/lib/PHP5 下设置了相同的会话变量。服务器不应该不识别会话变量吗?另外,如果这是由于保存的 Cookie 而发生的,那么为什么我无法在 moz_cookies table 下看到它们......请解释一下?(这是我的第二个问题)
需要考虑的几点:
Cookie 可以存储在磁盘或内存中。它们的存储方式取决于浏览器设置(例如 Firefox 中的隐私浏览模式)和服务器设置(参见 PHP docs on session handling)。
可以使用 Keep-Alive header.
缓存 HTTP 连接
看起来您已设置为将服务器上的 session ID 存储在文件中,但浏览器 cookie 存储在内存中而不是文件中,这就是为什么您会看到 PHP 文件但不是 Firefox cookie 文件。
至于为什么即使您删除了文件,服务器仍能识别出 session id,看起来发生的事情是 PHP 知道 session 已通过身份验证,这意味着它必须缓存在服务器的 RAM 中。 (PHP 大师也许能够填写 session 机制的细节)。
收到请求3后,PHP查找sessionid文件,没找到就重新写入文件,因为它知道session是有效的。这是预期的行为,因为如果你想删除 session,你作为一个 PHP 程序员应该在 PHP 中删除而不是删除后面的 session id 文件场景。
您会注意到客户端在请求 3 中发送了 session id。客户端将在每个请求中发送 session id cookie,但服务器不应响应 Set-Cookie 在此期间再次指令 session。浏览器现在有 session id cookie 并且可以在每个请求中发送它,因此不需要进一步的身份验证。
此策略的弱点在于它可能会留下 session 劫持的可能性。如果攻击者可以从您的浏览器获取 session id cookie,他们就可以使用您的凭据对服务器执行命令。
获取 session id 对于攻击者来说可能是也可能不是微不足道的任务,这取决于许多因素。例如,如果受害者的计算机受到攻击者控制的病毒或其他恶意软件的危害,攻击者可能会在此时伪造受害者的计算机并可以为所欲为,包括获取 session id。其他攻击使用 cross-site 脚本 (XSS) 攻击来诱骗受害者的浏览器泄露 session id。
有针对XSS和session劫持的具体防御措施,同样这个主题相当复杂,值得详细研究。例如,更安全的 session 可能会使用随每次响应而改变的 one-time 令牌,尽管使用异步 HTTP 调用可能会使该策略变得复杂。
Till here there has no exchange of session ids or cookies. (If I'm wrong, prompt me ?)
不,没错。直到这里,session_start
还没有被调用,所以没有会话 cookies 可以交换。
Now the Server has again got the same session variable set under the /var/lib/PHP5. Shouldn't the server not recognize the session variable ?
它可能应该,但事实并非如此。我认为正在发生的事情:当客户端发送会话 ID 时,服务器很乐意使用它,即使它还不知道。这可以(理论上)用于 session fixation,这就是为什么建议每次会话中的某些内容更改时重新生成 id(实际上,session.use_trans_sid
默认设置为 0
,这使得会话固定不再是一个问题)。
我觉得奇怪的是数据还在那里(我无法复制这种行为)。我猜您实际上是在重新提交表格?
Also if this is happening because of the saved Cookies, then why isn't I'm able to see them under moz_cookies table
我的猜测是 cookie 仍在浏览器内存中。您是否尝试过同样的操作,但通过浏览器界面删除了 cookie?
其他
我知道这只是一个测试脚本,但仍然:
- 建议使用准备好的语句而不是简单的转义。
- 不要在会话中存储明文密码。
- 不要回显未经处理的用户输入。
- 哈希应该是特定于用户的,而不是特定于站点的。
- htmlentities 防止 XSS,而不是 SQL 注入,因此应该在回显数据时应用,而不是在将数据插入数据库时应用。
---我一直在试验 cookies/session ids,但在理解这些概念时有点困难。 我正在开发 Debian 发行版。使用 Burp 来 capture/alter 请求/响应。我所知道的如下。
一个。 Cookie 存储在 /root/.mozilla/firefox/pya18ecc.default/cookies.sqlite 的客户端计算机数据库中。在moz_cookiestable。我正在使用 sqlite3 访问数据库。
b。会话变量正在存储在服务器上的 /var/lib/PHP5 中。
服务器上的PHP代码如下
<?php
require_once 'login.php';
$connection = new mysqli($db_hostname,$db_username,$db_password,$db_database);
if($connection->connect_error) die ($connect->connect_error);
if (isset($_SERVER['PHP_AUTH_USER']) &&
isset($_SERVER['PHP_AUTH_PW']))
{
$username = mysql_entities_fix($connection,$_SERVER['PHP_AUTH_USER']);
$password = mysql_entities_fix($connection,$_SERVER['PHP_AUTH_PW']);
$query = "SELECT * FROM user WHERE username = '$username'";
$result = $connection->query($query);
if(!$result) die ($connection->error);
elseif ($result->num_rows)
{
$row = $result->fetch_array(MYSQLI_NUM);
$result->close();
$salt1="!@#$";
$salt2="$#@!";
$token = hash('ripemd128',"$salt1$password$salt2");
if($token == $row[3])
{
session_start();
$_SESSION['username'] = $username;
$_SESSION['password'] = $password;
$_SESSION['forename'] = $row[0];
$_SESSION['surname'] = $row[1];
echo "$row[0] $row[1] : Hi '$row[0]' you are logged
in as '$row[2]'";
die("<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>");
}
else {die("Invalid Username/ Password Combination");}
}
else
{
die("Invalid Username/ Password Combination");
}
}
else
{
header('WWW-Authenticate: Basic realm="Restricted Section"');
header('HTTP/1.0 401 Unauthorized');
die("Please enter your username and password to Login");
}
$connection->close();
function mysql_entities_fix($connection,$var)
{
return htmlentities(mysql_entities_string($connection,$var));
}
function mysql_entities_string($connection,$var)
{
if (get_magic_quotes_gpc()) $var = stripslahes($var);
return $connection->real_escape_string($var);
}
?>
- 当我发送请求(1)时,它看起来像这样。
请求(1)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
- 响应(1)是登录页面(基本HTTP认证)
回复(1)
HTTP/1.0 401 Unauthorized
Date: Sat, 28 Mar 2015 07:27:44 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
WWW-Authenticate: Basic realm="Restricted Section"
Vary: Accept-Encoding
Content-Length: 48
Connection: close
Content-Type: text/html
Please enter your username and password to Login
----到这里为止还没有会话 ID 或 cookie 的交换。 (如果我错了,提示我?)
- 我回复了用户名和密码,请求(2)是
请求(2)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Authorization: Basic YnNtaXRoOm15c2VjcmV0
---- 在转发上述请求时,我观察到我在 /var/lib/PHP5 中收到了会话 ID:cl5mi7tbhdnobpv8kkau7thjo6,甚至在转发响应 (2) 之前。那是因为服务器 已经创建了相同的并准备在响应中转发它 (2)
- 响应(2)是
回复(2)
HTTP/1.1 200 OK
Date: Sat, 28 Mar 2015 07:36:13 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
Set-Cookie: PHPSESSID=cl5mi7tbhdnobpv8kkau7thjo6; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must- revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 117
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
Bill Smith : Hi 'Bill' you are logged
in as 'bsmith'<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>
----现在检查 SELECT * FROM moz_cookies,我没有看到任何保存在客户端 m/c 上的 cookie。那么他们在哪里得救呢? (这是我的第一个问题)
- 接下来,我从服务器中删除了会话变量 "cl5mi7tbhdnobpv8kkau7thjo6",然后再次点击 客户端浏览器上的刷新按钮。请求(3)/响应(3)低于
请求(3)
GET /ses3.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=cl5mi7tbhdnobpv8kkau7thjo6
Authorization: Basic YnNtaXRoOm15c2VjcmV0
Connection: keep-alive
Cache-Control: max-age=0
响应(3)
HTTP/1.1 200 OK
Date: Sat, 28 Mar 2015 07:50:01 GMT
Server: Apache/2.2.22 (Debian)
X-Powered-By: PHP/5.4.36-0+deb7u3
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 117
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html
Bill Smith : Hi 'Bill' you are logged
in as 'bsmith'<p><a href = continue.php> CLICK HERE TO CONTINUE</a></p>
-----现在服务器再次在 /var/lib/PHP5 下设置了相同的会话变量。服务器不应该不识别会话变量吗?另外,如果这是由于保存的 Cookie 而发生的,那么为什么我无法在 moz_cookies table 下看到它们......请解释一下?(这是我的第二个问题)
需要考虑的几点:
Cookie 可以存储在磁盘或内存中。它们的存储方式取决于浏览器设置(例如 Firefox 中的隐私浏览模式)和服务器设置(参见 PHP docs on session handling)。
可以使用 Keep-Alive header.
缓存 HTTP 连接
看起来您已设置为将服务器上的 session ID 存储在文件中,但浏览器 cookie 存储在内存中而不是文件中,这就是为什么您会看到 PHP 文件但不是 Firefox cookie 文件。
至于为什么即使您删除了文件,服务器仍能识别出 session id,看起来发生的事情是 PHP 知道 session 已通过身份验证,这意味着它必须缓存在服务器的 RAM 中。 (PHP 大师也许能够填写 session 机制的细节)。
收到请求3后,PHP查找sessionid文件,没找到就重新写入文件,因为它知道session是有效的。这是预期的行为,因为如果你想删除 session,你作为一个 PHP 程序员应该在 PHP 中删除而不是删除后面的 session id 文件场景。
您会注意到客户端在请求 3 中发送了 session id。客户端将在每个请求中发送 session id cookie,但服务器不应响应 Set-Cookie 在此期间再次指令 session。浏览器现在有 session id cookie 并且可以在每个请求中发送它,因此不需要进一步的身份验证。
此策略的弱点在于它可能会留下 session 劫持的可能性。如果攻击者可以从您的浏览器获取 session id cookie,他们就可以使用您的凭据对服务器执行命令。
获取 session id 对于攻击者来说可能是也可能不是微不足道的任务,这取决于许多因素。例如,如果受害者的计算机受到攻击者控制的病毒或其他恶意软件的危害,攻击者可能会在此时伪造受害者的计算机并可以为所欲为,包括获取 session id。其他攻击使用 cross-site 脚本 (XSS) 攻击来诱骗受害者的浏览器泄露 session id。
有针对XSS和session劫持的具体防御措施,同样这个主题相当复杂,值得详细研究。例如,更安全的 session 可能会使用随每次响应而改变的 one-time 令牌,尽管使用异步 HTTP 调用可能会使该策略变得复杂。
Till here there has no exchange of session ids or cookies. (If I'm wrong, prompt me ?)
不,没错。直到这里,session_start
还没有被调用,所以没有会话 cookies 可以交换。
Now the Server has again got the same session variable set under the /var/lib/PHP5. Shouldn't the server not recognize the session variable ?
它可能应该,但事实并非如此。我认为正在发生的事情:当客户端发送会话 ID 时,服务器很乐意使用它,即使它还不知道。这可以(理论上)用于 session fixation,这就是为什么建议每次会话中的某些内容更改时重新生成 id(实际上,session.use_trans_sid
默认设置为 0
,这使得会话固定不再是一个问题)。
我觉得奇怪的是数据还在那里(我无法复制这种行为)。我猜您实际上是在重新提交表格?
Also if this is happening because of the saved Cookies, then why isn't I'm able to see them under moz_cookies table
我的猜测是 cookie 仍在浏览器内存中。您是否尝试过同样的操作,但通过浏览器界面删除了 cookie?
其他
我知道这只是一个测试脚本,但仍然:
- 建议使用准备好的语句而不是简单的转义。
- 不要在会话中存储明文密码。
- 不要回显未经处理的用户输入。
- 哈希应该是特定于用户的,而不是特定于站点的。
- htmlentities 防止 XSS,而不是 SQL 注入,因此应该在回显数据时应用,而不是在将数据插入数据库时应用。