当前位置:WooYun(白帽子技术社区) >> java >> JavaWeb开发安全大家都是怎么处理的?

JavaWeb开发安全大家都是怎么处理的?

园长 (喵~) | 2013-01-22 16:00

最近一个人在写一个论坛系统,求共享在现实中的项目的WEB开发安全姿势。
对于常见的SQL注入采用预编译就行了,但是很多时候简单的条件较多或较为复杂的时候很多人都想偷懒拼SQL总是似乎免不了的。
写了个这样的多条件查询条件自动匹配:
public static String SQL_FORUM_CLASS_SETTING = "SELECT * from bjcyw_forum_forum where 1=1 ";
public List<Map<String, Object>> getForumClass(Map<String,Object> forum) {
    StringBuilder sql=new StringBuilder(SQL_FORUM_CLASS_SETTING);
    List<Object> ls=new ArrayList<Object>();
    if (forum.size()>0) {
      for (String key : forum.keySet()) {
        Object obj[]=(Object [])forum.get(key);
        sql = SqlHelper.selectHelper(sql, obj);
        if ("like".equalsIgnoreCase(obj[2].toString().trim())) {
          ls.add("%"+obj[1]+"%");
        }else {
          ls.add(obj[1]);
        }
      }
    }
    return jdbcTemplate.queryForList(sql.toString(),(Object[])ls.toArray());
  }


public static StringBuilder selectHelper(StringBuilder sql, Object obj[]){
    if (Constants.SQL_HELPER_LIKE.equalsIgnoreCase(obj[2].toString())) {
      sql.append(" AND "+obj[0]+" like ?");
    }else if (Constants.SQL_HELPER_EQUAL.equalsIgnoreCase(obj[2].toString())) {
      sql.append(" AND "+obj[0]+" = ?");
    }else if (Constants.SQL_HELPER_GREATERTHAN.equalsIgnoreCase(obj[2].toString())) {
      sql.append(" AND "+obj[0]+" > ?");
    }else if (Constants.SQL_HELPER_LESSTHAN.equalsIgnoreCase(obj[2].toString())) {
      sql.append(" AND "+obj[0]+" < ?");
    }else if (Constants.SQL_HELPER_NOTEQUAL.equalsIgnoreCase(obj[2].toString())) {
      sql.append(" AND "+obj[0]+" != ?");
    }
    return sql;
  }


信任客户端的参数一切参数只匹配查询条件,把参数和条件自动装配到框架。如果客户端提交了危险的SQL也没有关系在query的时候是会预编译。

XSS和Get的参数的简易处理(参数过滤得有点问题,懒得改一般就用下htmlSpecialChars):

package net.ltan.bbs.security;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;

/**
* 检测用户提交的数据
* @author selina
*
*/
public class CheckRequestData {
  
  public static String getIpAddr(HttpServletRequest request) {
    String ipAddress = null;
    ipAddress = request.getHeader("x-forwarded-for");
    if (ipAddress == null || ipAddress.length() == 0
        || "unknown".equalsIgnoreCase(ipAddress)) {
      ipAddress = request.getHeader("Proxy-Client-IP");
    }
    if (ipAddress == null || ipAddress.length() == 0
        || "unknown".equalsIgnoreCase(ipAddress)) {
      ipAddress = request.getHeader("WL-Proxy-Client-IP");
    }
    if (ipAddress == null || ipAddress.length() == 0
        || "unknown".equalsIgnoreCase(ipAddress)) {
      ipAddress = request.getRemoteAddr();
      if (ipAddress.equals("127.0.0.1")) {
        // 根据网卡取本机配置的IP
        InetAddress inet = null;
        try {
          inet = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
          e.printStackTrace();
        }
        ipAddress = inet.getHostAddress();
      }

    }
    // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
    if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                              // = 15
      if (ipAddress.indexOf(",") > 0) {
        ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
      }
    }
    return ipAddress;
  }

  
  /**
   * SQL注入于XSS攻击自动检测
   * @param str
   * @return
   */
  public static boolean checkSqlInjection(String str) {
    Logger logger = Logger.getLogger("u3");
    String limit = "-囧script囧alert囧hex囧\\(囧\\)囧\\*囧iframe囧\\+囧\\%囧window囧cookie囧and囧or囧net user囧/add囧execute囧into囧outfile囧exec囧net囧select囧count囧char囧insert囧delete囧drop囧from囧master囧truncate囧char囧declare囧or囧by囧;囧#囧%囧`囧:囧\"囧\'囧";
    String keys[] = limit.split("囧");
    for (int i = 0; i < keys.length; i++) {
      Pattern checkSQL = Pattern.compile(keys[i],Pattern.CASE_INSENSITIVE);
      Matcher matcherFaild = checkSQL.matcher(str.trim());
      if (matcherFaild.find()) {
        logger.info("攻击地址:"+str);
        return false;
      }
    }
    return true;
  }
  
  /**
   * 模拟PHP的htmlSpecialChars,替换HTML标记
   * @param str
   * @return
   */
  public static String htmlSpecialChars(String str) {
    str = str.replaceAll("&", "&amp;");
    str = str.replaceAll("<", "&lt;");
    str = str.replaceAll(">", "&gt;");
    str = str.replaceAll("\"", "&quot;");
    str = str.replaceAll("'", "&#x27;");
    return str;
  }
}


