Java安全之velocity 模板注入

Java安全之velocity 模板注入

前言

水篇文,簡單記錄整理一些雜亂無章的東西。

velocity 語法

#表示符

"#"用來標識Velocity的腳本語句,包括#set、#if 、#else、#end、#foreach、#end、#iinclude、#parse、#macro等;
如:

#if($info.imgs)
<img src="$info.imgs" border=0>
#else
<img src="noPhoto.jpg">
#end

$表示符

"$"用來標識一個對象(或理解爲變量);如
如:$i、$msg、$TagUtil.options(...)等。

{} 標識符

"{}"用來明確標識Velocity變量;
比如在頁面中,頁面中有一個$someonename,此時,Velocity將把someonename作爲變量名,若我們程序是想在someone這個變量的後面緊接着顯示name字符,則上面的標籤應該改成${someone}name。

!標識符

"!"用來強制把不存在的變量顯示爲空白。
如當頁面中包含$msg,如果msg對象有值,將顯示msg的值,如果不存在msg對象同,則在頁面中將顯示$msg字符。這是我們不希望的,爲了把不存在的變量或變量值爲null的對象顯示爲空白,則只需要在變量名前加一個“!”號即可。
如:$!msg
我們提供了五條基本的模板腳本語句,基本上就能滿足所有應用模板的要求。這四條模板語句很簡單,可以直接由界面設計人員來添加。在當前很多EasyJWeb的應用實踐中,我們看到,所有界面模板中歸納起來只有下面四種簡單模板腳本語句即可實現:
   1、$!obj  直接返回對象結果。
   如:在html標籤中顯示java對象msg的值。

<p>$!msg</p>
  在html標籤中顯示經過HtmlUtil對象處理過後的msg對象的值
  <p>$!HtmlUtil.doSomething($!msg)</p>

  2、#if($!obj) #else #end 判斷語句
   如:在EasyJWeb各種開源應用中,我們經常看到的用於彈出提示信息msg的例子。

   #if($msg)

   <script>
   alert('$!msg');
   </script>

   #end

模板注入

 @RequestMapping("/ssti/velocity1")
    @ResponseBody
    public String velocity1(@RequestParam(defaultValue="nth347") String username) {
        String templateString = "Hello, " + username + " | Full name: $name, phone: $phone, email: $email";

        Velocity.init();
        VelocityContext ctx = new VelocityContext();
        ctx.put("name", "Nguyen Nguyen Nguyen");
        ctx.put("phone", "012345678");
        ctx.put("email", "[email protected]");

        StringWriter out = new StringWriter();
        Velocity.evaluate(ctx, out, "test", templateString);

        return out.toString();
    }

傳遞拼接templateString,帶入Velocity.evaluate

查了一下官方文檔對該方法的解釋

Renders the input reader using the context into the output writer.

從context取值去做模板解析,輸出到 output writer中。

org.apache.velocity.runtime.parser.node.ASTMethod#execute

image-20220503170247401

image-20220503170319340

解析表達式,去反射調用表達式內容。解析到Runtime.exec

看到第二段代碼

 @RequestMapping("/ssti/velocity2")
    @ResponseBody
    public String velocity2(@RequestParam(defaultValue="nth347") String username) throws IOException, ParseException {
        String templateString = new String(Files.readAllBytes(Paths.get("template.vm")));
        templateString = templateString.replace("<USERNAME>", username);

        StringReader reader = new StringReader(templateString);

        VelocityContext ctx = new VelocityContext();
        ctx.put("name", "Nguyen Nguyen Nguyen");
        ctx.put("phone", "012345678");
        ctx.put("email", "[email protected]");

        StringWriter out = new StringWriter();
        org.apache.velocity.Template template = new org.apache.velocity.Template();

        RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
        SimpleNode node = runtimeServices.parse(reader, template);

        template.setRuntimeServices(runtimeServices);
        template.setData(node);
        template.initDocument();

        template.merge(ctx, out);

        return out.toString();
    }

調用template.merge做模板解析

template.merge官網文檔說明

The AST node structure is merged with the context to produce the final output

從ast樹和context中取值去做模板解析。

解析流程會走入到org.apache.velocity.runtime.parser.node.SimpleNode#render

 public boolean render(InternalContextAdapter context, Writer writer) throws IOException, MethodInvocationException, ParseErrorException, ResourceNotFoundException {
        int k = this.jjtGetNumChildren();

        for(int i = 0; i < k; ++i) {
            this.jjtGetChild(i).render(context, writer);
        }

        return true;
    }

image-20220503172256297

children變量裏面是加載vm模板填充後,並且分割處理後的內容,處理成一個個asttext,和ASTReference。ASTReference爲表達式內容。在遍歷調用children.render,調用到ASTReference.render即開始解析表達式。

image-20220503172939762

image-20220503173021492

image-20220503173135677

後語

喜歡技術分享與交流,更喜歡思維碰撞產生的火花。願各位都能獨立思考並有獨立思考空間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章