典型Filter登录示例
基本上所有的web应用都有登录认证业务,如果未登录,则访问站内几乎所有的资源都会被返回一个登录页面,这样的实现技术多数都是通过fileter(通配url处理机制)
如web.xml如下:
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
loginCheck
com.test.SecurityFilter
loginPage
/login.jsp
loginCheck
/*
add
com.test.Servlet
add
/add
web.xml通过配置/*表示站内所有的资源(url)都需要经过登录验证,使用init-param参数来指定登录节目
java代码的filter是这样的:
package com.test;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Created by conquer on 1/16/16.
*/
public class SecurityFilter implements Filter {
private String loginPage;
public void init(FilterConfig filterConfig) throws ServletException {
loginPage = filterConfig.getInitParameter("loginPage");
System.out.println("init loginPage: " + loginPage);
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();
if (session != null && session.getAttribute("user") != null) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
String servletPath = httpServletRequest.getServletPath();
if (servletPath.endsWith(loginPage)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
String loginLocation = httpServletRequest.getContextPath() + loginPage;
httpServletResponse.sendRedirect(loginLocation);
}
public void destroy() {
}
}
在登录验证逻辑:如果session中包含了user属性表示已经登录,直接交给下个过滤器(表示通过),如果没有登录则验证当前访问的资源是否是“登录页面“,如果是登录页面则不进行过滤,直接交给下个过滤器,最后如果既没有登录而且访问的也不是可以跳过验证(如登录页面)的资源,就强制返回登录界面要求用户登录。
其他介绍:
文中的过滤器参数是 ServletRequest 和 ServletResponse ,而不是HttpServletRequest和HttpServletResponse,这是因为Servlet规范(其中包括ServletRequest 和 ServletResponse内方法的意义)定义是抽象的,不与任何协议相关联,无论使用哪种实现都要遵守Servlet规范,也就是说传递进来的对象实现ServletRequest 和 ServletResponse接口,通常情况下我们使用HTTP协议访问Servlet资源,通过HTTP协议进入Servlet容器的ServletRequest 和 ServletResponse的实现则分别是HttpServletRequest和HttpServletResponse,这两个HTTP协议下的实现类除了支持Servlet的全部规范外,又添加了自己关于HTTP协议相关的特性,如Cookie、Header等等,Servlet容器支持的协议通常有HTTP AJP等,本文仅讨论HTTP协议,当我们确定我们使用的是HTTP协议进入Servert容器的时候,我们就可以强制进行类型转换,以支撑我们的业务需求。
HttpServletRequest的一些方法说明 :
getRequestURI:获取到浏览器输入的完整地址,这里是完全和输入的一致,不会进行任何修正,比如 /web1/aa////b///c.jsp?a=b&c=d
getContextPath:获取应用的访问名称,对于上面的地址则是: /web1
getServletPath:获取访问你的资源名称,对于上面的地址则是:/aa/b/c.jsp,注意这里对url进行了修正
getQueryString:获取附带的参数,对于上面的地址则是:a=b&c=d
所有一般我们在java里面要指定一个资源一般是:getContextPath() + "/" + "xxx.jsp"
而在jsp里要指定资源一般是:
<%
String basePath = request.getContextPath();
%>
<%=basePath %> + "/" + "xxx.jsp"
有了后台的过滤器,就要有对应的登录界面login.jsp:
<%
String input = request.getParameter("input");
if (input != null) {
session.setAttribute("user", input);
response.sendRedirect("index.jsp");
}
%>
Login: <%=session.getAttribute("user") %>
笔者 将这个登录逻辑直接提交到自己这个jsp页面,该页面除了显示登录框和按钮以外,同时验证如果登录参数包含则放入 session 认证。
登录成功后的展示页面:
<%
String path = request.getContextPath();
%>
Hello World!
add my number
show my number
一个简单的 注销 页面:
<%
String contextPath = request.getContextPath();
session.invalidate();
response.sendRedirect(contextPath + "/login.jsp");
%>
如果能够登录成功就可以访问下面的Servlet资源,否则就会被打回到登录界面:
package com.test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created by conquer on 1/16/16.
*/
public class Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
Integer mynumber = (Integer) session.getAttribute("mynumber");
if (mynumber == null) {
mynumber = 0;
}
mynumber++;
session.setAttribute("mynumber", mynumber);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
也可以访问jsp资源:
Show My Number:<%= session.getAttribute("mynumber")%>
====================================================================================
上面介绍了一个典型的网站登录保护资源的例子,下面我们分析其存在的 XSS漏洞和CSRF漏洞 :
XSS漏洞分析
上述login.jsp中将获取到的user直接显示:
Login: <%=session.getAttribute("user") %>
xss漏洞多发生在可以自定义输入和原样的地方,这里可以输入用户名并直接显示,如果用户输入的是:
则在显示的时候就会直接跳转到xxx.com并偷走你的cookie,也就意味这你的session id泄漏,即登录认证信息泄漏,此时攻击者在自己的站点后台便可以直接进行相关业务操作(因为攻击者已经连接到了业务操作对于那个的url),比如一个转账url xxxx/xx?from=zhangsan&to=lisi,这个url是受保护的,如果我们同时发送session id到其服务器,就会被认为是一个合法的请求达到恶意目的。当然,攻击者也不需要跳转到自己的服务器直接使用脚本在当前页面内操作也是一样的,说白了,你可以写任意的js代码来做任何事情,这就是xss漏洞的风险。
xss漏洞虽然可怕,但由于其普遍性已经得到了很好的保护,大所属的网站都作了xss漏洞防护,主要的工作就是对可输入的内容进行验证和检查其是否具有危害性。
CSRF漏洞分析
相比与xss漏洞,而csrf漏洞就没有那么出名了,然而他的危害一样不容小觑,如果我们正常登录了一个网站,操作完毕后没有进行注销或关闭浏览器,即session依然处于活跃状态,
我们通过标签页的方式打开了一个其他网站(钓鱼网站),(通常都是及其诱惑的图片、链接)点击过来的,这样的网站页面在你的浏览器上被运行,直接发送一个转账业务url到银行的网站,由于发送的网址是你刚才打开的银行网址,对浏览器来说这和你自己在银行网站点击转帐的连接是一模一样的,所以就连同你的cookie信息一起发送到后台,从而发生了转账攻击,csrf攻击非常具有隐蔽性,它不需要获取到你的 cookie信息 ,也不需要在目标站点输入任何xss恶意脚本等,它是利用当前浏览器中存在的活跃的认证信息冒充正常用户发送命令到后台的,由于 其发生的概率低,受到的关注就小,由于其高隐秘性,以至于 我们受攻击后很长时间才发现,甚至 无从分析是如何发生的,因为没有任何现场痕迹,在服务器看来 那 就是一个合法的请求!!
关于csrf的防范,简单说明如下,详细请看:http://blog.csdn.net/conquer0715/article/details/45024549
1.首先 认证refer,对于refer存在而不正确的直接拒绝
2.refer为空或正确时,验证其token,包括自定义的参数token和ajax的header token,如果token是服务器端放置到页面上的则通过
注意token绝对不能放到cookie中,否则就和session id被盗用一样悲剧了,csrf主要的手段就是盗用你的cookie。
建议 开发网站的时候,对于需要保护的资源使用ajax访问,便于统一进行安全防护,如添加csrf_token。