防御还得从根入手,JEECMS在这做的就非常不错。是JSP、PHP、ASP的filter让所有的动态脚本请求失效,我们也可以采用类似的手段来保护我们的WEB应用程序。
我这里采用的框架是:Spring3+SpringMVC+FreeMarker。没有采用ORM框架而是用了Spring的jdbcTemplate,缓存采用了memcached(IP限制端口修改)。服务器:apache+nginx+resin3。

展示层采用了htm,对没错只是把freemarker的ftl换做了htm然后在htm里面写freemarker的循环。


<!-- FreeMarker 配置 -->
  <servlet>
    <servlet-name>freemarker</servlet-name>
    <servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>

    <!-- FreemarkerServlet settings: -->
    <init-param>
      <param-name>TemplatePath</param-name>
      <param-value>/</param-value>
    </init-param>
    <init-param>
      <param-name>NoCache</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>ContentType</param-name>
      <param-value>text/html</param-value>
    </init-param>

    <!-- FreeMarker settings: -->
    <init-param>
      <param-name>template_update_delay</param-name>
      <param-value>0</param-value>
      <!--
        0 is for development only! Use higher value otherwise.
      -->
    </init-param>
    <init-param>
      <param-name>default_encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <init-param>
      <param-name>locale</param-name>
      <param-value>zh_CN</param-value>
    </init-param>
    <init-param>
      <param-name>number_format</param-name>
      <param-value>0.##########</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>freemarker</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>


控制层的配置:
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>*.shmtl</url-pattern>
    <url-pattern>*.php</url-pattern>
    <url-pattern>*.phpx</url-pattern>
  </servlet-mapping>


很明显我们的一切的shmtl(特殊业务保留后缀)、php(前台的普通业务请求后缀)、phpx(后台的一般请求后缀)都会被Spring处理。URL授权和业务逻辑权限控制。也许用户在前台会看到这样的连接:http://ltan.net/index.php。但是这并不是请求了服务器的index.php文件而是Controller,采用了kindEditor,简单改改就行了:
package net.ltan.bbs.app.editor;

import java.util.*;
import java.io.*;
import java.text.SimpleDateFormat;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.disk.*;
import org.apache.commons.fileupload.servlet.*;
import org.json.simple.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* kindEditor 编辑器文件上传
* @author selina
*
*/
@SuppressWarnings("unchecked")
@Controller
public class KindEditorUploadUtil {

