代码审计--2--认证会话管理

论坛 期权论坛 脚本     
匿名技术用户   2020-12-23 12:13   114   0

2.1 图形验证码

图形验证码一般是防止使用程序恶意注册、暴力破解用户名密码或者批量发帖而设置的。在页面初试化时服务器向页面发送一个随机字符串,同时在 session 里也保存一份,当用户提交时将随机数一起 post 到后台,通过与 session 中保存的值对比,如果不相同,则有可能是恶意攻击。

示例代码

用户登陆过程中是否启用了验证码
验证码复杂度是否符合要求
验证码的有效性是否只有一次
代码示例:

//从 session 中获取验证码
String validateCode=(String)request.getSession().getAttribute("rand");

//获取用户输入的验证码
String userInput=request.getParameter("validateCode");
if(userInput==null||(!validateCode.equalsIgnoreCase(userInput)))
{
 //提示验证码错误
 ……
 return;
}
……
//从以上代码中可以看到在验证码校验时,只是校验了验证码是否正确并未清除session的验证码

验证码是否设置了超时机制
代码示例:

protected boolean checkTimeout(EMPSession session)
{
 return System.currentTimeMillis() - session.getLastAccessTime() > this.sessionTimeOut;
}
//从上面的代码中可以看到验证码校验机制

验证顺序
代码示例:

//从session中获取验证码
String validateCode=(String)request.getSession().getAttribute("rand");

//获取用户输入的验证码
String userInput=request.getParameter("validateCode");
String username=request.getParameter(“userName”);
String userpwd=request.getParameter(“userPwd”);

if(LoginSuccess(username,userpwd)) {
 if(validateCode.equalsIgnoreCase(userInput)) {
  ……
 } else {
  //提示验证码错误
  request.getSession().removeAttribute("rand");
  return;
 } 
} else {
 //提示用户名或密码错误
 request.getSession().removeAttribute("rand");
 return;
}
//正确的验证过程,应将验证码的验证放在首位,验证码验证通过后,再验证登陆或者其他操作。

图形验证码安全防范编码

String userName = request.getParameter("adminname");
String userPwd = request.getParameter("adminpwd");
//从session中取得验证码
String validateCode=(String)request.getSession().getAttribute("rand");
//获取用户输入的验证码
String userInput=request.getParameter("validateCode");

if(userName==null||userPwd==null)
 return;

if(validateCode!=null) {
 if(!validateCode.equalsIgnoreCase(userInput)) {
  out.print("<script>alert('验证错误! ');window.location='AdminLogin.jsp'</script>"); 
  //移除使用过的验证码
  request.getSession().removeAttribute("rand");
  return;
 } else {
  request.setAttribute("message", "验证正确");
 }
 
 boolean loginValid=dao.LoginStatus(userName, userPwd);

 if(loginValid) {
  request.getSession().invalidate();
  request.getSession().setAttribute("AdminName", userName);
  out.print("<script>alert('登录成功,跳转到首页');window.location='AdminIndex.jsp'</script>");
 } else {
  out.print("<script>alert(' 用 户 名 或 密 码 错 误 !');window.location='AdminLogin.jsp'</script>");
 }
 
 //移除使用过的验证码
 request.getSession().removeAttribute("rand");
}

2.2 用户登陆认证

用户登陆认证应检查用户名和密码的合法性。
对于用户名错误和密码错误的提示信息应统一,降低账号、密码被猜解的风险。
用户唯一性(用户注册时检查)。

日志信息
登录日志中记录客户访问系统的远程 IP 地址和时间等详细信息,可以统计客户访问系统的次数;除了正常的登陆和操作日志,还应记录异常的日志信息并严格控制系统日志的访问权限。

用户登录认证安全编码规范:

public boolean checkUser(String username,String password) {
 try { 
  //使用预编译的方式防止产生注入
  pstmt=ct.prepareStatement("select * from userTable where username=? and password=?");
  pstmt.setString(1, username);

  //密码加密
  pstmt.setString(2, Entrypt. string2MD5(password,salt));
  ResultSet rs=pstmt.executeQuery();
  User user=new User();
  while(rs.next()) {
   user.setId(rs.getInt(1));
   user.setUsername(rs.getString(2));
   user.setPassword(rs.getString(3));

   //更新session数据
   HttpSession session=dhUtil.changeSessionIdentifier(request);
   session. .setAttribute("username", username);
   return user;
  }
  return null;
 } catch(Exception e) {
  return null;
 }
}

2.3 注销

系统应为用户提供退出系统功能,在退出系统后,清理会话信息。

注销安全编码规范:

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);
 ESAPI.httpUtilities().sendRedirect(“登录页面”);
}

2.4 认证失败次数限制

系统应对客户登录密码输入次数进行记录,如果客户密码输入次数累计达到3次,系统应自动将此客户冻结,防止恶意攻击

2.5 重定向认证

Web应用中经常需要指定完成当前页面操作之后下一个页面的请求地址。常见的例如:登录操作完成之后,返回指定页面或者返回登录操作前页面。如果返回地址可被攻击者控制,可能导致受害者访问恶意网站、钓鱼网站的链接。

重定向认证安全编码规范

对需要重定向的参数进行判断,如果重定向的URL不符合"Redirect"正则表达式的要求,则触发异常,并重定向回主页。

注:ESAPI是通过ESAPI.properties文件中的Redirect正则表达式进行过滤判断的,根据具体需要编辑ESAPI.properties

