Java EE 정리 13
14 Mar 2019
Reading time ~10 minutes
Java EE 정리 13 - EL, EL 취약점(XSS), JSTL
EL(Expression Language)
- 브라우저에 출력하기 위해 만들어진 언어
- 페이지 지시자가
isELIgnored = false
인 상태에서 사용가능 - null은 출력하지 않는다.
- 문자열은 “” 또는 ‘’ 모두 사용가능
- 변수에 직접출력은 할 수 없다. * scriptlet에서 만들어진 변수를 직접출력할 수 없다. * scope객체(page, request, session, application)를 사용하면 가능 * 그러나 JSTL로 만들어진 변수는 직접 출력가능
- 일반적으로 값을 설정하는 작업은 JSP에서 하지 않기 때문에 EL의 사용되는 변수를 설정한 곳을 찾아야 한다.
${코드}
<!--
'코드'에 들어갈 수 있는 것들
* 연산자
- 산술 : +,-,*,/,%(mod)
- 관계 : >(gt),<(lt),>=(ge),<=(le),==(eq),!=(ne)
- 논리 : &&(and), ||(or)
- 삼항 : ? :
- 단항 : !
* scope객체 (pageScope, requestScope, sessionScope, applicationScope)
- Scope은 생략가능
* empty : 비어있는지 물어볼 때 사용
* Web Parameter 출력 : param.이름 (이름은 HTML Form Control의 이름)
- 이름이 같을 때(배열)는 출력할 순 없다.
-->
- scope객체 - pageContext
- 값을 현재 페이지에서만 사용가능
<%
int i = 10;
// pageContext.setAttribute("이름", 값);
pageContext.setAttribute(a, i);
%>
<!-- EL : ${pageScope.이름} -->
EL: ${pageSope.a} <!-- i가 직접출력 -->
- scope객체 - request * 값을 현재 페이지와 forward로 이동한 페이지에서도 사용가능
<%
request.setAttribute("이름", 값);
%>
EL : ${requestScope.이름}
- scope객체 - session * 접속자마다 하나씩 생성 * 모든 페이지에서 사용가능
<%
session.setAttribute("이름",값);
%>
EL : ${sessionScope.이름}
- scope객체 - application * 최초 접속자에의해 하나만 생성 * 모든 접속자가 하나를 사용(공유)
<%
application.setAttribute("이름",값);
%>
EL : ${applicationScope.이름}
- EL 연산자 사용 예
<!-- use_el.jsp -->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
isELIgnored="false"
info="브라우저 출력을 목적으로 만들어진 EL 사용"
%>
...
<div>
<strong>연산자</strong>
<div>
<ul>
<li>산술</li>
<li>3+14=${ 3+14 }</li>
<li>14-3=${ 14-3 }</li>
<li>3*14=${ 3*14 }</li>
<li>14/3=${ 14/3 }</li>
<li>14%3=${ 14%3 } / ${ 14 mod 3 }</li>
<li>관계</li>
<li>14 > 3 = ${ 14 > 3 } / ${ 14 gt 3 }</li>
<li>14 < 3 = ${ 14 < 3 } / ${ 14 lt 3 }</li>
<li>14 >= 3 = ${ 14 >= 3 } / ${ 14 ge 3}</li>
<li>14 <= 3 = ${ 14 <= 3 } / ${ 14 le 3}</li>
<li>14 == 3 = ${ 14 == 3 } / ${ 14 eq 3 }</li>
<li>14 != 3 = ${ 14 != 3 } / ${ 14 ne 3 }</li>
<li>논리</li>
<li>14 > 3 && 3 < 14 = ${ 14 > 3 && 3 < 14 } / ${ 14 gt 3 and 3 lt 14 }</li>
<li>14 < 3 || 3 > 14 = ${ 14 < 3 || 3 > 14 } / ${ 14 lt 3 or 3 gt 14 }</li>
<li>삼항</li>
<li>10은 ${ 10%2 == 0 ? "짝수" : '홀수' } / ${ 10 mod 2 eq 0 ? "짝수" : '홀수' }</li>
<li>9는 ${ 9%2 == 0 ? "짝수" : '홀수' } / ${ 9 mod 2 eq 0 ? '짝수' : "홀수" }</li>
<%-- 산술, 관계, 논리, 삼항 이외에는 사용할 수 없습니다. --%>
<%-- <li>${ 1 << 2 }</li> 쉬프트 연산자 사용 시 에러발생 --%>
</ul>
</div>
</div>
- EL Parameter 사용 예
<div>
<strong>EL 사용하여 Parameter 처리</strong>
<ul>
<!-- empty : 대상이 null이거나 ''인 경우 비교할 때 사용 -->
<li>이름 : ${ empty param.name ? "아래링크를 클릭하거나 폼에 입력" : param.name }</li>
<!-- empty를 쓰는건 ${ param.name == null ? ... } 과 동일 -->
<li>나이 : ${ param.age }</li>
</ul>
</div>
<br/>
<a href="use_el.jsp?name=jungyun&age=30">파라메터 연습</a>
<br/><br/>
<form action="use_el.jsp" method="get">
<label>이름 : </label>
<input type="text" name="name" class="inputBox"/><br/>
<label>나이 : </label>
<input type="text" name="age" class="inputBox"/><br/>
<input type="submit" value="입력" class="btn"/><br/>
</form>
- EL Scope 객체 사용 예 * EL에서 Scope은 생략가능
<%
String name = "기미철";
// scope 객체에 할당한 값은 EL에서 사용할 수 있다.
pageContext.setAttribute("pageName", name); // pageScope
request.setAttribute("reqName", name); // requestScope
session.setAttribute("sesName", name); // sessionScope
application.setAttribute("appName", name); // applicationScope
%>
<ul>
<li><strong>scope객체의 값 사용</strong></li>
<li>변수의 직접 사용(사용불가) : ${ name }</li>
<li>pageScope 사용 : ${ pageScope.pageName } / ${ pageName }</li>
<li>requestScope 사용 : ${ requestScope.reqName } / ${ reqName }</li>
<li>sessionScope 사용 : ${ sessionScope.sesName } / ${ sesName }</li>
<li>applicationScope 사용 : ${ applicationScope.appName } / ${ appName }</li>
</ul>
- 넘겨온 값 EL로 사용하기 예 * Servlet은 View로 안쓰임, 데이터 처리만
// TestForward.java, Servlet을 실행시키면 request객체에 값을 설정 후 forward로 이동
public class TestForward extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name = "이재찬";
String[] hobby = { "집에가기", "점심먹기", "낮잠자기" };
request.setAttribute("name", name);
request.setAttribute("hobby", hobby);
RequestDispatcher rd = request.getRequestDispatcher("date0314/use_forward.jsp");
rd.forward(request, response);
}
}
<!-- use_forward.jsp -->
...
<ul>
<li>이름 : <strong>${ name }</strong></li>
<li>취미 : <!-- EL은 배열을 출력할 수 없다(배열 주소가 찍힘) -->
<%
String[] hobby = (String[])request.getAttribute("hobby");
if (hobby != null) {
for (int i=0; i < hobby.length; i++) {
out.print(hobby[i]);
out.println(" ");
}
} else {
out.println("취미가 없네요..");
}
%>
</li>
</ul>
...
EL 취약점 - XSS(Cross Site Scripting)
- EL은 XSS(Cross Site Scripting) 취약점을 갖는다.
- 악의적인 목적의 사용자가 HTML Form Control이나 QueryString에 JavaScript Code를 작성하여 서버에서 실행시키는 공격
<form action="use_el.jsp" method="get">
<label>이름 : </label> <!-- 입력받는 페이지 -->
<input type="text" name="name" class="inputBox"/><br/>
...
<!-- 입력된 값을 보여주는 페이지 -->
이름 : ${ empty param.name ? "아래링크를 클릭하거나 폼에 입력" : param.name }
- 위 이름 입력칸에 아래 내용을 입력하면..
<script type="text/javascript">alert("ㅋㅋㅋㅋㅋㅋㅋ")</script>
- EL을 사용할 땐 항상 XSS 취약점을 막아야 한다. * JSTL c:out을 사용해서 막는다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
...
<!-- JSTL의 c:out을 사용하면 XSS가 실행되지 않고 그대로 보여진다.(XSS 취약점 막기) -->
이름 : <c:out value="${ empty param.name ? '아래링크를 클릭하거나 폼에 입력' : param.name }"/>
JSTL(JSP Standard Tag Library)
- 액션태그(표준액션)를 Sun사가 아닌 외부개발자가 제작하여 배포하는 것
- OGNL(Object Graph Navigation Library) * 태그를 정의하면 태그에 따른 문장을 찾아서 변환한 후 실행하는 언어 * Markup 언어의 형태
- core, fmt, sql, xml 등 지원 * core - 변수관련, 제어문 관련, 예외처리, 페이지 삽입 등 * fmt - 날짜, 숫자 형식의 변환
- Apache에서 제작하고 배포하는 library로 WEB-INF\lib 폴더에 배포되는 jar를 설치하여 사용 * standard.jar, jstl.jar
- 사용법 * taglib를 필요로 하는 JSP에서 지시자(directive)를 선언 * prefix는 접두어 * uri 은 쓰고자 하는 태그에 따라 다른 uri 입력
<%@ taglib prefix="접두어" uri="http://java.sun.com/jsp/jstl/사용할라이브러리" %>
- 태그 사용
<prefix:지원태그명 속성="값" 속성="값" ... ></prefix:지원태그명>
<!-- 열린태그 또는 닫힘 태그로 사용 -->
<prefix:지원태그명 속성="값" 속성="값" ... />
JSTL - core
- 사용 설정
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 변수 생성 * scriptlet을 사용하지 않고 변수 생성
<c:set var="변수명" value="변수값" />
- 변수 삭제
<c:remove var="삭제할 변수명"/>
- 출력 * 들어온 값을 그대로 출력한다.
<c:out value="출력할 값"/>
- 변수관련 사용 예
<%
// EL은 변수를 직접사용할 수 없다.
// EL에서 변수를 사용하는 방법
// 1. scriptlet 변수를 JSTL로 변수 생성 후 사용
int month = 3;
// 2. scope객체에 설정 후 사용
pageContext.setAttribute("month", month);
%>
<!-- 변수의 선언 -->
<c:set var="i" value="14"/>
<c:set var="mon" value="<%= month %>"/><!-- 1번 방법 -->
<c:set var="name" value="기미철"/>
<!-- 출력 -->
<!-- c:set으로 만든 변수는 EL로 직접출력이 가능,
scriptlet에 만든 변수는 직접출력은 불가능 -->
<!-- X SS 취약점을 막기위해 EL은 항상 c:out으로 출력처리를 해야 한다 -->
i = <c:out value="${ i }"/><br/>
<strong><c:out value="${ name }"/></strong><br/>
<strong>=== EL에서 변수 사용 ===</strong><br/>
<c:set> 사용 : <c:out value="${ mon }"/><br/>
pageScope으로 사용 : <c:out value="${ pageScope.month }"/><br/>
<!-- 변수의 삭제 : 삭제된 변수는 아무것도 출력되지 않는다. -->
<c:remove var="i"/>
<c:remove var="name"/>
<br/>
<strong>변수 삭제 후</strong><br/>
i = <c:out value="${ i }"/><br/>
name = <c:out value="${ name }"/><br/>
month = <c:out value="${ mon }"/><br/>
- 제어문 - if * 단일 if만 지원 * 한 조건을 비교할 때
<c:if test="조건식">
조건에 맞을 때 수행할 문장
</c:if>
- 제어문 - choose * 여러 조건을 비교할 때
<c:choose>
<c:when test="조건식1">
조건에 맞을 때 수행할 문장
</c:when>
<c:when test="조건식2">
...
</c:when>
<c:otherwise>
모든 조건이 맞지 않을 때 수행할 문장
</c:otherwise>
</c:choose>
- 반복문 - forEach * 인덱스 사용 * 배열/리스트 값 순차적 출력
<!-- 인덱스 사용 -->
<c:forEach var="변수명" begin="시작값" end="끝값" step="증가값">
반복코드 <!-- ${변수명}을 써서 인덱스로 사용가능 -->
</c:forEach>
<!-- 배열/리스트 출력, 배열|리스트명은 거의 대부분 Scope객체로부터 얻어낸 값 -->
<c:forEach var="변수명" items="${ 배열|리스트명 }">
반복코드 <!-- ${변수명}을 써서 순서대로 변수값을 사용가능 -->
<!-- VO의 경우 ${ 변수명.getter명 } 으로 값을 얻어올 수 있다 -->
</c:forEach>
- 반복문 - forTokens * 특정 문자열로 구분하여 반복시키고 출력할 때 사용
<c:forTokens var="변수명" items="${ 특정 문자가 반복되는 문자열 }" delims="문자열을 구분할 조건">
${ 변수명 } <!-- 토큰화된 문자열 -->
</c:forTokens>
- 제어문 if 사용 예 1
<!-- jstl_if.jsp -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<!--
조건은 true | false가 들어간다.
<c:if test="false">
오늘은 목요일입니다.(true일 때만 보여진다)
</c:if>
-->
<a href="#void">공지사항 읽기</a>
<c:if test="${ param.user_role eq 'admin' }">
<a href="#void">공지사항 쓰기</a>
<a href="#void">공지사항 수정</a>
<a href="#void">공지사항 삭제</a>
</c:if>
<div>
<a href="use_if.jsp?user_role=user">일반사용자</a>
<a href="use_if.jsp?user_role=admin">관리자</a>
</div>
- 제어문 if 사용 예 2
<!-- jstl_if.jsp -->
<form action="jstl_if.jsp">
<label>이름</label> : <input type="text" name="name" class="inputBox"/>
<input type="submit" value="입력" class="btn"/>
</form><br/>
<div>
위의 Form Control에서 입력된 이름을 출력하는데 이름이 '이재찬'이라면<br/>
이름이 흘러가도록 만들어보세요.<br/><br/>
</div>
입력한 이름 :
<c:if test="${ param.name eq '이재찬' }">
<marquee>
</c:if>
<c:out value="${ param.name }"/>
<c:if test="${ param.name eq '이재찬' }">
</marquee>
</c:if>
- 제어문 choose 사용 예 1
<!-- jstl_choose.jsp -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<div>
<form action="jstl_choose.jsp" method="get">
<label>혈액형 선택</label>
<select name="blood_type">
<option value="a"${ param.blood_type eq 'a'?" selected='selected'":''}>A형</option>
<option value="b"${ param.blood_type eq 'b'?" selected='selected'":''}>B형</option>
<option value="o"${ param.blood_type eq 'o'?" selected='selected'":''}>O형</option>
<option value="ab"${ param.blood_type eq 'ab'?" selected='selected'":''}>AB형</option>
</select>
<input type="submit" value="혈액형 선택" class="btn"/>
</form>
</div>
<!-- 여러 조건을 비교할 때 choose 사용 -->
<c:choose>
<c:when test="${ param.blood_type eq 'o' }">
<c:set var="img" value="blood_type_o.png"/>
</c:when>
<c:when test="${ param.blood_type eq 'b' }">
<c:set var="img" value="blood_type_b.png"/>
</c:when>
<c:when test="${ param.blood_type eq 'ab' }">
<c:set var="img" value="blood_type_ab.png"/>
</c:when>
<c:otherwise>
<c:set var="img" value="blood_type_a.png"/>
</c:otherwise>
</c:choose>
<br/>
<img src="images/${ img }"/>
- 제어문 choose 사용 예 2
- jQuery tooltip 플러그인을 이용, 랜덤한 tooltip 효과를 보여줄 것
- https://plugins.jquery.com/ui.tooltip/
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<!-- 복붙한 플러그인 내용 -->
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<!-- Google CDN -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$( function() {
$( ".show-option" ).tooltip({
show: {
effect: "slideDown",
delay: 250
}
});
$( ".hide-option" ).tooltip({
hide: {
effect: "explode",
delay: 250
}
});
$( ".open-event" ).tooltip({
show: null,
position: {
my: "left top",
at: "left bottom"
},
open: function( event, ui ) {
ui.tooltip.animate({ top: ui.tooltip.position().top + 10 }, "fast" );
}
});
} );
</script>
...
<%
int randomNum = new Random().nextInt(3);
pageContext.setAttribute("tooltip", randomNum);
%>
<c:choose>
<c:when test="${ tooltip eq 0 }">
<c:set var="tooltipClass" value="open-event"/>
</c:when>
<c:when test="${ tooltip eq 1 }">
<c:set var="tooltipClass" value="show-option"/>
</c:when>
<c:otherwise>
<c:set var="tooltipClass" value="hide-option"/>
</c:otherwise>
</c:choose>
<div style="width:500px; height:200px; overflow:auto; border:1px solid #333;">
1조는 PC방 프로그램을 아이템으로 구현하였고, 조장
<span class="${ tooltipClass }" title="좌석, 메시지">이봉현</span>, 조원
<span class="${ tooltipClass }" title="주문, 통계">이재찬</span>,
<span class="${ tooltipClass }" title="회원관리, 관리자">김정운</span>,
<span class="${ tooltipClass }" title="주문">박은영</span>,
<span class="${ tooltipClass }" title="회원조회, 마일리지">백인제</span>,
<span class="${ tooltipClass }" title="로그인, 회원관리">정택성</span>으로 구성되어 있습니다.
PC방 프로그램을 완벽하게 구현하기 위해서 조장 이하 모든 조원은 합심하여
PC방으로 전격 출근!!! 알바를 하였습니다.
</div>
- 반복문 forEach 사용 예 1
<!-- jstl_foreach.jsp -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<select>
<c:forEach var="i" begin="1" end="100" step="1">
<option value="${ i }"><c:out value="${ i }"/></option>
</c:forEach>
</select>
- 반복문 forEach 사용 예 2
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<%
String[] movies = { "시네마 천국", "주토피아", "코어", "7인의 사무라이", "트루먼쇼", "인셉션" };
pageContext.setAttribute("movies", movies);
%>
<ul>
<c:forEach var="movie" items="${ movies }">
<c:set var="idx" value="${ idx+1 }"/> <!-- set이 0으로 자동 초기화-->
<li>${ idx } ${ movie }</li>
</c:forEach>
</ul>
- 반복문 foreach 사용 예 3 * 배열/리스트 모두 사용법은 같다.
<%
List<String> list = new ArrayList<String>();
list.add("Java SE");
list.add("Java EE");
list.add("DBMS");
list.add("HTML");
<!-- pageContext scope객체에 저장 후 EL에서사용 -->
pageContext.setAttribute("list", list);
%>
<ul>
<c:forEach var="subject" items="${ list }">
<li><c:out value="${ subject }"/></li>
</c:forEach>
</ul>
- 반복문 forEach 사용 예 4 * List가 Generic로 VO를 가진 경우 * forEach안에서 “var명.getter명”으로 값 사용
public class TestVO {
private String firstName;
private int age;
public TestVO(String firstName, int age) {
this.firstName = firstName;
this.age = age;
}
...
<%
// List가 generic으로 VO를 가진 경우
List<TestVO> voList = new ArrayList<TestVO>();
voList.add(new TestVO("정윤",30));
voList.add(new TestVO("희철",27));
voList.add(new TestVO("재찬",27));
voList.add(new TestVO("택성",27));
pageContext.setAttribute("vl", voList);
%>
<table border="1">
<thead>
<tr>
<th width="50">번호</th>
<th width="150">이름</th>
<th width="50">번호</th>
</tr>
</thead>
<tbody>
<c:forEach var="data" items="${ vl }">
<c:set var="cnt" value="${ cnt+1 }"/>
<tr>
<!-- XSS를 막기위해 c:out JSTL 사용 -->
<td><c:out value="${ cnt }"/></td>
<td><c:out value="${ data.firstName }"/></td>
<td><c:out value="${ data.age }"/></td>
</tr>
</c:forEach>
</tbody>
</table>
- 반복문 forTokens 사용 예
<%
// CSV - Comma-Separated Variables/Values
String csvData = "Java SE,Java EE,DBMS.Oracle,HTML5,JavaScript,CSS3.jQuery";
pageContext.setAttribute("csv", csvData);
%>
<ul>
<c:forTokens var="data" items="${ csv }" delims=",.">
<li><c:out value="${ data }"/></li>
</c:forTokens>
</ul>