  @RequestMapping("/editor/upload_json.php")
  public void upload(HttpServletRequest request, HttpServletResponse response)
      throws IOException, FileUploadException {

    PrintWriter out = response.getWriter();
    // 文件保存目录路径
    String savePath = request.getSession().getServletContext().getRealPath(
        "/")
        + "uploads/";
    // 文件保存目录URL
    String saveUrl = request.getContextPath() + "/uploads/";
    // 定义允许上传的文件扩展名
    HashMap<String, String> extMap = new HashMap<String, String>();
    extMap.put("image", "gif,jpg,jpeg,png,bmp");
    extMap.put("flash", "swf,flv");
    extMap.put("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
    extMap.put("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2");

    // 最大文件大小
    long maxSize = 1000000;

    response.setContentType("text/html; charset=UTF-8");

    if (!ServletFileUpload.isMultipartContent(request)) {
      out.println(getError("请选择文件。"));
      return;
    }
    // 检查目录
    File uploadDir = new File(savePath);
    if (!uploadDir.isDirectory()) {
      out.println(getError("上传目录不存在。"));
      return;
    }
    // 检查目录写权限
    if (!uploadDir.canWrite()) {
      out.println(getError("上传目录没有写权限。"));
      return;
    }

    String dirName = request.getParameter("dir");
    if (dirName == null) {
      dirName = "image";
    }
    if (!extMap.containsKey(dirName)) {
      out.println(getError("目录名不正确。"));
      return;
    }
    // 创建文件夹
    savePath += dirName + "/";
    saveUrl += dirName + "/";
    File saveDirFile = new File(savePath);
    if (!saveDirFile.exists()) {
      saveDirFile.mkdirs();
    }
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    String ymd = sdf.format(new Date());
    savePath += ymd + "/";
    saveUrl += ymd + "/";
    File dirFile = new File(savePath);
    if (!dirFile.exists()) {
      dirFile.mkdirs();
    }

    FileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload upload = new ServletFileUpload(factory);
    upload.setHeaderEncoding("UTF-8");
    List items = upload.parseRequest(request);
    Iterator itr = items.iterator();
    while (itr.hasNext()) {
      FileItem item = (FileItem) itr.next();
      String fileName = item.getName();
      if (!item.isFormField()) {
        // 检查文件大小
        if (item.getSize() > maxSize) {
          out.println(getError("上传文件大小超过限制。"));
          return;
        }
        // 检查扩展名
        String fileExt = fileName.substring(
            fileName.lastIndexOf(".") + 1).toLowerCase();
        if (!Arrays.<String> asList(extMap.get(dirName).split(","))
            .contains(fileExt)) {
          out.println(getError("上传文件扩展名是不允许的扩展名。\n只允许"
              + extMap.get(dirName) + "格式。"));
          return;
        }

        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String newFileName = df.format(new Date()) + "_"
            + new Random().nextInt(1000) + "." + fileExt;
        try {
          File uploadedFile = new File(savePath, newFileName);
          item.write(uploadedFile);
        } catch (Exception e) {
          out.println(getError("上传文件失败。"));
          return;
        }
        JSONObject obj = new JSONObject();
        obj.put("error", 0);
        obj.put("url", saveUrl + newFileName);
        out.println(obj.toJSONString());
      }
    }

  }

  private String getError(String message) {
    JSONObject obj = new JSONObject();
    obj.put("error", 1);
    obj.put("message", message);
    return obj.toJSONString();
  }

}


package net.ltan.bbs.app.editor;

import java.util.*;
import java.io.*;
import java.text.SimpleDateFormat;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileUploadException;
import org.json.simple.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* kindEditor 编辑器文件管理
* @author selina
*
*/
@SuppressWarnings("unchecked")
@Controller
public class KindEditorFileManager {