<%
 String redirect=request.getParameter("redirect");
 if(redirect!=null) 
 {
  ESAPI.httpUtilities().sendRedirect(redirect);
 }
%>

2.6 SSO认证

SSO(Single Sign On,单点登录)是目前比较流行的企业业务整合方案之一。在SSO定义的众多应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。在实现过程中应避免在客户端中明文传输账号密码信息,建议使用Token交换认证信息或者将用户名密码加密处理后再进行传输。

SSO认证安全编码规范

在Token认证中,将SSO分为SSO-Server和SSO-Client两个部分,SSO-Server主要负责用户的登陆注销、为SSO-Client分配Token和验证Token工作,SSO-Client作为一个受信系统,没有自己的认证系统,通过SSO-Server完成身份认证工作。

if (Domain.Security.SmartAuthenticate.LoginUser != null)
{
 //生成Token,并持久化Token
 Domain.SSO.Entity.SSOToken token = new Entity.SSOToken();
 token.User = new Entity.SSOUser();
 token.User.UserName=Domain.Security.SmartAuthenticate.LoginUser.UserName;
 token.LoginID = Session.SessionID;
 Domain.SSO.Entity.SSOToken.SSOTokenList.Add(token);
 //拼接返回的url,参数中带Token
 string spliter = returnUrl.Contains('?') ? "&" : "?";
 returnUrl = returnUrl + spliter + "token=" + token.ID;
 Response.Redirect(returnUrl);
}

当完成Token分配之后,页面将带有TokenID的参数跳转到SSO-Client页面,并在SSO-Client的Cookie中添加Token值,在以后的每次请求中,SSO-Client通过调用SSO-Server的服务来验证Token的合法性。

public Entity.SSOToken ValidateToken(string tokenID)     //验证token的有效性
{
 if (!KeepToken(tokenID))
  return null;
 var token = Domain.SSO.Entity.SSOToken.SSOTokenList.Find(m => m.ID ==tokenID);
 return token;
}

public bool KeepToken(string tokenID)           //保持token不过期
{
 var token = Domain.SSO.Entity.SSOToken.SSOTokenList.Find(m => m.ID ==tokenID);
 if (token == null)
  return false;
 if (token.IsTimeOut())
  return false;
 token.AuthTime = DateTime.Now;
 return true;
}

当用户请求SSO-Client的受保护资源时,SSO-Client会首先是否有TokenID,如果存在TokenID,则调用SSO-Server的WebService来验证这个TokenID是否合法;验证成功以后将会返回SSOToken的实例,里面包含已登录的用户信息。

if (!string.IsNullOrEmpty(tokenID)) {
 AuthTokenService.AuthTokenServiceSoapClient client = new AuthTokenService.AuthTokenServiceSoapClient();
 var token = client.ValidateToken(tokenID);
 if (token != null) {
  //登录成功!跳转
 } else {
  //获取不到用户信息!跳转到登录页面
 }
} else {
 //tokenID无效
}

2.7 session 建立

JavaWeb系统会在客户登录成功之后为其在应用服务器内存中建立Session,在客户后续的交易请求中,系统不断检查内存中Session的有效性,如果Session失效(没有、超时或被人窜改),则交易请求是非法的,系统不予接受;

为了防止攻击者暴力猜解session会话ID标识,应至少包含128位安全随机数标识符;

为了防止session会话ID劫持,应当在每次认证完成后开启全新的用户ID,保证会话安全。

session建立安全编码规范

//将当前session数据拷贝到新session,并使当前session失效,并将新session绑定到当前user对象中,并返回新的session。

HttpSession ESAPI.httpUtilities().changeSessionIdentifier(HttpServletRequest request);

2.8 Session超时处理

Session超时处理包括两部分:Session时间戳重置,Session超时检查。

Session时间戳重置是指在有新的交易请求提交到JavaWeb系统时,系统首先检查Session是否超时,如果未超时,则重置Session的时间戳,继续后续操作;否则,执行Session超时处理,向客户返回超时信息。

Session超时检查是指为防止垃圾Session的在内存中堆积而占用系统资源,系统通过后台线程定时检查超时Session,并将其从内存中清除,从而释放系统资源。

session超时处理安全编码规范

DefaultUser user = (DefaultUser) getUserFromSession();  //检查用户是否已登录

if(!user .isSessionTimeout()) {
 user. setLastLoginTime(new Date())
} else {
 //注销
 Logout();
}

2.9 Session清理

应当允许用户安全的结束自己的会话。
一个会话结束后,应该安全的从硬盘和内存中清除相关数据。

session清理安全编码规范:与《注销》安全编码规范相同

2.10 Session实时检查

JavaWeb系统每个交易的定义中,第一个步骤必须是Session检查交易步骤,来检验Session有效性。

session实时检查安全编码规范

DefaultUser user = (DefaultUser) getUserFromSession();   
 //检查用户是否已登录

if (user.isAnonymous()||!user.isEnabled()||user.isLocked()||user.isExpired()||user.isSessionTimeout()||user.isSessionAbsoluteTimeout())      
//各种用户状态条件判断
{
 //交易处理
}

2.11 cookie管理

是否会在Cookie中存储明文或简单编码/加密(base64编码)过的密码
是否会在Cookie中存储应用的特权标识
是否设置了Cookie的有效域和有效路径
是否设置了合适的Cookie有效时间

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP