欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JSP安全开发之XSS漏洞详解

程序员文章站 2024-03-13 16:39:09
前言      大家好,好男人就是我,我就是好男人,我就是-0nise。在各大漏洞举报平台,我们时常会看到xss漏洞。那么问题...

前言

     大家好,好男人就是我,我就是好男人,我就是-0nise。在各大漏洞举报平台,我们时常会看到xss漏洞。那么问题来了,为何会出现这种漏洞?出现这种漏洞应该怎么修复?

正文

1.xss?xss?xss是什么鬼?

     xss又叫跨站脚本攻击(cross site scripting),我不会告诉他原本是叫css的,但是为了不和我们所用的层叠样式表(cascading style sheets)css搞混。css(跨站脚本攻击),css(层叠样式表)傻傻分不清。所以就叫xss咯。

2.xss的危害是什么?

实验一:

0x00构造代码

<%@ page language="java" import="java.util.*" pageencoding="utf-8"%>
<%
string path = request.getcontextpath();
string basepath = request.getscheme()+"://"+request.getservername()+":"+request.getserverport()+path+"/";
%>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
 <head>
 <base href="<%=basepath%>">
 <title>my jsp 'index.jsp' starting page</title>
  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0"> 
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  <meta http-equiv="description" content="this is my page">
 </head>
 <body>
 <div style="margin: 0 auto">
 <%
//设置编码
   request.setcharacterencoding("utf-8");
//接收用户传入值
   string tmp = request.getparameter("opr");
   //减速传入值是否为空
   if(tmp == null){
     out.print("111");
   }else{
     //转码
     string opr = new string(tmp.getbytes("iso-8859-1"),"utf-8");
     out.print(opr);
   }
 %>
   我是内容
 </div>
 </body>
</html>

0x01环境布局

JSP安全开发之XSS漏洞详解

0x02漏洞演练

我们访问:http://localhost:8080/xss/index.jsp?opr=i%e6%98%a5%e7%a7%8b

JSP安全开发之XSS漏洞详解

然后访问:http://localhost:8080/xss/index.jsp?opr=0nise

JSP安全开发之XSS漏洞详解

最后我们发现了一个“伟大的规律”:

   opr参数等于什么页面就打印什么。(好像是废话)

我们接着来加载一个图片看看

访问:http://localhost:8080/xss/index.jsp?opr=%3cimg%20src=%221.png%22%3e%3c/img%3e

JSP安全开发之XSS漏洞详解

既然图片都可以加载,那么我们js文件是不是也阔以加载呢?

访问:http://localhost:8080/xss/index.jsp?opr=%3cscript%3ealert(/i%e6%98%a5%e7%a7%8b%e7%a4%be%e5%8c%ba%e6%ac%a2%e8%bf%8e%e5%a4%a7%e5%ae%b6/)%3c/script%3e

JSP安全开发之XSS漏洞详解

js?js?那么是不是可以来改变跳转后地址?

访问:http://localhost:8080/xss/index.jsp?opr=%3cscript%3elocation.href=%27http://bbs.ichunqiu.com%27%3c/script%3e

JSP安全开发之XSS漏洞详解

既然xss都可以加载js,那么,我们是不是通过js来打开本地的某些东西?

JSP安全开发之XSS漏洞详解

提前放了一个md5.exe文件

访问:http://localhost:8080/xss/index.jsp?opr=<script> var objshell = new activexobject("wscript.shell");objshell.run("g:/work/xss/webroot/md5.exe");</script>

JSP安全开发之XSS漏洞详解

既然连本地文件都可以打开那么远程文件木马?来个电脑恶搞?这个自己慢慢象限。我可没说啊。。。。。

文件都可以打开,那么写一些文件呢?