  @RequestMapping("/editor/file_manager_json.php")
  public void fileManager(HttpServletRequest request,
      HttpServletResponse response) throws IOException,
      FileUploadException {

    PrintWriter out = response.getWriter();
    // 根目录路径,可以指定绝对路径,比如 /var/www/attached/
    String rootPath = request.getSession().getServletContext().getRealPath(
        "/")
        + "uploads/";
    // 根目录URL,可以指定绝对路径,比如 http://www.yoursite.com/attached/
    String rootUrl = request.getContextPath() + "/uploads/";
    // 图片扩展名
    String[] fileTypes = new String[] { "gif", "jpg", "jpeg", "png", "bmp" };

    String dirName = request.getParameter("dir");
    if (dirName != null) {
      if (!Arrays.<String> asList(
          new String[] { "image", "flash", "media", "file" })
          .contains(dirName)) {
        out.println("Invalid Directory name.");
        return;
      }
      rootPath += dirName + "/";
      rootUrl += dirName + "/";
      File saveDirFile = new File(rootPath);
      if (!saveDirFile.exists()) {
        saveDirFile.mkdirs();
      }
    }
    // 根据path参数,设置各路径和URL
    String path = request.getParameter("path") != null ? request
        .getParameter("path") : "";
    String currentPath = rootPath + path;
    String currentUrl = rootUrl + path;
    String currentDirPath = path;
    String moveupDirPath = "";
    if (!"".equals(path)) {
      String str = currentDirPath.substring(0,
          currentDirPath.length() - 1);
      moveupDirPath = str.lastIndexOf("/") >= 0 ? str.substring(0, str
          .lastIndexOf("/") + 1) : "";
    }

    // 排序形式,name or size or type
    String order = request.getParameter("order") != null ? request
        .getParameter("order").toLowerCase() : "name";

    // 不允许使用..移动到上一级目录
    if (path.indexOf("..") >= 0) {
      out.println("Access is not allowed.");
      return;
    }
    // 最后一个字符不是/
    if (!"".equals(path) && !path.endsWith("/")) {
      out.println("Parameter is not valid.");
      return;
    }
    // 目录不存在或不是目录
    File currentPathFile = new File(currentPath);
    if (!currentPathFile.isDirectory()) {
      out.println("Directory does not exist.");
      return;
    }

    // 遍历目录取的文件信息
    List<Hashtable> fileList = new ArrayList<Hashtable>();
    if (currentPathFile.listFiles() != null) {
      for (File file : currentPathFile.listFiles()) {
        Hashtable<String, Object> hash = new Hashtable<String, Object>();
        String fileName = file.getName();
        if (file.isDirectory()) {
          hash.put("is_dir", true);
          hash.put("has_file", (file.listFiles() != null));
          hash.put("filesize", 0L);
          hash.put("is_photo", false);
          hash.put("filetype", "");
        } else if (file.isFile()) {
          String fileExt = fileName.substring(
              fileName.lastIndexOf(".") + 1).toLowerCase();
          hash.put("is_dir", false);
          hash.put("has_file", false);
          hash.put("filesize", file.length());
          hash.put("is_photo", Arrays.<String> asList(fileTypes)
              .contains(fileExt));
          hash.put("filetype", fileExt);
        }
        hash.put("filename", fileName);
        hash.put("datetime",
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file
                .lastModified()));
        fileList.add(hash);
      }
    }

    if ("size".equals(order)) {
      Collections.sort(fileList, new SizeComparator());
    } else if ("type".equals(order)) {
      Collections.sort(fileList, new TypeComparator());
    } else {
      Collections.sort(fileList, new NameComparator());
    }
    JSONObject result = new JSONObject();
    result.put("moveup_dir_path", moveupDirPath);
    result.put("current_dir_path", currentDirPath);
    result.put("current_url", currentUrl);
    result.put("total_count", fileList.size());
    result.put("file_list", fileList);

    response.setContentType("application/json; charset=UTF-8");
    out.println(result.toJSONString());

  }

  public class NameComparator implements Comparator {
    public int compare(Object a, Object b) {
      Hashtable hashA = (Hashtable) a;
      Hashtable hashB = (Hashtable) b;
      if (((Boolean) hashA.get("is_dir"))
          && !((Boolean) hashB.get("is_dir"))) {
        return -1;
      } else if (!((Boolean) hashA.get("is_dir"))
          && ((Boolean) hashB.get("is_dir"))) {
        return 1;
      } else {
        return ((String) hashA.get("filename"))
            .compareTo((String) hashB.get("filename"));
      }
    }
  }

  public class SizeComparator implements Comparator {
    public int compare(Object a, Object b) {
      Hashtable hashA = (Hashtable) a;
      Hashtable hashB = (Hashtable) b;
      if (((Boolean) hashA.get("is_dir"))
          && !((Boolean) hashB.get("is_dir"))) {
        return -1;
      } else if (!((Boolean) hashA.get("is_dir"))
          && ((Boolean) hashB.get("is_dir"))) {
        return 1;
      } else {
        if (((Long) hashA.get("filesize")) > ((Long) hashB
            .get("filesize"))) {
          return 1;
        } else if (((Long) hashA.get("filesize")) < ((Long) hashB
            .get("filesize"))) {
          return -1;
        } else {
          return 0;
        }
      }
    }
  }

