'Java'에 해당되는 글 26건

  1. 2011.11.16 JAVA/JSP SQL Injection 해킹 방지를 위한 코딩 5
  2. 2011.11.13 Java Map 반복(Iteration)시키는 3가지 방법 5
  3. 2011.11.09 디폴트 패키지에 있는 자바 class 실행하기 ( Could not find the main class 에러날때 ) 1
  4. 2011.10.18 WAS(Tomcat 또는 기타등등~) 메모리 사용현황 모니터링 JSP 3
  5. 2011.10.10 Runtime 에 jar 파일 클래스패스에 추가 시키기 : RuntimeJarLoader 1

JAVA/JSP SQL Injection 해킹 방지를 위한 코딩


SQL Injection 은 사용자가 요상한 문자열을 입력해서 쿼리를 요상하게 바꿔 버려서 아무 아이디로나 로그인이 훅 되버린다던지 하는 쪼금은 싱거운 해킹방법중의 하나라고 나는 알고 있다.


뭐 예를들어 아이디 비번을 입력하고 로그인 할때

아이디로  
admin' OR '1'='1 요런걸 입력하고 로그인을 했을때 admin 으로 싹 로그인 되 버리는 어처구니 없는 일이 발생하는 것이 SQL Injection 처리를 쪽바로 해주지 않았을때 일어나는 참혹한 일이다.


SQL Injection 이 발생하는 전형적인 코드는 다음과 같다.
String userid=request.getParameter("userid");
String password=request.getParameter("password");

....
....

Statement stmt = conn.createStatement();
ResultSet rs = 
   stmt.executeQuery("select count(*) as cnt from member where userid='"+userid+"' and password='"+password+"'");

....
....



위 코드처럼 사용자 정보를 받아서 쿼리를 실행하고 실행결과가 1 이면 로그인 시켜주는 코드가 있다.

그런데 사용자가 아디디로 admin' OR '1'='1  을 입력하고 비밀번호로 abcd 를 입력한 후 로그인 시켜주셈~ 이라고 하면 우째될까?

위 코드대로라면  select count(*) from member where userid='amdin' OR '1'='1' and password = 'abcd'   이 쿼리가 실행되 버린다 -_-;;;

무섭다. 만약 사용자 테이블에 userid 가 admin 이라는 레코드가 있다면 admin 으로 로그인 되버리는 것이다. 

이런 어처구니 없는일이 놀랍게도 몇몇 허접한 사이트에서는 더러 통하는데도 있다 . -_-



아무튼 헛소리는 고만하고 본론으로 들어가서 SQL Injection 을 방지하려면 우째해야 될까? 

Statement 대신  PreparedStatement 를 쓰면 된다. 요렇게.
....
....

PreparedStatement stmt = conn.prepareStatement("select count(*) from member where userid=? and password=?");
stmt.setString(1, userid);
stmt.setString(2, password);

ResultSet rs = stmt.executeQuery();

....
....



