[JAVA] class 동적로딩

JAVA 2017. 10. 30. 12:49

class 동적로딩


보안 전무업체에서 사용하는 소스 취약점 점검툴로 운영중인(또는 개발중인) 소스를 점검해 보면 동적으로 Class를 로딩하는 부분이 취약 하다는 

보고서를 받게 되는 경우가 종종있습니다. 

이때 보고서에 적힌 취약점 명은 다음과 같을 경우가 있습니다.


국정원 취약점 : 2. 파일 다운로드 취약점


안행부 보안취약점 : 15. 무결성 검사없는 코드 다운로드 취약점


그리고 취약하다고 보고된 코드는 보통 다음과 같을수 있습니다.


String className = "sample.security.checksum.DesObject";


<<중략>>


Class loadedClass = Class.forName(className);

WorkerObj workerObj = (WorkerObj)loadedClass.newInstance();


이에 대한 조치를 다음과 같이 수행하라고 적혀 있다.


Class loadedClass = Class.forName(className);

if(loadedClass.getChecksum() == loadedClass.isChecksum() ){

WorkerObj workerObj = (WorkerObj)loadedClass.newInstance();

}


상기 취약점은 클래스가 동적으로 로딩되는 과정에서 다른 클래스로 대체되어 악성코드가 실행될 수 있다는 가정이 전제되어 있습니다.

따라서 점검툴은 소스상에 Class.forName() 코드가 보이면 무조건 취약 코드로 잡아 낸다. 이 과정에서 오탐도 많이 발생하는데, 예를 들어 동적으로 로딩되는 클래스가 단순히 WEB-INF>classes 밑에 놓여있는 클래스들일 경우에는 로딩 과정에서 다른 객체로 변경될 가능성이 거의 제로에 가까우므로 오탐으로 간주해도 되나, 원격지 서버에 있는 라이브러리들을 동적으로 로딩하는 경우에는 충분히 악성클래스로 대체 될 수 있으르모 가이드를 참고해서 적절한 조치를 취해야 합니다.


이때의 조치방법으로는 Object Checksum 이나 File Checksum 의 소스 코드를 소개합니다.


다음은 Object Checksum의 소스 코드입니다.


public static BigInterger getObjChecksum(Object obj) {

BigInteger bigInt;

MessageDigest mDigest = null;


if(obj == null) {

bigInt = BigInteger.ZERO;

}else{


ByteArrayOutputStream baos = new ByteArrayOutputStream();


try{

ObjectOutputStream oos = new ObjectOutputStream(baos);

oos.writeObject(obj);  

//직렬화된 객체를 ByteArrayOutputStream(baos)에 write한다.

oos.close();

mDigest = MessageDigest.getInstance("SHA-256"); 

//SHA-256 알고리즘을 사용한다.

mDigest.update(baos.toByteArray());

}catch (IOException e){

System.out.println(e.toString());

BigInteger.ZERO;

}catch (NoSuchAlgorithmException e) {

System.out.println(e.toString());

bigInt = BigInteger.ZERO; 

}

}


bigInt = new BigInteger(1, mDigest.digest());

return bigInt;

}


이렇게 작성을 하게 되면 1233459834509830455656566456464564665456 와 같은 해쉬코드가 반환됩니다.


구글링을 해보면 유사한 코드가 소개되어 있는데 대부분 해쉬 알고리즘으로 MD5를 사용한 코드가 공개되어 있습니다. 그러나 MD5는 보안 취약점이 다수 발견되어 현재는 사용하지 않는 알고리즘이므로 보다 강화된 SHA-256 알고리즘을 사용한 코드로 변경하였습니다.


다음은 File Checksum 의 소스 코드입니다.


public static String getFileChecksm(String filename){

String checksum;

byte[] byteData = createChecksum(filename);


StringBuffer sb = new StringBuffer();

for(int i = 0 ; i < byteData.length ; i++) {

sb.append(Integer.toString((byteData[i]&0xff) + 0x100, 16).substring(1));

}


checksum = sb.toString();

return checksum;

}


 public static byte[] createChecksum(String filename){

   

     byte[] result = null;

     InputStream fis;

     

     try {

         fis = new FileInputStream(filename);

         byte[] buffer = new byte[1024];

         MessageDigest mDigest = MessageDigest.getInstance("SHA-256");

         int numRead;

         do {

              numRead = fis.read(buffer);

              if (numRead > 0) {

                  mDigest.update(buffer, 0, numRead);

              }

         } while (numRead != -1);

        fis.close();

      

        result = mDigest.digest();

    } catch (FileNotFoundException e) {

        System.out.println(e.toString());

    } catch (NoSuchAlgorithmException e) {

        System.out.println(e.toString());

    } catch (IOException e) {

        System.out.println(e.toString());

    }

  

    return result;

  }


