OWASP(CsrfGuard)源码解析05----JavaScriptServlet分析

一、介绍

前面已经 介绍了 listener 、 filter , 整个jar 包后端就剩下一个 JavaScriptServlet 了
在这里插入图片描述

二、JavaScriptServlet分析

JavaScriptServlet 继承了 HttpServlet 类, 主要重写了 init(), doGet() , doPost() 方法,下面主要分析这3个方法:

2.1 init() 方法

主要就是赋值, 并打印log

	@Override
	public void init(ServletConfig theServletConfig) {
	  servletConfig = theServletConfig;
	  //打印log
	  CsrfGuardServletContextListener.printConfigIfConfigured(servletConfig.getServletContext(),
			  "Printing properties after Javascript servlet, note, the javascript properties have now been initialized: ");
	}

2.2 doGet() 方法

方法 doGet() 的主要流程就是:

  1. 先获取 请求 转过来的url , 并和 配置的url 进行正则匹配, 如果 不同,那就报错
  2. 接着 比较域名, 如果域名不同也报错, 这里的domain 设置的值, 从 context-parm -> config 配置-> default value 顺序获取
  3. 将 请求url 后缀 加到 白名单
  4. 返回 csrfguard.js 内容
@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
	    // 获取来访者的地址
		String refererHeader = request.getHeader("referer");
		boolean hasError = false;
		// 从配置文件里面获取 org.owasp.csrfguard.JavascriptServlet.refererPattern 对应的 来访者匹配的正则
		Pattern javascriptRefererPattern = CsrfGuard.getInstance().getJavascriptRefererPattern();
		// 如果 refererHeader  不为null 并且 和设置的不匹配,那就 打印log ,设置response 的状态为 404 
		if(refererHeader != null && !javascriptRefererPattern.matcher(refererHeader).matches()) {
			CsrfGuard.getInstance().getLogger().log(LogLevel.Error, "Referer domain " + refererHeader + " does not match regex: " + javascriptRefererPattern.pattern());
			response.sendError(404);
			hasError = true;
		}
		
		// 如果	refererHeader  不为null,并且 需要匹配 domain
		// 这里的domain 设置的值, 从 context-parm -> config 配置-> default value 顺序获取
		if (refererHeader != null && CsrfGuard.getInstance().isJavascriptRefererMatchDomain()) {
			//this is something like http://something.com/path or https://something.com/path
			String url = request.getRequestURL().toString();
			// 对两个url 从 第8位开始截取, 并比较,如果不相同那就 设置 状态404 
			String requestProtocolAndDomain = CsrfGuardUtils.httpProtocolAndDomain(url);
			String refererProtocolAndDomain = CsrfGuardUtils.httpProtocolAndDomain(refererHeader);
			if (!refererProtocolAndDomain.equals(requestProtocolAndDomain)) {
				CsrfGuard.getInstance().getLogger().log(LogLevel.Error, "Referer domain " + refererHeader + " does not match request domain: " + url);
				hasError = true;
				response.sendError(404);
			}
			
		}
		// 如果没有error , 获取 从 contextPath 开始的路径, 放到 javascriptUris里面 ,后面这里作为白名单
		// csrfguard.js 文件内容
		if (!hasError) {
			
			//save this path so javascript is whitelisted
			String javascriptPath = request.getContextPath() + request.getServletPath();
			
			/**
			理论上这里应该只有一个url ,但是不排除配置了多个的可能,所以javascriptUris 是一个Set 集合
			**/
			if (javascriptUris.size() < 100) {
				javascriptUris.add(javascriptPath);
			}
			
			writeJavaScript(request, response);
		}
	}

2.3 writeJavaScript() 方法