访问:http://localhost:8080/xss/index.jsp?opr=%3cscript%3evar%20fso,tf;fso%20=%20new%20activexobject(%22scripting.filesystemobject%22);tf%20=%20fso.createtextfile(%22d:\\test.txt%22,true);tf.writeline(%22i%e6%98%a5%e7%a7%8b%e7%a4%be%e5%8c%ba%e6%ac%a2%e8%bf%8e%e6%82%a8%22);tf.close();alert(%22%e6%96%87%e4%bb%b6%e5%86%99%e5%85%a5%e6%88%90%e5%8a%9f%ef%bc%81%22);%3c/script%3e

JSP安全开发之XSS漏洞详解

JSP安全开发之XSS漏洞详解

通过以上实验我们可以看出opr参数赋值操作。如果opr参数没有值的话,就无法执行执行,被攻击者必须访问攻击者提前设计好的才能攻击。这种xss攻击方式叫做:存储型xss

如果你想看到更给力的实验,请接着往下看。

实验二:

前言:

大部分网站都会和数据打交道那么,xss漏洞出现这些网站是什么样子的?

0x00构造代码

数据库部分

JSP安全开发之XSS漏洞详解

basedao.java

import java.sql.connection;
import java.sql.drivermanager;
import java.sql.preparedstatement;
import java.sql.resultset;
import java.sql.sqlexception;
import java.sql.statement;
public class basedao {
  //打开连接
  public connection getconn(){
    connection conn = null;
    try {
      class.forname("com.microsoft.sqlserver.jdbc.sqlserverdriver");
      conn = drivermanager.getconnection("jdbc:sqlserver://localhost:1433;databasename=sqltmp","sa","sa");
    } catch (classnotfoundexception e) {
      e.printstacktrace();
    } catch (sqlexception e) {
      e.printstacktrace();
    }
 
    return conn;
  }
   
  //关闭链接的方法
  public void closeall(connection conn,statement stat,resultset rs){
    try {
      if(rs != null)
        rs.close();
      if(stat != null)
        stat.close();
      if(conn != null)
        conn.close();
       
    } catch (sqlexception e) {
      e.printstacktrace();
    }
  }
  //重载关闭方法
  public void closeall(connection conn,preparedstatement pstat,resultset rs){
    try {
      if(rs != null)
        rs.close();
      if(pstat != null)
        pstat.close();
      if(conn != null)
        conn.close();
       
    } catch (sqlexception e) {
      e.printstacktrace();
    }
  }
  //继续重载
  public void closeall(connection conn,preparedstatement pstat){
    try {
      if(pstat != null)
        pstat.close();
      if(conn != null)
        conn.close();
    } catch (sqlexception e) {
      e.printstacktrace();
    }
  }
  //增删改的公用方法
  public int update(string sql,object[] pram){
     
    preparedstatement pstat = null;
    connection conn = null;
    int a = 0;
     
    try {
      conn = getconn();
      pstat =conn.preparestatement(sql);
      //遍历参数集合,将集合中的参数对应添加到sql语句中
      for (int i = 1; i <= pram.length; i++) {
        pstat.setobject(i, pram[i-1]);
      }
      //调用方法
      a = pstat.executeupdate();
       
    } catch (sqlexception e) {
      e.printstacktrace();
    }finally{
      closeall(conn, pstat);
    }
    return a;
  }
}
  

commentdao.java

