Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2296629
  • 博文数量: 252
  • 博客积分: 5472
  • 博客等级: 大校
  • 技术积分: 3107
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-17 18:39
文章分类

全部博文(252)

文章存档

2012年(96)

2011年(156)

分类: 系统运维

2011-12-14 20:06:42

留言板程序可能遇到的问题:
     输入HTML代码 破坏了留言板的正常显示 和安全隐患
     输入了不雅的字句
 
不对现有的代码进行修改  可以考虑加入过滤器 过滤器可以截取到请求和响应对象 但是无法直接使用这两个对象对他们的数据进行替换 但是我们可以利用请求和响应的包装类来间接改变请求和响应的信息
 
利用过滤器和包装类就可以解决留言板程序中存在在的这两个问题
第一个问题   编写一个HttpServletRequestWrapper类的子类 然后重写getParameter()方法 在这个方法中 对请求参数的值进行过滤 将特殊字符转换成对应的实体引用或字符引用
第二个问题   编写一个HttpServletResponseWrapper类的子类 响应的内容是通过字符(PrintWrite)或者字节(ServletOutputStream)输出流对象向客户端输出的 而字符和字节输出流对象则是通过HttpServletResponse.getWriter()和HttpServletResponse.getOutputStream()方法得到的 正常情况下 响应的内容将被容器直接发送到客户端 要想得到响应的内容 就要替换默认的输出流对象 并且 新的输出流对象应该是内存输出流对象 也就是我们在调用输出流对象的write()方法时 数据被写到内存的缓冲区中 我们可以使用java.io包中的ByteArrayOutputStream类 让数据写到字节数组中 同时重写HttpServletResponse类的getWrite()和getOutputStream()方法 返回构建在ByteArrayOutputStream之上的PrintWriter对象和ServletOutputStream对象
 
 
实例开发步骤
1  MyRequestWrapper.java
从HttpServletRequestWrapper类继承 并重写getParameter()方法 对请求参数进行过滤 将特殊字符转换成对应的实体引用或者字符引用
  1. package filter;

  2. import javax.servlet.http.HttpServletRequest;
  3. import javax.servlet.http.HttpServletRequestWrapper;

  4. public final class MyRequestWrapper extends HttpServletRequestWrapper
  5. {
  6.     public MyRequestWrapper(HttpServletRequest request)
  7.     {
  8.         super(request);
  9.     }
  10.     
  11.     /**
  12.      * 覆盖基类的getParameter()方法,对请求参数的值进行过滤。
  13.      */
  14.     public java.lang.String getParameter(java.lang.String name)
  15.     {
  16.         String value=super.getParameter(name);
  17.         if(null!=value)
  18.             return toHtml(value.trim());
  19.         else
  20.             return null;
  21.     }
  22.     
  23.     /**
  24.      * 将特殊字符转换为对应的实体引用或字符引用。
  25.      */
  26.     private String toHtml(String str)
  27.     {
  28.         if(str==null)
  29.             return null;
  30.         StringBuffer sb = new StringBuffer();
  31.         int len = str.length();
  32.         for (int i = 0; i < len; i++)
  33.         {
  34.             char c = str.charAt(i);
  35.             switch(c)
  36.             {
  37.             case ' ':
  38.                 sb.append(" ");
  39.                 break;
  40.             case '\n':
  41.                 sb.append("
    "
    );
  42.                break;
  43.             case '\r':
  44.                break;
  45.             case '\'':
  46.                 sb.append("'");
  47.                 break;
  48.             case '<':
  49.                 sb.append("<");
  50.                 break;
  51.             case '>':
  52.                 sb.append(">");
  53.                 break;
  54.             case '&':
  55.                 sb.append("&");
  56.                 break;
  57.             case '"':
  58.                 sb.append(""");
  59.                 break;
  60.             case '\\':
  61.                 sb.append("\");
  62.                 break;
  63.             default:
  64.                 sb.append(c);
  65.             }
  66.         }
  67.         return sb.toString();
  68.     }
  69. }

 

 

2   ByteArrayServletOutputStream.java

这个类从ServletOutputStream类继承 该类的对象用于替换HttpServletResponse.getOutputStream()方法返回的ServletOutputStream对象 其内部使用java.io.ByteArrayOutputStream的write(int b)方法实现ServletOutputStream类的write(int b)方法

  1. package filter;

  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import javax.servlet.ServletOutputStream;

  5. public class ByteArrayServletOutputStream extends ServletOutputStream
  6. {
  7.     ByteArrayOutputStream baos;
  8.     
  9.     ByteArrayServletOutputStream(ByteArrayOutputStream baos)
  10.     {
  11.         this.baos = baos;
  12.     }
  13.     public void write(int data) throws IOException
  14.     {
  15.         baos.write(data);
  16.     }
  17. }

 

3  MyResponseWrapper.java

MyResponseWrapper类从HttpServletResponseWrapper类继承 并重写了getWriter() 和getOutputStream()方法 用构建在ByteArrayOutputStream之上的PrintWriter对象和ServletOutputStream对象替换Web容器创建的PrintWriter和ServletOutputStream对象

  1. package filter;

  2. import java.io.*;
  3. import javax.servlet.*;
  4. import javax.servlet.http.*;


  5. public class MyResponseWrapper extends HttpServletResponseWrapper
  6. {
  7.     private ByteArrayOutputStream baos;
  8.     private ByteArrayServletOutputStream basos;
  9.     private PrintWriter pw;
  10.     
  11.     public MyResponseWrapper(HttpServletResponse response)
  12.     {
  13.         super(response);
  14.         //创建ByteArrayOutputStream对象。

  15.         baos=new ByteArrayOutputStream();
  16.         //用ByteArrayOutputStream对象作为参数,

  17.         //构造ByteArrayServletOutputStream对象。

  18.         basos=new ByteArrayServletOutputStream(baos);
  19.         //用ByteArrayOutputStream对象作为参数,

  20.         //构造PrintWriter对象。

  21.         pw=new PrintWriter(baos);
  22.     }
  23.     
  24.     public PrintWriter getWriter()
  25.     {
  26.         return pw;
  27.     }
  28.     
  29.     public ServletOutputStream getOutputStream()
  30.     {
  31.         return basos;
  32.     }
  33.     
  34.     /**
  35.      * 以字节数组的形式返回输出流缓冲区中的内容。
  36.      */
  37.     public byte[] toByteArray()
  38.     {
  39.         return baos.toByteArray();
  40.     }
  41. }

4  GuestbookFilter.java

这个是过滤器类 利用MyRequestWrapper和MyPesponseWrapper类来替换请求中的特殊中的特殊字符和响应中的不雅字句不雅字句与替换的内容是以Java属性文件的格式保存到一个文件中 文件的路径名作为过滤器类的初始化参数在web.xml文件中进行配置。

  1. package filter;

  2. import java.io.*;
  3. import java.util.*;
  4. import javax.servlet.*;
  5. import javax.servlet.http.*;

  6. public class GuestbookFilter implements Filter
  7. {
  8.     private static final String WORD_FILE="word_file";
  9.     
  10.     HashMap<String,String> hm=new HashMap<String,String>();
  11.     
  12.     /**
  13.      * 在init()方法中,读取保存了不雅字句和替换内容的文件,
  14.      * 并以不雅字句作为key,替换内容作为value,保存到Hashmap对象中。
  15.      */
  16.     public void init(FilterConfig filterConfig) throws ServletException
  17.     {
  18.         String configPath=filterConfig.getInitParameter(WORD_FILE);
  19.         
  20.         ServletContext sc=filterConfig.getServletContext();
  21.         String filePath=sc.getRealPath(configPath);
  22.         
  23.         try
  24.         {
  25.             FileReader fr=new FileReader(filePath);
  26.             BufferedReader br=new BufferedReader(fr);
  27.             
  28.             String line;
  29.             while(null!=(line=br.readLine()))
  30.             {
  31.                 String[] strTemp=line.split("=");
  32.                 hm.put(strTemp[0],strTemp[1]);
  33.             }
  34.         }
  35.         catch(IOException ie)
  36.         {
  37.             throw new ServletException("读取过滤文件信息出错!");
  38.         }
  39.     }
  40.    
  41.     public void doFilter(ServletRequest request,
  42.                          ServletResponse response,
  43.                          FilterChain chain)
  44.                   throws IOException, ServletException
  45.     {
  46.         
  47.         HttpServletRequest httpReq=(HttpServletRequest)request;
  48.         HttpServletResponse httpResp=(HttpServletResponse)response;
  49.         
  50.         //得到请求和响应对象的封装类对象。

  51.         MyRequestWrapper reqWrapper=new MyRequestWrapper(httpReq);
  52.         MyResponseWrapper respWrapper=new MyResponseWrapper(httpResp);
  53.                 
  54.         chain.doFilter(reqWrapper,respWrapper);
  55.         
  56.         String content = new String(respWrapper.toByteArray());
  57.         String result=replaceText(content);
  58.         httpResp.setContentType("text/html;charset=GB2312");
  59.         PrintWriter out = httpResp.getWriter();
  60.         out.println(result);
  61.         out.close();
  62.     }
  63.     
  64.     /**
  65.      * 对内容中的不雅字句进行过滤。
  66.      */
  67.     public String replaceText(String content) throws IOException
  68.     {
  69.         StringBuffer sb=new StringBuffer(content);
  70.         Set<String> keys=hm.keySet();
  71.         Iterator<String> it=keys.iterator();
  72.         while(it.hasNext())
  73.         {
  74.             String key=(String)it.next();
  75.             int index=sb.indexOf(key);
  76.             while(-1!=index)
  77.             {
  78.                 sb.replace(index,index+key.length(),(String)hm.get(key));
  79.                 index=sb.indexOf(key);
  80.             }
  81.         }
  82.         return sb.toString();
  83.         
  84.     }
  85.     public void destroy(){}
  86. }

 

5  准备留言板程序

say.html

  1. <center>
  2.     <form action="process.jsp" method="post">
  3.     <table bgcolor="#B3B3FF">
  4.       <caption>欢迎访问留言板</caption>
  5.             <tr>
  6.                 <td>用户名:</td>
  7.                 <td><input type="text" name="name"></td>
  8.             </tr>
  9.             <tr>
  10.                 <td>主题:</td>
  11.                 <td><input type="text" name="title" size="40"></td>
  12.             </tr>
  13.             <tr>
  14.                 <td>内容:</td>
  15.                 <td>
  16.                     <textarea name="content" rows="10" cols="40"></textarea>
  17.                 </td>
  18.             </tr>
  19.             <tr>
  20.                 <td><input type="submit" value="提交"></td>
  21.                 <td><input type="reset" value="重填"></td>
  22.             </tr>
  23.         </table>
  24.     </form>
  25. </center>

process.jsp

 

  1. <%@ page contentType="text/html;charset=gb2312" %>
  2. <%@ page import="java.sql.*,javax.sql.*,javax.naming.*" %>

  3. <%
  4.     request.setCharacterEncoding("GBK");
  5.     
  6.     String name=request.getParameter("name");
  7.     String title=request.getParameter("title");
  8.     String content=request.getParameter("content");
  9.     
  10.     if(null==name || null==title || null==content)
  11.     {
  12.      response.sendRedirect("index.jsp");
  13.         return;
  14.     }
  15.     
  16.     if(name.equals("") || title.equals(""))
  17.     {
  18.         response.sendRedirect("say.html");
  19.         return;
  20.     }
  21.     
  22.     String fromIP=request.getRemoteAddr();
  23.     
  24.     Context ctx=new InitialContext();
  25.     DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/bookstore");
  26.     Connection conn=ds.getConnection();
  27.     
  28.     PreparedStatement pstmt=conn.prepareStatement(
  29.         "insert into guestbook(gst_user,gst_title,gst_content,gst_ip) values(?,?,?,?)");
  30.     pstmt.setString(1,name);
  31.     pstmt.setString(2,title);
  32.     pstmt.setString(3,content);
  33.     pstmt.setString(4,fromIP);
  34.     
  35.     pstmt.executeUpdate();
  36.     pstmt.close();
  37.     conn.close();
  38.     response.sendRedirect("index.jsp");
  39. %>

 

index.jsp

  1. <%@ page contentType="text/html;charset=gb2312" %>
  2. <%@ page import="java.sql.*,javax.sql.*,javax.naming.*" %>

  3. <html>
  4.     <head>
  5.         <title>网上书店留言板</title>
  6.     </head>
  7.     <body>
  8.         <a href="say.html">我要留言</a><br>
  9.         <%
  10.             Context ctx=new InitialContext();
  11.             DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/bookstore");
  12.             Connection conn=ds.getConnection();
  13.             
  14.             //创建可滚动的结果集。

  15.             Statement stmt=conn.createStatement(
  16.                 ResultSet.TYPE_SCROLL_INSENSITIVE,
  17.                 ResultSet.CONCUR_READ_ONLY);
  18.             ResultSet rs=stmt.executeQuery("select * from guestbook order by gst_time desc");
  19.             
  20.             //移动游标到结果集的最后一行。

  21.             rs.last();
  22.             
  23.             //得到当前行的行数,也就得到了数据库中留言的总数。

  24.             int rowCount=rs.getRow();
  25.             if(rowCount==0)
  26.             {
  27.                 out.println("当前没有任何留言!");
  28.                 out.flush();
  29.                 return;
  30.             }
  31.             else
  32.          {
  33.      %>
  34.      共有<strong><%=rowCount%></strong>条留言&nbsp;&nbsp;&nbsp;&nbsp;
  35.      <%
  36.      }
  37.             
  38.             String strCurPage=request.getParameter("page");
  39.             
  40.             //表示当前的页数。

  41.             int curPage;
  42.             
  43.             if(strCurPage==null)
  44.                 curPage=1;
  45.             else
  46.                 curPage=Integer.parseInt(strCurPage);
  47.             
  48.             //定义每页显示的留言数。

  49.             int countPerPage=5;
  50.             
  51.             //计算所有留言显示需要的总页数。

  52.             int pageCount=(rowCount+countPerPage-1)/countPerPage;
  53.             
  54.             //移动游标到结果集中指定的行。如果显示的是第一页,curPage=1,

  55.             //游标移动到第1行。

  56.             rs.absolute((curPage-1)*countPerPage+1);
  57.             
  58.          //如果是第1页,则显示不带链接的文字,如果不是第1页,

  59.          //则给用户提供跳转到第一页和上一页的链接。

  60.             if(curPage==1)
  61.             {    
  62.         %>
  63.          第一页&nbsp;&nbsp;&nbsp;&nbsp;
  64.          上一页&nbsp;&nbsp;&nbsp;&nbsp;
  65.         <%
  66.             }
  67.             else
  68.             {
  69.         %>
  70.          <a href="index.jsp?page=<%=1%>">第一页</a>
  71.          &nbsp;&nbsp;&nbsp;&nbsp;
  72.          <a href="index.jsp?page=<%=curPage-1%>">上一页</a>
  73.          &nbsp;&nbsp;&nbsp;&nbsp;
  74.         <%
  75.             }
  76.             //如果当前页是最后一页,则显示不带链接的文字,如果不是最后一页,

  77.             //则给用户提供跳转到最后一页和下一页的链接。

  78.             if(curPage==pageCount)
  79.             {
  80.             
  81.         %>
  82.          下一页&nbsp;&nbsp;&nbsp;&nbsp;
  83.          最后页&nbsp;&nbsp;&nbsp;&nbsp;
  84.         <%
  85.             }
  86.             else
  87.             {
  88.         %>
  89.          <a href="index.jsp?page=<%=curPage+1%>">下一页</a>
  90.          &nbsp;&nbsp;&nbsp;&nbsp;
  91.          <a href="index.jsp?page=<%=pageCount%>">最后页</a>
  92.          &nbsp;&nbsp;&nbsp;&nbsp;
  93.         <%
  94.             }
  95.     
  96.             int i=0;
  97.             
  98.             //以循环的方式取出每页要显示的数据,因为在前面针对要显示的页数,

  99.             //调用了rs.absolute((curPage-1)*countPerPage+1);

  100.             //所以是从游标所在的位置取出当前页要显示的数据。

  101.             while(i<countPerPage && !rs.isAfterLast())
  102.             {
  103.                 out.println("

    "
    );
  104.                 out.println("用户名:"+rs.getString("gst_user"));
  105.                 out.println("  ");
  106.                 
  107.                 Timestamp ts=rs.getTimestamp("gst_time");
  108.                 long lms=ts.getTime();
  109.                 Date date=new Date(lms);
  110.                 Time time=new Time(lms);
  111.                 
  112.                 out.println("留言时间:"+date+" "+time);
  113.                 
  114.                 out.println("  ");
  115.                 out.println("用户IP:"+rs.getString("gst_ip")+"
    "
    );
  116.                 out.println("主题:"+rs.getString("gst_title")+"
    "
    );
  117.                 out.println("内容:"+rs.getString("gst_content"));
  118.                 i++;
  119.                 rs.next();    
  120.             }
  121.             rs.close();
  122.             stmt.close();
  123.             conn.close();
  124.         %>
  125.     </body>
  126. </html><%out.flush();%>

26-30行  最后一行 都用到  out.flush()语句的调用非常重要 否则 MyResponseWrapper类的toByteArray()方法将得不到缓冲区中的内容

6  配置JDBC数据源

http://blog.chinaunix.net/space.php?uid=26284395&do=blog&id=3038773

7  创建不雅字句与替换内容文件

文件word.txt存放在WEB-INF目录下 

word.txt

 

  1. 我靠=我*
  2. fuck=****
  3. 他妈的=他**

 

8   配置web.xml

 

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="2.5"
  3.     xmlns=""
  4.     xmlns:xsi=""
  5.     xsi:schemaLocation="
  6.     /web-app_2_5.xsd">
  7. <filter>
  8.         <filter-name>GuestbookFilter</filter-name>
  9.         <filter-class>filter.GuestbookFilter</filter-class>
  10.         <init-param>
  11.             <param-name>word_file</param-name>
  12.             <param-value>/WEB-INF/word.txt</param-value>
  13.         </init-param>
  14.     </filter>
  15.     
  16.     <filter-mapping>
  17.         <filter-name>GuestbookFilter</filter-name>
  18.         <url-pattern>/process.jsp</url-pattern>
  19.     </filter-mapping>
  20.     
  21.      <filter-mapping>
  22.         <filter-name>GuestbookFilter</filter-name>
  23.         <url-pattern>/index.jsp</url-pattern>
  24.         <dispatcher>REQUEST</dispatcher>
  25.         <dispatcher>FORWARD</dispatcher>
  26.     </filter-mapping>
  27.        
  28. </web-app>
阅读(4175) | 评论(2) | 转发(1) |
给主人留下些什么吧!~~

xueliangfei2011-12-16 15:36:04

无色T恤: 会有什么安全隐患呢?.....
比如写入一个以HTML格式的操作数据库的代码窃取数据库的信息 这样算吧

无色T恤2011-12-15 23:24:59

会有什么安全隐患呢?