PreparedStatement 만 쓰면 해결된다고 해서 또 요렇게는 쓰지 말길 -.-;
PreparedStatement stmt = 
   conn.prepareStatement("select count(*) from member where userid='" + userid + "'" and password='" + password + "'");



※ 하이버네이트는 한번도 안써봐서 잘 모르겠는데 mybatis 나 ibatis 같은 경우는 무난하게 #{xxxx} 요런것만 썼다면 SQL Injection 이 방지된다.

그런데 WHERE 절에 ${xxxx} 이렇게 있다면 다시 한번 생각해 보기 바란다.

${xxxx} 이거는 문자열 더하는것과 똑같기 때문에 위험하다. ${xxxx} 요런건 ORDER BY 절 같은데만 사용하자.


Java Map 반복(Iteration)시키는 3가지 방법


왠지 도움이 될것 같다면 추천을 *(-_-)*


이 세가지 방법 말고도 뭐 다른 방법이 있겠지만 대충 요 3가지 정도만 알고 있어도 충분하고도 넘칠것 같다.

이전까지는 제일 첫번째 방법으로만 Map 에 있는 것들을 꺼내서 썼었는데

세번째 방법도 꽤 간결하고 가독성도 좋은것 같아 앞으로 세번째 방법을 주로 써야 겠다는 생각을 해본다.


package com.tistory.stove99;

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

public class MapIterationSample {
	public static void main(String[] agrs) {
		Map<String, String> map = new HashMap<String, String>();
		
		map.put("키1", "값1");
		map.put("키2", "값2");
		map.put("키3", "값3");
		map.put("키4", "값4");
		map.put("키5", "값5");
		map.put("키6", "값6");
		
		
		// 방법1
		Iterator<String> keys = map.keySet().iterator();
		while( keys.hasNext() ){
			String key = keys.next();
			System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );
		}
		
		// 방법2
		for( Map.Entry<String, String> elem : map.entrySet() ){
			System.out.println( String.format("키 : %s, 값 : %s", elem.getKey(), elem.getValue()) );
		}
		
		// 방법3
		for( String key : map.keySet() ){
			System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );
		}
	}
}

디폴트 패키지에 있는 자바 class 실행하기 ( Could not find the main class 에러날때 )



간만에 커맨드 창에서 실행해야 하는 자바예제를 작성해 볼 일이 있어서 맨들어 보았다.

그런데 다 맨든게 이클립스에서 run 을 하면 실행이 잘되는데 커맨드창에서 java 클래스명.class 로 실행을 하면

뒤져라 Could not find the main class 요러면서 main 함수를 찾을 수 없다는 에러만 뱉어 내고 실행이 잘 안됬다.


결국은 해결했는데 뭔가 많이 꾸리한 느낌을 받지 아니할 수가 없다.

예제로 맨들어본 아무 패키지 선언도 안된 디폴트 패키지에 있는 Test.java
public class Test {
	public static void main(String[] args) {
		System.out.println("Hello Everyone~");
	}
}

 컴파일
javac Test.java

 컴파일을 한 후 대부분 요래  실행을 할것이다. 아니면 나만 그런가-_-? 아무튼!
java Test.class

 하지만 죠래 실행하면 뒤져도 실행이 안된다. 해답은 요래 실행하는 것이다  -_-
java Test


지금와서 가만 생각해보니 java Test.class 라고 입력하면 Test 라는 패키지에 class 라는 클래스를 찾나 보다 -_-;;;




※ 패키지가 있는 java 파일 커맨드에서 컴파일하기

sample 패키지 Test.java
package sample;

public class Test {
	public static void main(String[] args) {
		System.out.println("Hello Everyone~");
	}
}

컴파일
javac -d . Test.java
요래 컴파일하면 현재 디렉토리 아래로 sample 디렉토리가 생성되고 거기에 Test.class 가 컴파일 된다.

실행
java sample.Test



WAS(Tomcat 또는 기타등등~) 메모리 사용현황 모니터링 JSP

java.lang.management.ManagementFactory 클래스를 이용하면 현재 실행중인 JVM 의 여러가지 정보를 가져올수 있다.

ManagementFactory 를 통해 가져올수 있는 정보들은 대충 메소드 이름을 보면 알수 있다.


뭐 이것저것 있는것 같은데 다른것들은 나중에 시간되면 한번 써묵어 보도록 해야겠다.



아무튼 쓸 수 있는 여러가지 메소드들중 
ManagementFactory.getMemoryMXBean() 메소드와 ManagementFactory.getMemoryPoolMXBeans() 메소드를 이용하면 현재 JVM 의 메모리 사용현황을 조회하는 JSP 페이지를 작성할 수 있다.

맨들 페이지의 최종 모양은 대충 요렇다.



그리고 JSP 솟스~!
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

<%@ page import="java.lang.management.ManagementFactory"%>
<%@ page import="java.lang.management.MemoryMXBean"%>