import java.sql.*;
import java.util.*;
import entity.*;
public class commentdao extends basedao {
  /**
   * 获取所有留言
   * */
  public list<comm> getcomment(){
    //sql语句
    string sql = "select cid,cname,ccontext from comments";
    list<comm> list = new arraylist<comm>();
    //数据库连接对象
    connection conn = null;
    //sql执行对象
    preparedstatement pstmt = null;
    //数据库执行返回值
    resultset rs = null;
    try {
      //创建数据库链接
      conn = this.getconn();
      //创建sql执行对象
      pstmt = conn.preparestatement(sql);
      //执行sql语句  返回值
      rs = pstmt.executequery();
      //读取
      while (rs.next()) {
        comm comment = new comm();
        comment.setcid(rs.getint("cid"));
        comment.setcname(rs.getstring("cname"));
        comment.setccontext(rs.getstring("ccontext"));
        list.add(comment);
      }
    } catch (exception e) {
      e.printstacktrace();
    }finally{
      //关闭
      this.closeall(conn, pstmt, rs);
    }
    return list;
  }
  public int addcomment(comm comment){
    string sql = "insert into comments values(?,?)";
    //受影响行数
    int result = 0;  
    //数据库连接对象
    connection conn = null;
    //sql执行对象
    preparedstatement pstmt = null;
    try {
      //创建数据库链接
      conn = this.getconn();
      //创建sql执行对象
      pstmt = conn.preparestatement(sql);
      //设置参数
      pstmt.setstring(1, comment.getcname());
      pstmt.setstring(2, comment.getccontext());
      //执行sql语句
      result = pstmt.executeupdate();
    } catch (exception e) {
      e.printstacktrace();
    }finally{
      this.closeall(conn, pstmt);
    }
    return result;
  }
}

commentservlvet

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import entity.*;
public class commentservlvet extends httpservlet {
  /**
   * doget()
   */
  public void doget(httpservletrequest request, httpservletresponse response)
      throws servletexception, ioexception {
    request.setcharacterencoding("utf-8");
    response.setcontenttype("text/html;charset=utf-8");
    printwriter out = response.getwriter();
    string opr = request.getparameter("opr");
    commentdao commentdao = new commentdao();
    //检索参数是否为空
    if(opr == null || opr.equals("all")){
      request.setattribute("all", commentdao.getcomment());
      //转发
      request.getrequestdispatcher("comment.jsp").forward(request, response);
    }else if (opr.equals("add")){
      comm comment = new comm();
      comment.setcname(request.getparameter("uname"));
      comment.setccontext(request.getparameter("context"));
      if(commentdao.addcomment(comment) > 0){
        out.print("<script>alert('留言成功');location.href='commentservlvet?opr=all';</script>");
      }else{
        out.print("<script>alert('留言失败');location.href='commentservlvet?opr=all';</script>");
      }
    }else{
      request.setattribute("all", commentdao.getcomment());
      //转发
      request.getrequestdispatcher("comment.jsp").forward(request, response);
    }
    out.flush();
    out.close();
  }
 
  /**
   * dopost()
   */
  public void dopost(httpservletrequest request, httpservletresponse response)
      throws servletexception, ioexception {
    doget(request, response);
  }
}

comment.jsp

<%@ page language="java" import="java.util.*,entity.*" pageencoding="utf-8"%>
<%
string path = request.getcontextpath();
string basepath = request.getscheme()+"://"+request.getservername()+":"+request.getserverport()+path+"/";
%>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
 <head>
 <base href="<%=basepath%>">
 <title>my jsp 'comment.jsp' starting page</title>
  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0"> 
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  <meta http-equiv="description" content="this is my page">
 </head>
 <body>
 <%
   request.setcharacterencoding("utf-8");
   if(request.getattribute("all") == null){
     request.getrequestdispatcher("commentservlvet?opr=all").forward(request, response);
   }
 %>
 <table>
 <%
   list<entity.comm> list = (list<entity.comm>)request.getattribute("all");
    for(int i = 0; i < list.size(); i++ ){
 %>
     <tr>
     <td><%=list.get(i).getcname() %></td>
     <td><%=list.get(i).getccontext() %></td>
     </tr>
     <%
   }
 %>
 </table>
 <form action="commentservlvet?opr=add" method="post">
   <textarea rows="5" cols="30" name="context"></textarea>
   昵称:<input type="text" name="uname" />
   <input type="submit" value="提交" />
 </form>
 </body>
</html>

0x01漏洞实验

root@1~#

我们在留言板留言:

<script> var objshell = new activexobject("wscript.shell");objshell.run("g:/work/xss/webroot/md5.exe");</script>