//c0bbc7bcf23736a8560f68f7af73cd974f4a48ab7bb79cb05cedab5c4549990//와 같은 해쉬코드가 반환됩니다.

'JAVA' 카테고리의 다른 글

[JAVA] Class.forName 사용하기  (0) 2017.10.30
[JAVA] class 클래스 로딩  (0) 2017.10.30
[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
[JAVA] BigInteger형을 바이트 배열로!!  (0) 2017.10.27
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

class path에서 resource 찾기


String filename = "";


Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);


가장 간단한 방법

ClassLoader.getSystemResource


자바에서는 resource 찾는 방법이 다양합니다.


모든 rresource는 URL로 정의된다. 로컬 디스크에서 찾는 리소스는 file:  

// 스킴을 사용할 것이고 외부 리소스라면  http:// 라던가 

//여러가지 sheme을 사용할 것이다.


오늘 이야기할 거리는 클래스 패스에서의 resource 찾는 방법이다.


클래스를 로딩하기 위해서는 class 파일을 바이트로 읽어 들여서 메모리에 로딩을 해야한다. 혹은 설정 파일이나 그림 파일들은 바이트로 읽기 위해서 InputStream을 얻어야 할 것이다. 즉, 클래스 패스에 존재하는 모든 클래스 파일들, 그림 파일, 사운드 파일, 설정 파일 등등 모든 파일들은 ClassLoader에서 찾아진다.


가장 간단하게 찾는 방법은!


ClassLoader.getSystemResource(name);


위와 같이 호출한다. 이는 완벽한 방법은 아닙니다. 바로 시스템 클래스로더를 사용하기 때문입니다. 


우리가 사용하는 어플리케이션은 각 쓰레드마다 다른 클래스 로더를 사용할 수 있으므로 가장 좋은 방법은!


Thread.currentThread().getContextClassLoader().getResource(name);


위와 같이 하면 현재 실행 중인 쓰레드의 클래스 로더를 얻어와서 해당 클래스 로더에게 resource 찾는 일을 위임합니다.


여기서 클래스 로더의 작동 방식은 나중에 설명드리고 일단 resource 찾는 것에 집중하겠습니다.


resource name은 사용 규칙이 있습니다.


만일 net.loggable.config 라는 팩키지에 config.xml이라는 설정 파일이 있다면


net/loggable/config/config.xml


즉, 패키에서 .을 /로 변환한 것이라고 보면 되고 파일의 확장자를 다 적어주면 끝이다.


만일 root 패키지에 있는 log4j.xml을 찾고 싶다면


log4j.xml 


즉, root는 파일명만 쓰면 됩니다.


이것 말고 또 하나는 방법은!


Class.getResource(name);


클래스에서 바로 resource를 얻어 오는 방법이 있다.


사용법은 클래스 로더에서 얻어 오는 방법과 동일하지만 resource name 규칙이 다르다.


/으로 시작하지 않으면 현재 포함된 패키지를 상대 결로로 하여 나머지 패키지 경로를 찾는다.


만일, net.loggable.config.xml 이란 패키지의 config.xml있고 net.loggable.config.Config 라는 클래스가 있다고 하면


이럴 경우 config.xml의 resource 명은 Config 클래스를 기준으로 하여 


xml/config.xml이 된다.


그렇가면 root 패키지는..?


/log4j.xml 


이렇게 /으로 시작하는 경우는 root 패키지 부터 하여 풀 네임을 사용할 수 있다.


/net/loggable/config/xml/config.xml


즉, Class로더는 상대결로를 root 패키지로 잡고 Class는 자신이 포함된 패키지를 기준으로 상대경로로 사용한다고 생각하면 된다.


추가로 현재 java를 실행한 work dir을 찾는 방법은????


System.getProperty("user.dir");


이렇게 하면 된다. 만일 실행 폴더에 같이 파일을 불러 들이고 싶으시다면 work dir을 사용하면 좋을 듯 합니다.


'JAVA' 카테고리의 다른 글

[JAVA] class 클래스 로딩  (0) 2017.10.30
[JAVA] class 동적로딩  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
[JAVA] BigInteger형을 바이트 배열로!!  (0) 2017.10.27
[JAVA] application att  (0) 2017.10.27
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

[JAVA] cache function

JAVA 2017. 10. 27. 11:15

cache function


1. ehcache.xml 등록


<cache

name="mainPage"

eternal="false"

maxElementsInMemory="1000"

timeToIdleSeconds="0"

timeToLiveSeconds="600"

overflowToDisk="false"

diskPersistent="false"

copyOnRead="false"

copyOnWrite="false"

memoryStoreEvictionPolicy="LRU"/>




2. cahe function


<%@page pageEncoding="utf-8"%>

<%@page import="org.springframework.cache.Cache"%>