  public class TypeComparator implements Comparator {
    public int compare(Object a, Object b) {
      Hashtable hashA = (Hashtable) a;
      Hashtable hashB = (Hashtable) b;
      if (((Boolean) hashA.get("is_dir"))
          && !((Boolean) hashB.get("is_dir"))) {
        return -1;
      } else if (!((Boolean) hashA.get("is_dir"))
          && ((Boolean) hashB.get("is_dir"))) {
        return 1;
      } else {
        return ((String) hashA.get("filetype"))
            .compareTo((String) hashB.get("filetype"));
      }
    }
  }
}


数据库直接采用的是DZ,所以提供了几个普遍的加密:
package net.ltan.bbs.security;

import java.util.HashMap;
import java.util.Map;

import net.ltan.bbs.util.StringUtil;

import org.apache.commons.codec.digest.DigestUtils;

/**
* 加密业务
* @author selina
*
*/
public class Encryption {
  
  /**
   * 实现Discuz用户加密算法
   * @author Administrator
   *
   */
  public static Map<String,String> Encryptions(String context)
  {
    String salt=StringUtil.randomString(6);
    String password=DigestUtils.md5Hex(DigestUtils.md5Hex(context)+salt).toString();
    Map<String,String> encryption=new HashMap<String, String>();
    encryption.put("salt",salt);
    encryption.put("password",password);
    return encryption;
  }
  
  public static Map<String,String> Encryptions(String context,String salt)
  {
    String password=DigestUtils.md5Hex(DigestUtils.md5Hex(context)+salt).toString();
    Map<String,String> encryption=new HashMap<String, String>();
    encryption.put("salt",salt);
    encryption.put("password",password);
    return encryption;
  }
  
  /**
   * 管理员密码加密
   * @param context
   * @return
   */
  public static String AdminPassWordEncryptions(String context)
  {
    StringBuffer sb=new StringBuffer();
    String md5=DigestUtils.md5Hex(context+"ltan");
    return sb.append(md5).reverse().delete(3,6).delete(13,19).delete(2,9).toString();
  }
}


异常处理就丢log就行了,界面最好不要出现报错。
log4j.appender.a_manage.File=${catalina.home}/logs/lt_manage.log
log4j.appender.a_forum.File=${catalina.home}/logs/lt_forum.log
log4j.appender.a_spr.File=${catalina.home}/logs/spring.log
log4j.appender.a_lt.File=${catalina.home}/logs/lt.log


XSS危险确实非常大,用户认证session+IP。