JSP安全开发之XSS漏洞详解

然后访问:http://localhost:8080/xss/comment.jsp

JSP安全开发之XSS漏洞详解

这样只要访问这个页面,软件就自动打开了,来个远程文件?慢慢领悟。

root@2~#

我们在留言板留言:

复制代码 代码如下:
<script>var fso,tf;fso = new activexobject("scripting.filesystemobject");tf = fso.createtextfile("d:\\test.txt",true);tf.writeline("i春秋社区欢迎您");tf.close();alert("文件写入成功!");</script>

JSP安全开发之XSS漏洞详解

然后访问: http://localhost:8080/xss/comment.jsp

JSP安全开发之XSS漏洞详解

文件写入成功。

root@3~#

留言内容:

[code]<script>location.href='http://bbs.ichunqiu.com'</script>[code]

访问页面:http://localhost:8080/xss/comment.jsp

JSP安全开发之XSS漏洞详解

访问留言页面自动跳转到攻击者特定的网站。难道这就是传说中的劫持吗?

3.xss防御方案

正所谓哪里有攻击,哪里就有防御。xss一样,有攻击的方式,也有防御的方案。

el表达式+jstl标签库

el(expression language):[size=12.0000pt]为了使jsp写起来更简单。表达式语言的灵感来自于ecmascript和xpath表达式语语言,他提供了jsp中简化表达式的方法,让jsp代码更简单。

jstl(jsp standard tag library):开放源代码的jsp标签库。

实验一防御代码:

<%@ page language="java" import="java.util.*" pageencoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%
string path = request.getcontextpath();
string basepath = request.getscheme()+"://"+request.getservername()+":"+request.getserverport()+path+"/";
%>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
 <head>
 <base href="<%=basepath%>"> 
 <title>my jsp 'index.jsp' starting page</title>
  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0"> 
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  <meta http-equiv="description" content="this is my page">
 </head>
 <body>
 <div style="margin: 0 auto">
 <%
   request.setcharacterencoding("utf-8");
   string tmp = request.getparameter("opr");
   //减速传入值是否为空
   if(tmp == null){
     out.print("111");
   }else{
     //转码
     string opr = new string(tmp.getbytes("iso-8859-1"),"utf-8");
     request.setattribute("name", opr);
     %>
     <c:out value="${requestscope.name }"></c:out>
     <%
   }
 %>
   我是内容
 </div>
 </body>
</html>

JSP安全开发之XSS漏洞详解

实验二防御代码:

<%@ page language="java" import="java.util.*,entity.*" pageencoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%
string path = request.getcontextpath();
string basepath = request.getscheme()+"://"+request.getservername()+":"+request.getserverport()+path+"/";
%>

<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
 <head>
 <base href="<%=basepath%>">
 <title>my jsp 'comment.jsp' starting page</title>
  <meta http-equiv="pragma" content="no-cache">
  <meta http-equiv="cache-control" content="no-cache">
  <meta http-equiv="expires" content="0"> 
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  <meta http-equiv="description" content="this is my page">
 </head>
 <body>
 <%
   request.setcharacterencoding("utf-8");
   if(request.getattribute("all") == null){
     request.getrequestdispatcher("commentservlvet?opr=all").forward(request, response);
   }
 %>
 <table>
<!-- 防御xss方案 -->
 <c:foreach var="x" items="${requestscope.all }">
 <tr>
   <td>
   <c:out value="${x.getcname() }"></c:out>
   </td>
   <td>
   <c:out value="${x.getccontext() }"></c:out>
   </td>
 </tr>
 </c:foreach>
 </table>
 <form action="commentservlvet?opr=add" method="post">
   <textarea rows="5" cols="30" name="context"></textarea>
   昵称:<input type="text" name="uname" />
   <input type="submit" value="提交" />
 </form>
 </body>
</html>

JSP安全开发之XSS漏洞详解

结束语

        技术无黑白,专研甚好。