<%@page import="org.springframework.cache.Cache.ValueWrapper"%>

<%@page import="org.springframework.cache.ehcache.EhCacheCacheManager"%>

<%@page import="org.springframework.cache.CacheManager"%>

<%@include file="/nci/psj/common_top.jsp" %>


<%!

/*

 * 객체를 cache에 저장한다.

 *

 * @param request

 * @param cacheKey cache 키

 * @param elementKey cache에 저장할 obejct 키

 * @param cacheObject cache에 저장할 object

 */

void addCacheElement(HttpServletRequest request, String cacheKey, 

Object elementKey, Object cacheObject){


try{

CacheManager cm = getApplicationContext(request).getBean(EhCacheCacheManager.class);

   

   Cache cache = cm.getCache(cacheKey);


if(cache != null){

cache.put(elementKey, cacheObject);

}

}catch(Exception e){

e.printStackTrace();

}

}


/*

 * cache에서 객체를 가져온다. cache에 저장된 객체가 없으면 null 리턴.

 *

 * @param request

 * @param cacheKey cache 키

 * @param elementKey cache에 저장된 obejct 키

 */

Object getCacheElement(HttpServletRequest request, String cacheKey, 

Object elementKey){

Object cacheObj = null;


try{

CacheManager cm = getApplicationContext(request).getBean(EhCacheCacheManager.class);

   

 Cache cache = cm.getCache(cacheKey);


if(cache != null){

ValueWrapper value = cache.get(elementKey);


if(value != null){

cacheObj = value.get();

}

}

}catch(Exception e){

e.printStackTrace();

}


return cacheObj;


/*

 * cache를 삭제한다. 

 *

 * @param request

 * @param cacheKey cache 키

 */

Object removeCache(HttpServletRequest request, String cacheKey){

Object cacheObj = null;


try{

CacheManager cm = getApplicationContext(request).getBean(EhCacheCacheManager.class);

      Cache cache = cm.getCache(cacheKey);


if(cache != null){

cache.clear();

}

}catch(Exception e){

e.printStackTrace();

}


return cacheObj;

}


/*

 * cache에서 객체를 삭제한다. 

 *

 * @param request

 * @param cacheKey cache 키

 * @param elementKey cache에 저장된 obejct 키

 */

Object removeCacheElement(HttpServletRequest request, String cacheKey, Object elementKey){

Object cacheObj = null;


try{

CacheManager cm = getApplicationContext(request).getBean(EhCacheCacheManager.class);

Cache cache = cm.getCache(cacheKey);


if(cache != null){

cache.evict(elementKey);

}

}catch(Exception e){

e.printStackTrace();

}


return cacheObj;

}

%>

'JAVA' 카테고리의 다른 글

[JAVA] class 동적로딩  (0) 2017.10.30
[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] BigInteger형을 바이트 배열로!!  (0) 2017.10.27
[JAVA] application att  (0) 2017.10.27
[JAVA] AES 대칭키 암복호화  (0) 2017.10.27
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

BigInteger형을 바이트 배열로!!


byte[] decBytes = dec.toByteArray();

'JAVA' 카테고리의 다른 글

[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
[JAVA] application att  (0) 2017.10.27
[JAVA] AES 대칭키 암복호화  (0) 2017.10.27
[JAVA] 1000분의 1초까지 나타내기  (0) 2017.10.20
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

[JAVA] application att

JAVA 2017. 10. 27. 10:20

application att


application.setAttribute("a123", "아자아자!");


out.println("a123:" + application.getAttribute("a123"));


'JAVA' 카테고리의 다른 글

[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
[JAVA] BigInteger형을 바이트 배열로!!  (0) 2017.10.27
[JAVA] AES 대칭키 암복호화  (0) 2017.10.27
[JAVA] 1000분의 1초까지 나타내기  (0) 2017.10.20
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

AES 대칭키 암복호화



package egovframework.kr.co.fw.util;


import java.security.SecureRandom;

import javax.crypto.Cipher;

import javax.crypto.KeyGenerator;

import javax.crypto.SecretKey;

import javax.crypto.spec.SecretKeySpec;


public abstract class SimpleCrypto

{

  private final static String TRANSFORMATION = "AES";

  private final static String  HEX  = "0123456789ABCDEF";


  private SimpleCrypto( )

  {}


  public static String encrypt( String seed , String cleartext ) throws Exception

  {

   byte[] rawKey = getRawKey( seed.getBytes( ) );

   byte[] result = encrypt( rawKey , cleartext.getBytes( ) );

   return toHex( result );

  }



  public static String decrypt( String seed , String encrypted ) throws Exception

  {

   byte[] rawKey = getRawKey( seed.getBytes( ) );

   byte[] enc = toByte( encrypted );

   byte[] result = decrypt( rawKey , enc );

   return new String( result );

  }



  private static byte[] getRawKey( byte[] seed ) throws Exception

  {

   KeyGenerator kgen = KeyGenerator.getInstance( TRANSFORMATION );

   SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG" );

   sr.setSeed( seed );

   kgen.init( 128 , sr ); // 192 and 256 bits may not be available

   SecretKey skey = kgen.generateKey( );

   byte[] raw = skey.getEncoded( );

   return raw;

  }



  private static byte[] encrypt( byte[] raw , byte[] clear ) throws Exception

  {

   SecretKeySpec skeySpec = new SecretKeySpec( raw , TRANSFORMATION );

   Cipher cipher = Cipher.getInstance( TRANSFORMATION );

   cipher.init( Cipher.ENCRYPT_MODE , skeySpec );

   byte[] encrypted = cipher.doFinal( clear );

   return encrypted;

  }



  private static byte[] decrypt( byte[] raw , byte[] encrypted ) throws Exception

  {

   SecretKeySpec skeySpec = new SecretKeySpec( raw , TRANSFORMATION );

   Cipher cipher = Cipher.getInstance( TRANSFORMATION );

   cipher.init( Cipher.DECRYPT_MODE , skeySpec );

   byte[] decrypted = cipher.doFinal( encrypted );

   return decrypted;

  }



  public static String toHex( String txt )

  {

   return toHex( txt.getBytes( ) );

  }



  public static String fromHex( String hex )

  {

   return new String( toByte( hex ) );

  }



  public static byte[] toByte( String hexString )

  {

   int len = hexString.length( ) / 2;

   byte[] result = new byte[len];

   for ( int i = 0 ; i < len ; i++ )

   {

    result[ i ] = Integer.valueOf( hexString.substring( 2 * i , 2 * i + 2 ) , 16 ).byteValue( );

   }

   return result;

  }


  public static String toHex( byte[] buf )

  {

   if ( buf == null )

   {

    return "";

   }


   StringBuffer result = new StringBuffer( 2 * buf.length );

   for ( int i = 0 ; i < buf.length ; i++ )

   {

    appendHex( result , buf[ i ] );

   }

   return result.toString( );

  }





  private static void appendHex( StringBuffer sb , byte b )

  {

   sb.append( HEX.charAt( ( b >> 4 ) & 0x0f ) ).append( HEX.charAt( b & 0x0f ) );

  }





  public static void main( String[] args ) throws Exception

  {

   String masterpassword = "1111111";

   String cleartext = "a1한글sfsfsdfsd423423423가나다라마ㅏ아말ㄴㅇ란ㅇ란아란아ㅏㅈㄷ갖다ㅏㄹㄴ알낭란아란아라낭ㅁ나ㅏ313123213213123423423423432123123ㄹㄴ아란아란ㅇ라ㅏ아말ㄴㅇ란ㅇ란아란아ㅏㅈㄷ갖다ㅏㄹㄴ알낭란아란아라낭ㅁ나ㅏ313123213213123423423423432123123ㄹㄴ아란아란ㅇ라낭라";

   System.out.println( cleartext.length( ) );

   String crypto = SimpleCrypto.encrypt( masterpassword , cleartext );

   System.out.println( crypto );

   String decryptcleartext = SimpleCrypto.decrypt( masterpassword , crypto );

   System.out.println( decryptcleartext );

  }

}

'JAVA' 카테고리의 다른 글

[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
[JAVA] BigInteger형을 바이트 배열로!!  (0) 2017.10.27
[JAVA] application att  (0) 2017.10.27
[JAVA] 1000분의 1초까지 나타내기  (0) 2017.10.20
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,

JAVA 1000분의 1초까지 나타내기


public static String getTimeStamp() {


String rtnStr = null;


//문자열로 변환하기 위한 패턴 설정

//(년도-월-일 시:분:초:초 (자정이후 초))


String pattern = "YYYYMMddhhmmssSSS";


try {

SimpleDateFormat sdfCurrent = new SimpleDateFormat(patten, Locale.KOREA);

Timestamp ts = new Timestamp(System.currentTimeMillis());


rtnStr = sdfCurrent.format(ts.getTime());

}catch (Exception e){

e.printStackTrace();

}


return rtnStr;

}

'JAVA' 카테고리의 다른 글

[JAVA] class path에서 resource 찾기  (0) 2017.10.30
[JAVA] cache function  (0) 2017.10.27
[JAVA] BigInteger형을 바이트 배열로!!  (0) 2017.10.27
[JAVA] application att  (0) 2017.10.27
[JAVA] AES 대칭키 암복호화  (0) 2017.10.27
블로그 이미지

마크제이콥스

초보 개발자의 이슈및 공부 내용 정리 블로그 입니다.

,