好的,有两个独立但相关的问题,每个问题都有不同的处理方式。
会话固定
这是攻击者明确设置用户会话标识符的地方。通常在PHP中,通过给他们一个类似于
http://www.example.com/index...?session_name=sessionid
的URL来实现。一旦攻击者将URL提供给客户端,攻击就与会话劫持攻击相同。
有几种方法可以防止会话固定(请全部采用):
在你的php.ini文件中设置
session.use_trans_sid = 0
。这将告诉PHP不在URL中包含标识符,并且不读取URL中的标识符。
在你的php.ini文件中设置
session.use_only_cookies = 1
。这将告诉PHP永远不要使用带有会话标识符的URL。
每当会话状态发生变化时重新生成会话ID。这意味着以下任何一种情况:
- 用户认证
- 在会话中存储敏感信息
- 更改会话的任何内容
- 等等...
会话劫持
这是攻击者获取会话标识符并能够发送请求,仿佛他们是该用户的地方。这意味着由于攻击者拥有标识符,他们在服务器方面几乎无法与有效用户区分开来。
你无法直接防止会话劫持。但是,你可以采取一些措施,使其变得非常困难和难以使用。
在php.ini
中使用一个强大的会话哈希标识符:session.hash_function
。如果PHP版本小于5.3,则将其设置为session.hash_function = 1
以使用SHA1。如果PHP版本大于等于5.3,则将其设置为session.hash_function = sha256
或session.hash_function = sha512
。
在php.ini
中发送一个强大的哈希:session.hash_bits_per_character
。将其设置为session.hash_bits_per_character = 5
。虽然这并不会使破解变得更加困难,但在攻击者尝试猜测会话标识符时会有所不同。会话ID会更短,但使用更多的字符。
在php.ini
文件中使用session.entropy_file
和session.entropy_length
设置额外的熵。将前者设置为session.entropy_file = /dev/urandom
,将后者设置为从熵文件中读取的字节数,例如session.entropy_length = 256
。
将会话的名称从默认的PHPSESSID更改为其他名称。在调用session_start
之前,通过将自己的标识符名称作为第一个参数调用session_name()
来实现。
如果你真的很偏执,你也可以定期更改会话名称,但要注意,如果你更改了会话名称(例如,如果你将其与时间相关联),所有会话将自动失效。但根据你的用例,这可能是一个选择...
经常更换会话标识符。我不会在每个请求中都这样做(除非你真的需要那种级别的安全性),而是在随机的时间间隔内更换。你希望经常更改这个,因为如果攻击者劫持了一个会话,你不希望他们能够长时间使用它。
在会话中包含来自$_SERVER['HTTP_USER_AGENT']
的用户代理。基本上,当会话开始时,将其存储在类似$_SESSION['user_agent']
的变量中。然后,在每个后续请求中检查它是否匹配。请注意,这可以被伪造,所以它并不是100%可靠,但比没有好。
在会话中包含来自$_SERVER['REMOTE_ADDR']
的用户IP地址。基本上,当会话开始时,将其存储在类似$_SESSION['remote_ip']
的变量中。这可能会对一些使用多个IP地址的ISP(例如AOL曾经使用的方式)造成问题。但如果你使用它,它将更加安全。攻击者伪造IP地址的唯一方法是在真实用户和你之间的某个网络上进行入侵。如果他们入侵了网络,他们可以做比劫持更糟糕的事情(例如中间人攻击等)。
在会话和浏览器端包含一个令牌,你可以经常递增并进行比较。基本上,对于每个请求,在服务器端执行$_SESSION['counter']++
。在浏览器端使用JS执行类似的操作(使用本地存储)。然后,当你发送请求时,只需获取令牌的一个随机数,并验证服务器上的随机数是否相同。通过这样做,你应该能够检测到劫持的会话,因为攻击者将没有确切的计数器,或者如果他们确实有,你将有两个系统传输相同的计数,并且可以判断其中一个是伪造的。这种方法不适用于所有应用程序,但是是解决问题的一种方式。
关于两者的说明
会话固定和劫持的区别仅在于会话标识符的泄露方式。在固定中,攻击者事先知道标识符的值。而在劫持中,标识符要么被猜测,要么从用户那里窃取。一旦标识符被泄露,两者的影响是相同的。
会话标识符重新生成
每当使用session_regenerate_id
重新生成会话标识符时,旧的会话应该被删除。核心会话处理程序会自动完成此操作。然而,一些使用session_set_save_handler()
的自定义会话处理程序没有执行此操作,因此容易受到旧会话标识符的攻击。如果您使用自定义会话处理程序,请确保跟踪您打开的标识符,并且如果它与保存的标识符不同,您需要显式地删除(或更改)旧标识符上的标识符。
使用默认的会话处理程序,只需调用
session_regenerate_id(true)
即可。这将为您删除旧的会话信息。旧的ID不再有效,如果攻击者(或其他人)尝试使用它,将会创建一个新的会话。但是,对于自定义的会话处理程序要小心……
销毁会话
如果要销毁会话(例如在注销时),请确保彻底销毁它。这包括取消设置cookie。使用
session_destroy
:
function destroySession() {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
session_destroy();
}