保护你的会话令牌
保护你的会话令牌
通常我们会采取以下的措施来保护会话。
1.采用强算法生成Session ID
正如我们前面用Web Scrab
分析的那样,会话ID
必须具有随机性和不可预测性。一般来说,会话ID
的长度至少为128
位。下面我们就拿常见的应用服务器Tomcat
来说明如何配置会话ID
的长度和生成算法。
首先我们找到{TOMCAT_HOME}\conf\context.xml,然后加入下面一段设置
<Manager sessionIdLength="20" ➊
secureRandomAlgorithm="SHA1PRNG" ➋
secureRandomClass="java.security.SecureRandom" ➌
/>
➊ 定义会话ID 的长度,如果我们这里不声明的话,默认是16字节。可能有读者会纳闷,怎么平时我看到的会话ID都是很长的呀?我们就拿这里的20个字节来讲吧,我们在浏览器发送请求时会发现这样的会话ID:
JSESSIONID=90503B6BE403D4AB6164A311E167CF1F6F3F2BD0
仔细看会发现ID的长度为40,因为这里显示的是十六进制,每两个字符代表一个字节。
➋ 定义随机数算法,默认的是SHA1PRNG,你也可以换成自己的算法。
➌ 定义随机数类,默认的是java.security.SecureRandom,我们也可以继承这个类来实现自己的算法。
有一点要注意,就是我们在实现自己的随机数算法时,一定要保证生成的Session ID不能有重复,这里我们参考一下Tomcat实现的机制。
/**
* Generate and return a new session identifier.
*/
protected () {StringgenerateSessionId
String result = null;
do {
if (result != null) {
duplicates++;
}
result = sessionIdGenerator.generateSessionId();
} while (sessions.containsKey(result));
return result;
}
由此可见,Tomcat是不会产生两个相同的会话ID的。
2.软硬兼施,会话过期
会话过期是应用程序的一项重要的安全控制,它定义了用户在多长时间段内不用重新登录而仍然维持一个登录状态。一般来说,有两种会话过期——软会话过期(Soft Session Timeout)和硬会话过期(Hard Session Timeout)。
软会话过期,它指的是用户在一定的时间内与应用系统没有交互,则会话过期。举一个简单的例子就是,一个用户登录了一个应用系统,他临时离开了计算机40分钟,而应用系统设置的会话过期时间为30分钟,这时候用户回到计算机前再做任何操作,系统都会重定向为登录页面让用户重新输入用户名和密码。
那么,软会话过期有什么用呢?我们知道在CSRF***中一个最基本的假设就是合法用户处在一个登录状态中,如果我们设置了一个合理的且较低的会话过期时间,就提高了实施CSRF***的难度,从而保护了系统。
通常有3种办法来设定软会话过期,其级别由高到低依次为:Tomcat级别> Web应用级别>Servlet运行时context级别,这时候低级别的设定会覆盖高级别的设定。
a.Tomcat级别的设定。
若你需要设定30分钟的会话过期,你可以在{TOMCAT_HOME}\conf\web.xml中进行设定如下:
<session-config>
<session-timeout>30</session-timeout><!-- set in minutes -->
</session-config>
b.Web应用级别的设定。
若你需要设定15分钟的会话过期,你可以在{TOMCAT_HOME}\webapps\ {APP_NAME}\WEB-INF\web.xml中这样进行设定:
<session-config>
<session-timeout>15</session-timeout><!-- set in minutes -->
</session-config>
c.在程序代码中进行设定。
若你需要在程序中设定5分钟的会话过期,你可以用下面一行代码来实现:
httpSession.setMaxInactiveInterval(5*60); // set in seconds
如果我们按照上面的步骤进行了会话过期设置,那么最后真正起作用的是在程序中进行设定的5分钟。
再看什么是硬会话过期。它指的是用户登录到系统中经过一定的时间后,不管用户做什么,该会话都会过期。大家都知道网络游戏防沉迷系统吧?如果未成年人的累计在线时间已满5小时,则累计在线时间清零,这个与我们这里说的硬会话过期很相似,只不过我们这里不是在线时间清零,而是强制用户退出并重新登录。
那么硬会话过期有什么用呢?它主要是用来防止永久的对一个账号劫持。比如说一个***者通过XSS得到了受害者的session,并用它冒充受害者进行登录,如果我们设定了硬会话过期,则经过了一段时间之后,系统会强制用户重新进行认证。
没有专门的API或者配置来设定硬会话过期,但我们可以通过在web filter中写自己的代码来实现这个功能。基本思路如下:对每个用户登录成功后记录下此时的时间,并且把这个时间与他们的Session ID绑定起来,如果用同一个Session ID发送的请求的时间减去这个Session ID刚登录成功的时间大于了我们设定的会话过期时间,则使这个会话无效,并重定向到登录页面。
3.保护你的Cookie
Cookie有两个很重要的属性:secure和HttpOnly,设置好这两个属性对于保护你的Cookie至关重要。
首先说secure属性。声明了它,则说明当前这个Cookie只会在HTTPS的链接中进行传递,这样就可以使得***者无法很容易地通过分析网络流量来获得会话ID,从而有效地防治了中间人***(Man-in-the-Middle)。
HttpOnly这个属性我们在XSS这一章已经介绍过,它不允许一些脚本(如JavaScript等)直接操作document.cookie这个DOM对象,这个属性对于阻止通过XSS窃取会话ID是必需的。
一个好消息是,Tomcat 7支持了Servlet 3.0,所以我们可以在web.xml设定上面的两个属性。
<session-config>
<cookie-config>
<secure>true</secure>
</cookie-config>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
</session-config>
需要注意的是Tomcat 6以前的版本不支持,Tomcat 6支持的是Servlet 2.5。
4.提供logout功能
上面介绍的是系统自动按照设定的时间使会话过期,一个好的应用程序应该提供一个功能,即用户可以手动地使当前会话过期,这就是我们在几乎所有网站上都看到的logout按钮。那么一般的logout需要完成哪些功能呢?让我们看看ESAPI中是如何实现logout功能的吧。
Class: org.owasp.esapi.reference.DefaultUser
public void logout() {
ESAPI.httpUtilities().killCookie( ESAPI.currentRequest(),
ESAPI.currentResponse(),
HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME );➊
HttpSession session = ESAPI.currentRequest().getSession(false);
if (session != null) {
removeSession(session);
session.invalidate();➋
}
ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(),
ESAPI.currentResponse(),
"JSESSIONID");➌
loggedIn = false;
logger.info(Logger.SECURITY_SUCCESS, "Logout successful" );
ESAPI.authenticator().setCurrentUser(User.ANONYMOUS);
}
killCookie的实现代码如下:
public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) {
String path = "//";
String domain="";
Cookie cookie = getFirstCookie(request, name);
if ( cookie != null ) {
path = cookie.getPath();
domain = cookie.getDomain();
}
Cookie deleter = new Cookie( name, "deleted" );
deleter.setMaxAge( 0 );➍
if ( domain != null ) deleter.setDomain( domain );
if ( path != null ) deleter.setPath( path );
response.addCookie( deleter );
}
我们简单地分析一下上面的代码:
➊ 的作用是清除remember me这个Cookie,这是针对网站有remember这个功能来说的。
➋ 是使得当前的会话无效,这样即使当前的会话ID泄露出去了,***者也无法用这个会话ID进行登录。
➌ 的作用是清除JSESSIONID这个Cookie。
➍ 使deleter(与传递进来的Cookie同名)立即无效。
本文节选自《Web应用安全威胁与防治——基于OWASP Top 10与ESAPI》
王文君 李建蒙 编著
电子工业出版社出版