分享到:
  1. 1#
    回复此人 感谢
    plt (乌云菜鸟) | 2013-01-22 16:22

    Spring里面有提供相应的工具类吧。
    Spring 为 HTML 和 JavaScript 特殊字符提供了转义操作工具类,它们分别是 HtmlUtils 和 JavaScriptUtils。
    至于防止SQL注入相关的可以通过 apache commons lang 通用类包中的 StringEscapeUtils 处理。

  2. 2# 感谢(4)
    回复此人 感谢
    园长 (喵~) | 2013-01-22 16:28

    @plt 我的select都采用的预编译,理论上不用考虑SQL注入。来自客户端的信息,是存在各种未知。后台处理判断少不了。

  3. 3#
    回复此人 感谢
    plt (乌云菜鸟) | 2013-01-22 16:32

    @园长 数据权限验证,资源权限过滤。spring有自己的安全框架,资源权限的话可以写过滤器啥的,或者把资源文件放到WEB-INF目录下。暂时想到这么多。

  4. 4# 感谢(4)
    回复此人 感谢
    园长 (喵~) | 2013-01-22 16:54

    @plt 其实并不一定非要用这用那的,一般来说能自己实现而又不算麻烦的东西最好自己写。乌云的JAVA板块人比较少,貌似。

  5. 5#
    回复此人 感谢
    plt (乌云菜鸟) | 2013-01-22 17:05

    @园长 是这么个道理,不过一些成熟的东西还是可以参考的。 java大牛们都深藏不漏的;-)

  6. 6#
    回复此人 感谢
    shine (shield) | 2013-01-22 18:03

    从“用户输入程序输出”上看,这些策略在应用防御上相对基本够了吧!另外,个人觉得还要看实际的代码实现效果以及应用部署情况(另外,如果觉得有必要,CSRF也稍微搞个token预防一下啊!)。

      我觉得也是,j2ee在电商里那么火,乌云的Java大牛怎么会少呢?都藏着,鄙视他们,感谢分享(发现管理员刚给哥升官了,行使一下权利过过瘾,给你加精了!希望多来分享)!

  7. 7#
    回复此人 感谢
    随风.潜入夜 (doie.net) | 2013-01-22 19:22

    @shine 其实看到乌云上那么多的人刷S2的,很多人甚至连JSP和Servlet都分不清,更别谈什么s2MVC了,大婶们不知道是不是都很忙。ITEYE之类的大牛Java是厉害,但要是谈到安全人家根本没把安全当回事。。XSS的确不是那么容易防范,做到绝对的安全也不是那么容易。

  8. 8#
    回复此人 感谢
    空虚浪子心 | 2013-01-23 15:25

    选好框架,选好安全框架。
    关于XSS、SQL注入神马的,不应该让程序人员想,要在设计架构时先想清楚了。
    嗯  用阿里巴巴的webx3吧,虽然开源的webx3安全方案是阉割版的,但是比常见框架强很多。

  9. 9#
    回复此人 感谢
    xsser (十根阳具有长短!!) | 2013-01-23 16:04

    @园长 @shine @空虚浪子心 我觉得可以在filter这一层就实现个防火墙的....

  10. 10#
    回复此人 感谢
    xsser (十根阳具有长短!!) | 2013-01-23 16:05

    @shine  是系统自己升官的 不是管理员

  11. 11# 感谢(4)
    回复此人 感谢
    园长 (喵~) | 2013-01-23 16:25

    @空虚浪子心 阿里的确实不错,如果有一个良好的习惯SQL和XSS还是能够有效的避免的。生产中的项目一般情况下是不会引用别人的安全框架的,因为太多的人不把安全当回事。

  12. 12#
    回复此人 感谢
    shine (shield) | 2013-01-23 16:39

    @空虚浪子心

    webx对于用惯主流框架的人有转变障碍(是阿里自己的程序员说的,不是我啊!^-^)。

    @xsser

    恩!filter这一层处理比较合理!

    @xsser

    这个也能用算法判定的?我只能说从百度出来的人都是“算法帝”!

  13. 13# 感谢(1)
    回复此人 感谢
    随风.潜入夜 (doie.net) | 2013-01-23 19:04

    @shine 悲剧啊,哥不懂算法。高中文科,数学五分党。

  14. 14# 感谢(4)
    回复此人 感谢
    园长 (喵~) | 2013-01-24 10:37

    @xsser 乌云分页bug还是隐藏了?还是假删了?
    http://zone.wooyun.org/p-91
    http://zone.wooyun.org/p-90 ?
    所有领域末页是91而91没有任何内容?

  15. 15#
    回复此人 感谢
    空虚浪子心 | 2013-01-27 12:22

    @xsser 你敢不敢重新修改wooyun网站程序,不要在业务逻辑中防御SQL注入和XSS(默认转义统统去掉),仅仅使用filter中的防火墙。

  16. 16#
    回复此人 感谢
    shine (shield) | 2013-01-27 12:55

    @xsser 搞起啊!做一个demo站,剔除业务中的防御,在filter中实现。我赞助100wb,但kxlzx要搞定它并且提供详细过程在zone里!

  17. 17#
    回复此人 感谢
    GaRY | 2013-01-30 09:51

    @空虚浪子心 实际上他就是这么做的。。。利用一个类似filter作用的通用函数过滤的

添加新回复

登录 后才能参与评论.

WooYun(白帽子技术社区)

网络安全资讯、讨论,跨站师,渗透师,结界师聚集之地

登录