方法writeJavaScript() 主要是 :

  1. 根据 rotate 或者 token-per-page 是否配置 ,进行设置 Header t头部信息
  2. 将 csrfguard.js 内容里面的 一些 替换到 ,比较 当前的 domain 等等

	private void writeJavaScript(HttpServletRequest request, HttpServletResponse response) throws IOException {
		HttpSession session = request.getSession(true);
		CsrfGuard csrfGuard = CsrfGuard.getInstance();

		/** cannot cache if rotate or token-per-page is enabled **/
		if (csrfGuard.isRotateEnabled() || csrfGuard.isTokenPerPageEnabled()) {
			response.setHeader("Cache-Control", "no-cache, no-store");
			response.setHeader("Pragma", "no-cache");
			response.setHeader("Expires", "0");
		} else {
			response.setHeader("Cache-Control", CsrfGuard.getInstance().getJavascriptCacheControl());
		}

		response.setContentType("text/javascript");

		/** build dynamic javascript **/
		String code = CsrfGuard.getInstance().getJavascriptTemplateCode();

		code = code.replace(TOKEN_NAME_IDENTIFIER, CsrfGuardUtils.defaultString(csrfGuard.getTokenName()));
		code = code.replace(TOKEN_VALUE_IDENTIFIER, CsrfGuardUtils.defaultString((String) session.getAttribute(csrfGuard.getSessionKey())));
		code = code.replace(INJECT_INTO_FORMS_IDENTIFIER, Boolean.toString(csrfGuard.isJavascriptInjectIntoForms()));
		code = code.replace(INJECT_GET_FORMS_IDENTIFIER, Boolean.toString(csrfGuard.isJavascriptInjectGetForms()));
		code = code.replace(INJECT_FORM_ATTRIBUTES_IDENTIFIER, Boolean.toString(csrfGuard.isJavascriptInjectFormAttributes()));
		code = code.replace(INJECT_INTO_ATTRIBUTES_IDENTIFIER, Boolean.toString(csrfGuard.isJavascriptInjectIntoAttributes()));
		code = code.replace(INJECT_INTO_XHR_IDENTIFIER, String.valueOf(csrfGuard.isAjaxEnabled()));
		code = code.replace(TOKENS_PER_PAGE_IDENTIFIER, String.valueOf(csrfGuard.isTokenPerPageEnabled()));
		code = code.replace(DOMAIN_ORIGIN_IDENTIFIER, CsrfGuardUtils.defaultString(parseDomain(request.getRequestURL())));
		code = code.replace(DOMAIN_STRICT_IDENTIFIER, Boolean.toString(csrfGuard.isJavascriptDomainStrict()));
		code = code.replace(CONTEXT_PATH_IDENTIFIER, CsrfGuardUtils.defaultString(request.getContextPath()));
		code = code.replace(SERVLET_PATH_IDENTIFIER, CsrfGuardUtils.defaultString(request.getContextPath() + request.getServletPath()));
		code = code.replace(X_REQUESTED_WITH_IDENTIFIER, CsrfGuardUtils.defaultString(csrfGuard.getJavascriptXrequestedWith()));

		/** write dynamic javascript **/
		OutputStream output = null;
		PrintWriter writer = null;

		try {
			output = response.getOutputStream();
			writer = new PrintWriter(output);

			writer.write(code);
			writer.flush();
		} finally {
			Writers.close(writer);
			Streams.close(output);
		}
	}

2.4 doPost() 方法

doPost() 主要是校验用的, 主要逻辑是:

  1. 首先获取 头部信息 FETCH-CSRF-TOKEN ,判断是否有值,如果 有, 那就 进行 设置 tokenName + tokenValue 并返回
  2. 如果没有,判断是否开启了tokenPerPage ,如果不是, 报错404, 如果是, 设置对应的token 值
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
		CsrfGuard csrfGuard = CsrfGuard.getInstance();
		// 获取 头部里面的信息 FETCH-CSRF-TOKEN 对应的值,这个值是 通过 js  设置的,为1 说明只需要 token 
		// 如果为null,如果开启了tokenPerPage ,那就将所有的page -> token  对应关系拼接转为String 返回
		String isFetchCsrfToken = request.getHeader("FETCH-CSRF-TOKEN");
		// 如果都不为空, 设置 tokenName + tokenValue 并返回
		if (csrfGuard != null && isFetchCsrfToken != null){
			fetchCsrfToken(request, response);
		} else {
		    // 如果 isFetchCsrfToken  为null, 判断是否是 tokenPerPage ,如果不是, 报错404, 如果是, 设置对应的token 值
			if (csrfGuard != null && csrfGuard.isTokenPerPageEnabled()) {
				writePageTokens(request, response);
			} else {
				response.sendError(404);
			}
		}
	}

2.5 fetchCsrfToken() 方法

private void fetchCsrfToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取session
		HttpSession session = request.getSession(true);
		@SuppressWarnings("unchecked")
        CsrfGuard csrfGuard = CsrfGuard.getInstance();
        // 从 配置里面获取 tokenName
		String token_name = csrfGuard.getTokenName();
		// 从session里面获取token value
		String token_value = (String) session.getAttribute(csrfGuard.getSessionKey());
		// 进行name + token 拼接
		String token_pair = token_name + ":" + token_value;

		/** setup headers **/
		response.setContentType("text/plain");

		/** write dynamic javascript **/
		OutputStream output = null;
		PrintWriter writer = null;
       // 放到response 里面返回
		try {
			output = response.getOutputStream();
			writer = new PrintWriter(output);

			writer.write(token_pair);
			writer.flush();
		} finally {
			Writers.close(writer);
			Streams.close(output);
		}
	}

2.6 writePageTokens() 方法

private void writePageTokens(HttpServletRequest request, HttpServletResponse response) throws IOException {
		HttpSession session = request.getSession(true);
		@SuppressWarnings("unchecked")
		// 从 session 里面获取 pageTokenKey 的集合 Owasp_CsrfGuard_Pages_Tokens_Key
		Map<String, String> pageTokens = (Map<String, String>) session.getAttribute(CsrfGuard.PAGE_TOKENS_KEY);
		// 这里获取pageTokensString  的所有值
		String pageTokensString = (pageTokens != null ? parsePageTokens(pageTokens) : Strings.EMPTY);

		/** setup headers **/
		response.setContentType("text/plain");
		response.setContentLength(pageTokensString.length());

		/** write dynamic javascript **/
		OutputStream output = null;
		PrintWriter writer = null;
        // 返回
		try {
			output = response.getOutputStream();
			writer = new PrintWriter(output);

			writer.write(pageTokensString);
			writer.flush();
		} finally {
			Writers.close(writer);
			Streams.close(output);
		}
	}

三、小结

本章主要对 JavaScriptServlet里面的 doGet(),doPost() 以及涉及到的方法 进行了 分析, 相对还是比较简单的,下一章 分析一下最后一部分,也是 核心部分 csrfguard.js.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直打铁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值