<%
	pageContext.setAttribute("memoryBean", ManagementFactory.getMemoryMXBean());
	pageContext.setAttribute("poolBeans", ManagementFactory.getMemoryPoolMXBeans());
%>

<html>
<head>
	<title>JVM Memory Monitor</title>

	<style type="text/css">
		body{ font-family:"Myriad Pro","Myriad Web","Tahoma","Helvetica","Arial",sans-serif; }
		table{ border-collapse: collapse; }
		td,th{ padding: 5px; }
		th { background-color: navy; color: #fff; font-weight: bold; }
		td { text-align: right; }
	</style>
</head>

<body>
	<h1>TOTAL</h1>
	
	<table border="1" width="100%">
		<colgroup>
			<col width="20%"/>
			<col width="20%"/>
			<col width="20%"/>
			<col width="20%"/>
			<col width="20%"/>
		</colgroup>
		<tr>
			<th>Usage</th>
			<th>Max</th>
			<th>Init</th>
			<th>Used</th>
			<th>Committed</th>
		</tr>
		<tr>
			<td style="text-align: left">Heap Memory Usage</td>
			<td><fmt:formatNumber value="${memoryBean.heapMemoryUsage.max/(1024 * 1024)}" 
					maxFractionDigits="1" />MB</td>
			<td><fmt:formatNumber value="${memoryBean.heapMemoryUsage.init/(1024 * 1024)}" 
					maxFractionDigits="1" />MB</td>
			<td><fmt:formatNumber value="${memoryBean.heapMemoryUsage.used/(1024 * 1024)}" 
					maxFractionDigits="1" />MB</td>
			<td><fmt:formatNumber value="${memoryBean.heapMemoryUsage.committed/(1024 * 1024)}"
					maxFractionDigits="1" />MB</td>
		</tr>
		<tr>
			<td style="text-align: left">Non-heap Memory Usage</td>
			<td><fmt:formatNumber value="${memoryBean.nonHeapMemoryUsage.max/(1024 * 1024)}" 
					maxFractionDigits="1" />MB</td>
			<td><fmt:formatNumber value="${memoryBean.nonHeapMemoryUsage.init/(1024 * 1024)}" 
					maxFractionDigits="1" />MB</td>
			<td><fmt:formatNumber value="${memoryBean.nonHeapMemoryUsage.used/(1024 * 1024)}" 
					maxFractionDigits="1" />MB</td>
			<td><fmt:formatNumber value="${memoryBean.nonHeapMemoryUsage.committed/(1024 * 1024)}" 
					maxFractionDigits="1" />MB</td>
		</tr>
	</table>

	<hr/>
	<h1>Memory Pools</h1>
	
	<c:forEach var="bean" items="${poolBeans}">
		<h2>${bean.name}(${bean.type})</h2>
		<table border="1" width="100%">
			<colgroup>
				<col width="20%"/>
				<col width="20%"/>
				<col width="20%"/>
				<col width="20%"/>
				<col width="20%"/>
			</colgroup>
			<tr>
				<th>Usage</th>
				<th>Max</th>
				<th>Init</th>
				<th>Used</th>
				<th>Committed</th>
			</tr>
			<tr>
				<td style="text-align: left">Memory Usage</td>
				<td><fmt:formatNumber value="${bean.usage.max/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.usage.init/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.usage.used/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.usage.committed/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
			</tr>
			<tr>
				<td style="text-align: left">Peak Usage</td>
				<td><fmt:formatNumber value="${bean.peakUsage.max/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.peakUsage.init/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.peakUsage.used/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.peakUsage.committed/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
			</tr>
			<tr>
				<td style="text-align: left">Collection Usage</td>
				<td><fmt:formatNumber value="${bean.collectionUsage.max/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.collectionUsage.init/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.collectionUsage.used/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
				<td><fmt:formatNumber value="${bean.collectionUsage.committed/(1024 * 1024)}" 
						maxFractionDigits="1" />MB</td>
			</tr>
		</table>
	</c:forEach>
</body>
</html>

이 페이지를 실행하기 위해서는 jstl-1.2.jar 파일을 WEB-INF/lib 폴더에 추가시켜 주면되고,

maven 을 이용하는 경우는 dependencies 에
<dependency>
	<groupId>jstl</groupId>
	<artifactId>jstl</artifactId>
	<version>1.2</version>
</dependency>
을 추가하면 된다.

jstl-1.2.jar



Runtime 에 jar 파일 클래스패스에 추가 시키기 : RuntimeJarLoader

자바로 만든 프로그램을 실행시킬때 java -classpath "jar 파일들 목록" "실행할 자바클래스" 요런식으로 프로그램에서 사용하는 jar 파일을

클래스패스에 추가시켜 줘야 된다.


그런데 톰캣같은 WAS 를 보면 WEB-INF\lib 폴더 아래에 있는 jar 파일들을 딱히 클래스패스에 추가시켜 주지 않아도 지가 알아서 클래스 패스에 추가시켜준다.


요런 기능과 비슷한일을 하는 클래스를 맨들어 보았다.
import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 
 * @author stove99.tistory.com
 *
 */
public class RuntimeJarLoader {
	public static void loadJarIndDir(String dir){
		try{
			final URLClassLoader loader = (URLClassLoader)ClassLoader.getSystemClassLoader();
			final Method method = URLClassLoader.class
                                       .getDeclaredMethod("addURL", new Class[]{URL.class});
			method.setAccessible(true);
			
			new File(dir).listFiles(new FileFilter() {
				public boolean accept(File jar) {
					// jar 파일인 경우만 로딩
					if( jar.toString().toLowerCase().contains(".jar") ){
						try{
							// URLClassLoader.addURL(URL url) 메소드 호출
							method.invoke(loader, new Object[]{jar.toURI().toURL()});
							System.out.println(jar.getName()+ " is loaded.");
						}catch(Exception e){
							System.out.println(jar.getName()+ " can't load.");
						}
					}
					return false;
				}
			});
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
}

RuntimeJarLoader.java



※ 코드 설명

일단 현재 실행중인 JVM 에 동적으로 jar 파일을 클래스 패스에 추가 시킬려면
요 코드로 시스템의 클래스 로더를 가져와야 한다.
URLClassLoader loader = (URLClassLoader)ClassLoader.getSystemClassLoader();

가져온 현재 JVM 의 클래스 로더를 이용하여 jar 파일을 로딩시켜 주면 되는데

문제는 jar 파일을 로딩시켜주는 일을 하는 메소드( URLClassLoader.addURL(URL url) )가 protected 라는 것이다.

그래서 loader.addURL("jar 파일"); 요런식으로 외부에서 직접 호출이 불가능 하다.



뭔가 이유가 있어서 protected 로 만들어 놨겠지만 나의 목표를 위해서는 꼭 저 addURL 메소드를 호출해야 할 필요가 있다.

아무튼 저 addURL 메소드를 호출하기 위한 방법으로 Java reflection 을 이용했다.

요코드로 호출할 메소드를 정의하고
Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});


핵심은 요부분이 되겠다. protected 로 선언된 메소드를 public 이로 바꿔주는 일을 한다. 요렇게 public 으로 바꿔준다음
method.setAccessible(true);


요런 Java reflection 코드를 이용해서 addURL() 을 호출해 주면, 바라던 Runtime 에 jar 파일을 클래스 패스에 추가시킬수 있다.
method.invoke(loader, new Object[]{jar.toURI().toURL()});



※ 참고 RuntimeJarLoader 사용법
// c:\lib 폴더 아래에 있는 jar 파일들 로딩
RuntimeJarLoader.loadJarInDir("c:/lib");

// 현재 실행중인 경로의 하위에 있는 lib 폴더 로딩
RuntimeJarLoader.loadJarInDir("./lib");

요렇게 쓰면 된다~
prev 1 2 3 4 5 6 next