반응형

 

 

빵집 다운로드 최신버전 무료로 다운받아 사용하세요 ( 사용법 추가 )

 

 

원래 집에서 프로그램을 설치할 때는 알집을 많이 사용하곤 했었는데

 

 

회사에서는 라이센스 문제로 빵집이라는 프로그램과 반디집이라는

 

 

무료 압축 솔루션을 알게되었습니다.

 

 

사실 압축만 잘 풀면 되지 용량이 많거나 무거운 것은 그다지 필요 없거든요

 

 

빵집과 반디집 중 아무거나 사용하면 되는데

 

 

그 중에서 귀여운 빵모양이 있는 빵집을 선택했습니다.

 

 

빵집이 2015년까지는 인터넷에서 쉽게 다운로드 받을 수 있었는데

 

 

어찌된 일인지 어느순간부터는 빵집이 검색에 걸리지 않게 되었습니다ㅠ

 

 

구글링을 열심히 했더니 설치파일을 가지고 있는 사람들이 있어서 받을 수 있었지만

 

 

언제 사라질 지 모르는 이 두려움에 업로드 해놓기로 했습니다.

 

 

 

#빵집 최신버전 다운로드 및 설치

bz4setup.exe
2.94MB

 

 

 

빵집은 설치와 사용이 아주 편하게 되어 있습니다.

 

 

 

 

 

bz4setup.exe 파일을 실행하면 바로 설치할 수 있습니다.

 

 

 

 

개인, 기업, 관광서, 학교, 피씨방 등 기타 모든 곳에서 무료로 사용할 수 있는 빵집!

 

 

빵집은 프리웨어이기에 정품인증서나 제품코드가 필요 없이

 

 

모든 제품이 정품이고 단속 대상도 아닙니다

 

 

진짜 빵집 개발자 양병규님은 재능기부천사 아닐까요^^

 

 

 

 

 

 

다음 > 다음 > 설치를 누르면 샤샤샥 설치가 완료됩니다!

 

 

#빵집 압축하기

 

요즘 압축을 사용할 일은 별로 없지만 한번 끄적여 보겠습니다.

 

 

 

 

1. 빵집을 실행시킨 후 새로압축을 클릭합니다.

 

 

 

 

2. 압축할 파일들을 선택하세요!

 

 

 

 

3. 압축 될 파일명과 경로를 지정합니다.

 

 

 

 

 

4. 압축 완료!

 

 

 

 

#빵집 압축풀기

 

 

 

 

압축 푸는 방법은 더 쉽습니다.

 

 

 

1. 압축 파일을 double click 하여 파일을 엽니다.

 

 

2. 압축 파일 안에 내용이 보이는데 압축풀기를 클릭합니다.

 

 

 

3. 풀어 넣을 경로를 지정 후 압축 풀기를 클릭합니다.

 

 

 

4. Desktop 폴더 안에 압축 파일들이 풀어진 것을 볼 수 있습니다.

 

 

 

 


 

빵집이 무료압축프로그램인데도 불구하고

 

 

알집에서 제공하는 ALZ 확장자의 압축을 풀 수 있어서 사용하기 정말 좋았는데

 

 

업데이트가 되지 않고 있어서 아쉬운 부분이 많이 있습니다.

 

 

고급적인 기능을 사용하는 ( 대용량 파일을 압축하거나 해제하는.. ) 것이 아니라면

 

 

빵집은 참 귀여운 프로그램인것 같습니다

 

 

설치나 사용법에 문의가 있으시면 아는 한도안에서 대답해드리겠습니다^^

 

 

감사합니다.

 

 

bz4setup.exe
2.94MB

 

 

 

마태복음 28장 19~20절

 

그러므로 너희는 가서 모든 민족을 제자로 삼아

아버지와 아들과 성령의 이름으로 세례를 베풀고

내가 너희에게 분부한 모든 것을 가르쳐 지키게 하라

볼지어다

내가 세상 끝날가지 너희와 항상 함께 있으리라 하시니라

 

 

 

시편 1편 1~2절

 

복 있는 사람은 악인들의 꾀를 따르지 아니하며

죄인들의 길에 서지 아니하며

오만한 자들의 자리에 앉지 아니하고

오직 여호와의 율법을 즐거워하여

그의 율법을 주야로 묵상하는 도다

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

오라클 계정생성, 권한부여

 

 

안녕하세요?

 

오늘은 오라클 명령어 관련하여

 

계정생성하는 방법과

 

권한부여 하는 방법에 대해 알아보겠습니다.

 

자세히 확인하시고 테스트 한번씩 해보시면 금방 습득하실 수 있을것이라 예상합니다.

 


계정 생성하기

CREATE USER 유저ID IDENTIFIED BY "비밀번호";

 

 

계정 비밀번호 변경

ALTER USER "유저ID" IDENTIFIED BY "비밀번호";
 

 

 

계정 삭제

DROP USER "유저ID" CASCADE;
  


( CASCADE 명령어로 관련 SCHEMA 를 모두 삭제 가능합니다. )

 

 

모든 계정 조회

  SELECT * FROM ALL_USERS;

  SELECT * FROM DBA_USERS;
  

 

 

 

시스템 권한 리스트

CREATE USER : 데이터 베이스 유저 생성 권한

SELECT ANY TABLE : 모든 유저의 테이블 조회 권한

CREATE SESSION : 데이터베이스 접속 권한

CREATE TABLE : 테이블 생성 권한

CREATE VIEW : view 생성 권한

CREATE PROCEDURE USER : procedure, function, package 생성 권한

CREATE SEQUENCE  : sequence 생성 권한

SYSDBA : 데이터베이스 관리 최고 권한

SYSOPER : 데이터베이스 관리 권한

 
 
DML 권한 부여 및 권한 취소

GRANT  DELETE, INSERT, SELECT, UPDATE ON 테이블명 TO 유저ID;

REVOKE DELETE, INSERT, SELECT, UPDATE ON 테이블명 TO 유저ID;

 

권한부여 관련해서는 아래 포스팅 참고하시면 더 자세한 내용이 있습니다.

 

오라클 테이블 권한 부여 ( GRANT / REVOKE )

 

 

 

DBA 권한 부여

GRANT CONNECT, RESOURCE, DBA TO 유저ID;

 

 

 

 

 

이상으로 oracle 명령어 관련한 내용을 알아봤습니다.

 

 

한 주 잘 마무리 하시고 좋은 주말 보내세요^^

 

 

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

오라클 CASE WHEN 문법과 DECODE 문법의 예제를 알아보자

 

 

 

 

오늘은 오라클의 case 문과 decode 문에 대해서 포스팅을 해보려고 합니다.

 

case 문과 decode 문을 사용할 때는

 

일반적으로 프로그래밍을 할 때 if 문을 사용하는 것과 동일한 용도로 sql 에서 사용하겠죠?

 

 

아래는 오라클 공식 사이트에서 제공하는 

 

 CASE 와 DECODE 의 문법을 정리해놓은 그림입니다.

 

 

CASE 문법

 

 

 

DECODE 문법

 

 

 

 

딱 한눈에 봐도 CASE 문을 매우 복잡해 보이고

 

DECODE 는 간단해보이지 않나요??

 

 

그럼 사용방법을 알아보겠습니다.

 

 

 

 

CASE 문법

 

simple case expression 과 search case expression 으로 나뉘어져있지요?

 

 

simple case expression

 

SELECT cust_last_name

             , CASE credit_limit WHEN 100 THEN 'Low'

                                               WHEN 5000 THEN 'High'

                                               ELSE 'Medium'

                END

FROM customers;

 

 

 

credit_limit 가 100 이면 Low, 5000 이면 High , 이도저도 아니면 Medium 을 출력하는 문장입니다.

 

위처럼 credit_limit 를 기준으로 결과값을 도출해내는 것이 simple case expression 입니다.

 

 

search case expression

 

SELECT AVG(CASE WHEN e.salary > 2000 THEN e.salary ELSE 2000 END) "Average Salary"

  FROM employees e;

 

 

salary 가 2000 보다 크면 salary 값을 출력, 아니면 ( 2000보다 같거나 작으면 ) 2000 을 입력해서 평균 급여를 출력하는 문장입니다.

 

 

CASE 문 뒤에 특정필드를 기준으로 값을 비교해서 TRUE 값을 찾는것과

WHEN 절이 나온 후 기준필드의 특정값과 비교를 하느냐가 차이점입니다.

 

 

 

 

 

 

 

DECODE 문법

 

 

그렇다면 이번에는 위의 문장을 DECODE 문법으로 바꿔서 작성해볼까요?

 

SELECT cust_last_name

             , DECODE ( credit_limit, 100, 'Low', 5000, 'High', 'Medium' )

             , CASE credit_limit WHEN 100 THEN 'Low'

                                               WHEN 5000 THEN 'High'

                                               ELSE 'Medium'

                END

FROM customers;

DECODE 

 

 

 

좀 더 짧고 간단해졌죠?

 

편의상 사용하기 쉽고 가독성이 좋은것으로 사용하시면 될 것 같습니다.

 

 

 

 

이상으로 CASE 문법과 DECODE 문법의 사용법에 대해 알아보았습니다.

 

궁금한점이 있으면 답글 남겨주세요

 

감사합니다.

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

시스템 운영정보 노출 여부 및 아파치버전 숨기는 설정

 

 

모의해킹 수행 중 자주 나오는 항목 중 하나인

 

시스템 운영정보 노출이 있습니다.

 

시스템 운영 노출이란

 

원치않는 에러페이지가 표시되거나

 

존재하지 않는 디렉토리에 접근 시

 

tomcat 의 버전 정보가 노출 될 수 있습니다.

 

 

 

 

일반적으로 버전정보가 노출되는게 뭐 어때서?

 

라고 생각할 수 있지만

 

 

웹 서비스 사용 시 버전 정보가 노출되거나

 

오류 메세지 등에서 중요한 정보가 노출되어

 

공격자에게 2차공격을 위한 정보가 제공될 가능성이 있기 때문에

 

노출을 막을 수 있다면 막는것이 좋습니다.

 

 

 

 

 

 

tomcat 설정 에러페이지 예시

 

web.xml 을 통한 오류페이지 설정

 

일반적으로 잘 아는 404 에러  500 에러를 표시하지 않는 것은

 

web.xml 에서 처리가 가능합니다.

 

<web-app>

   <error-page>

       <error-code>404</error-code>

       <location>/error404.html</location>

   </error-page>

   <error-page>

       <error-code>500</error-code>

       <location>/error500.html</location>

   </error-page>

 </web-app>

 

 

 

 

 

apache 버전 숨기는 설정 예시

 

 

apache 버전은 응답 내 헤더정보에 포함되어 있습니다.

일반적으로

Server : Apache/2.0.64 (Unix)

이렇게 표시되는데 아래 설정을 변경하겠습니다.

 

 

httpd.conf 파일의 ServerTokens 값 변경

 

변경전 : ServerTokens Full

변경후 : ServerTokens Prod

 

 

결과

변경전 :  Apache/2.0.64 (Unix)

변경후 :  Apache

 

 

 

이상으로 시스템 운영정보 노출 및 아파치버전 숨기는 설정에 대해 알아보았습니다.

 

궁금한 것이 있으면 댓글로 남겨주세요

 

감사합니다.

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

 

 

 

디렉토리 목록 노출 취약점 및 해결방법

 

 

모의해킹 수행 중 자주 나오는 항목 중 하나인

 

 

디렉토리 목록 노출이 있습니다.

 

 

디렉토리 목록 노출되는게 무엇인가요?

 

 

아래 그림처럼 예시를 보겠습니다.

 

 

WebServer 에서 일반적으로 보여지는 특정 위치를 입력하면

 

 

아래와 같이 파일을 다운로드 받을 수 있는 화면이 나옵니다.

 

 

 

 

여기서 폴더를 자유자재로 넘나들며

 

 

주요 파일을 다운로드 받을 여지가 있습니다.

 

 

icons 폴더나 images 폴더는 일반적으로 웹서버를 설치하면

 

 

default 로 생성되는 폴더이기 때문에 접근할 수 없도록 별도 처리를 해주어야 합니다.

 

 

 

 

 

 

해결방법

 

 

잘못된 서버 설정으로 인하여 발생하였으니

 

URL 입력 시 노출되지 않도록 처리해야 합니다.

 

 

 

방법1. 웹서버 설정에서 디렉토리 인덱싱 기능 비활성화

 

방법1은 apache 를 예로 들겠습니다.

 

httpd.conf 파일에서 indexes 인자를 제거 합니다.

 

변경전 : Options Indexes FollowSymlinks

변경후 : Options FollowSymlinks

 

변경전 : Options Indexes MultiViews

변경후 : Options MultiViews

 

변경전 : Options Indexes

변경후 : Options

 

 

위의 설정값들은

<Directory : "/app/apache/icons">

</Directory>

 

<Directory : "/app/apache/manual">

</Directory>

 

태그들 안에 있으니 찾아서 변경 후 서버를 재시작하시기 바랍니다.

 

아파치 서버 재시작 방법은 다들 아시죠?

 

 

 

bin 폴더 내

 

중지 : ./apachectl stop

시작 : ./apachectl start

 

 

 

 

 

 

방법2. 유추가능한 디렉토리명 사용 금지

 

icons , manual 외에 css , js , lib, logs 와 같이 일반적으로 많이 사용하는 폴더를 대신하여

 

유추가 어려운 폴더 이름으로 생성하시길 바랍니다.

 

 

 

 

 

이상으로 디렉토리 목록 노출 취약점과 해결방법에 대해 알아보았습니다.

 

궁금하신 것 있으면 댓글로 남겨주세요

 

감사합니다.

 

 

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

XSS 방어 방법 ( 크로스사이트스크립팅 )


웹 화면 내 input box 가 있는 곳에 script를 악의적으로 사용하여 정보를 추출할 수 있습니다.




악성 스크립트 


 
"><script>alert(document.cookie)</script>)




위와 같이 입력했을 때 외부인이 쿠키정보를 손쉽게 얻을 수 있습니다.

<> 태그나 () 등의 특수문자를 문자 그대로 인식하지 않도록 처리하여

악의적인 사용을 막을 수 있게 합니다.





처리방법





톰캣 web.xml




  
    XSS
    util.filter.XSSFilter
  
  
    XSS
    /*
  



filter-class : 특수문자를 필터하는 로직이 담긴 내용을 입력

url-pattern : 특정 경로로 유입되는 경우에 필터를 적용 ( /* 을 입력 시 모든 경로에 해당 )









Filter 를 implements 해서 상세구현을 합니다.



XSSFilter.java



 

package util.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;





public class XSSFilter implements Filter {

 @SuppressWarnings("unused")
 private Object filterConfig;

 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
  this.filterConfig = filterConfig;
 }

 @Override
 public void destroy() {
  this.filterConfig = null;
 }

 @Override
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);
 }

}












각 유형에 해당하는 문자를 필터하는 내용입니다.



RequestWrapper.java



 

package util.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class RequestWrapper extends HttpServletRequestWrapper {

 public RequestWrapper(HttpServletRequest servletRequest){
  super(servletRequest);
 }



 public String[] getParameterValues(String parameter){
  String[] values = super.getParameterValues(parameter);
  
  if(values == null){
   return null;
  }
  
  int count = values.length;
  String[] encodedValues = new String[count];
  for ( int i = 0; i<count; i++){
   encodedValues[i] = cleanXSS(values[i]);
  }
  
  return encodedValues;
 }
 
 public String getParameter(String parameter){
  String value = super.getParameter(parameter);
  if(value == null){
   return null;
  }
  return cleanXSS(value);
 }
 
 public String getHeader(String name){
  String value = super.getHeader(name);
  if(value==null){
   return null;
  }
  return cleanXSS(value);
 }
 
 
 /**
  * 크로스사이트 스크립팅 필터처리
  * @param value
  * @return
  */
 private String cleanXSS(String value){

  value = value.replaceAll("<"                                         , "<");
  value = value.replaceAll(">"                                         , ">");
  value = value.replaceAll("\\("                                       , "(");
  value = value.replaceAll("\\)"                                       , ")");
  value = value.replaceAll("'"                                         , "'");
  value = value.replaceAll("eval\\((.*)\\)"                            , "");
  value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']" , "\"\"");
  value = value.replaceAll("[\\\"\\\'][\\s]*vbscript:(.*)[\\\"\\\']"   , "\"\"");
  value = value.replaceAll("script"                                    , "");
  value = value.replaceAll("onload"                                    , "no_onload");
  value = value.replaceAll("expression"                                , "no_expression");
  value = value.replaceAll("onmouseover"                               , "no_onmouseover");
  value = value.replaceAll("onmouseout"                                , "no_onmouseout");
  value = value.replaceAll("onclick"                                   , "no_onclick");
  value = value.replaceAll("<iframe"                                   , "<iframe");
  value = value.replaceAll("<object"                                   , "<object");
  value = value.replaceAll("<embed"                                    , "<embed");
  value = value.replaceAll("document.cookie"                           , "document.cookie");
  
  
  return value;
 }
 
}




처음에 서술했던 악성 스크립트를 입력했을 때 cookie 정보가 나오지 않는 것을 확인합니다.

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

불필요한 메서드 제한


클라이언트에서 GET, POST 이외의 method 로 

작업 요청 시 보안문제가 발생할 수 있으며


PUT, DELETE 등의 method 로 인해 파일 변조 및 

삭제를 당하는 등 악의적인 이용으로 피해가 발생할 수 있습니다.



HTTP Method 란?


 - HTTP 메서드는 서버가 http 요청을 어떻게 처리하는지 지시해주는 필드이며

   서버는 HTTP method 에 Reuqest-URI 에 대해 다른 동작을 수행할 수 있습니다.

   우리가 흔희 아는 GET POST 를 말합니다.


Method 종류



  • GET

 - Request-URI 요청에 필요한 정보를 모두 담는다

 - 재요청이 잦은 경우 유용 ( ex : 새로고침 )



  • POST

 - 요청에 필요한 정보들을 body 필드에 숨긴다.

 - 길이 제한 없으며 정보가 노출되지 않도록 사용한다.

 - Text 및 binary 데이터 전송 가능하다



  • DELETE

 - Request-URI 정보 삭제



  • PUT

 - 요청에 동봉된 정보를 저장한다.



  • OPTIONS

 - Request-URI 통신에 대한 정보 제공

 - 어떤 method 기능을 제공하는지 체크

 - URI 에 대한 기능적인 체크 동작 수행

 - Request-URI 에는 body 필드 정의하지 않아도 된다.



  • HEAD

 - 요청에 대한 응답 body 를 return하지 않고 헤더 정보만 return

 - 요청에 대한 유효성, 접근성을 확인할 때 사용



  • CONNECT

 - SSL tunneling 에 사용되는 method



  • TRACE

 - 클라이언트로부터 수신한 메시지를 응답에 포함시킨다.

 - TRACE 메서드 요청에는 요청 정보를 포함하는 URI 를 사용하면 안된다.








톰캣 web.xml 설정



   
    Protected
    /*
    PUT
    DELETE
    HEAD
    TRACE
    OPTIONS
   
   
    
   
  



  • 각 태그 설명


  1. security-constraint : web-resource-collection 내부에 정의된 방식의 요청을 제한
  2. url-pattern : 특정 디렉토리에 인증(제한)을 걸수 있게 pattern 지정
  3. auth-constraint : 모든 사용자 접근불가
  4. http-method : 인증(제한)할 메서드 지정

제한할 http-method 를 하나도 설정하지 않으면 url-pattern에 해당하는 모든 요청이 제한




아파치 httpd.conf 설정


  • 허용할 method 정의


<Directory />
 <LimitExcept GET POST>
  Order allow, deny
  deny from all
 </LimitExcept>
</Directory>



  • 제한할 method 정의


<Directory />
 <Limit PUT DELETE OPTIONS>
  Order allow,deny
  Allow from all
 </Limit>
</Directory>



Directory 태그를 사용하면 Directory 별로 모두 제한을 걸어줘야 하므로

URL pattern 을 이용하여 일괄적으로 제한하는 방법도 있습니다.



  • Location 을 통한 method 제한


<Location /*>
 <LimitExcept GET POST>
  Order deny,allow
  Deny from all
 </LimitExcept>
</Location>



  • Trace Method 처리 방법


LimitExcept 제한 시 정상적으로 제한되지 않아 TraceEnable off 를 사용합니다.

(https 프로토콜 사용 시 httpd-ssl.conf 파일에도 동일한 설정 필요합니다.)


TraceEnable off






불필요한 메서드 적용 확인 방법



위의 내용을 적용을 한 후 정상적으로 반영됐는지 확인해보는 방법입니다.



  1. cmd 창 오픈
  2. telnet localhost 80
  3. 아무것도 보이지 않는 상태에서 TRACE / HTTP/1.0 입력 후 엔터 두번 
( TRACE , GET , POST 등 http method 값을 넣어주면 각각의 메서드가 허용되는지 확인할 수 있습니다. )





  • 제한 적용 전


HTTP/1.1 403 Forbidden
Date: Thu, 10 Jan 2019 08:31:11 GMT
Server: Apache/2.0.64 (Unix)
Last-Modified: Wed, 19 Dec 2018 11:05:50 GMT
ETag: "35fc89-3c6-57d5aff2a1f80"
Accept-Ranges: bytes
Content-Length: 950
Connection: close
Content-Type: text/html




  • 제한 적용 후


HTTP/1.1 200 OK
Date: Thu, 10 Jan 2019 08:32:05 GMT
Server: Apache/2.0.64 (Unix)
Last-Modified: Wed, 19 Dec 2018 11:03:29 GMT
ETag: "35fca1-2b-53d2df2c3b260"
Accept-Ranges: bytes
Content-Length: 42
Connection: close
Content-Type: text/html


반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

jquery 로 iframe form submit ( sample 포함 )




특정 페이지를 submit(호출)하기 위해

jsp 에서 iframe 을 사용했는데

iframe 의 src 에 넣은 link가

하필 보안에 위배되는 주소가 되어버렸습니다.



jsp 에서 iframe 에 사용한 src 를 어떻게 숨길지 고민하다

jqeury 를 사용해서 해당 페이지를 호출 후 

삭제하는 로직을 넣어보기로 했습니다.



변경전 jsp 구조샘플

<%@ 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" %>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>TEST</title>
<link rel="stylesheet" type="text/css" href="./css/style.css">
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript" src="./js/index.js"></script>
<form id="frm" method="post" target="cgi">
<div>
---중략---
</div>
</form>    
<iframe name="iFrm" style="display: none;" src="https://tistory.com"></iframe>


iframe 에서 src 로 입력한 부분이 소스보기시 노출되어 보안문제 발생!

과감히 iframe 태그를 삭제하고 js 파일에서 작성하도록 합니다.



index.js 에서 jquery 를 이용한 iframe 생성

function init() {
	var iframe = $('');
	$( "body" ).append(iframe);
	var src = "https://tistory.com";
	iframe.attr('src',src).load(function(){
						$('#iFrm').remove()
						});
}




body 에 onload event 를 처리한 function 에 아래내용을 넣어줬습니다.


iframe 태그를 load 한 후 바로 삭제 했으니 남아있는게 없게 되었습니다!





궁금한점 있으면 댓글 작성해주세요!

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형

Apache ErrorDocument 설정 따라하기



apache 서버를 운영 중 오류 발생 시


 - 404 not found error 

 - 500 Internal Server Error 


등의 에러메세지가 그대로 노출됩니다.



개발시 에러메세지가 노출되면

오류메세지를 바로 확인 가능하기 때문에

개발하는 입장에서는 수정개발이

용이한 장점이 있습니다.




하지만 시스템 운영시에는??



일반 사용자가 알 수 없는 에러메시지를 봐야 할 필요가 있을까요?


A : "페이지 개발이 덜 되었네~"

B : "누가 개발한건지 디테일이 부족하네"

C : "우리가 쓰는 시스템에 심각한 문제가 있는거 아니야?"

D : "다른 기능은 정상동작하고 있는게 맞을까??"


이런 생각을 할 수도 있습니다.




또한 오류발생 시 서버 경로도 노출되기 때문에

프로그램을 조금이라도 아는 사람이

의도치 않은 경로로 접속할수도 있습니다.




위의 상황을 사전방지하기 위해선

오류발생 시 개발자가 의도한 화면이 

보여지도록 처리해야 합니다.




그럼 아파치 서버 내 에러페이지 전환방법을 알아보겠습니다.







1. httpd.conf 파일


#ErrorDocument 500 "The server made a boo boo."
#ErrorDocument 404 /missing.html
#ErrorDocument 402 http://www.example.com/subscription_info.html



아파치 서버 설치시 ErrorDocument 에 기본값이며,

3가지 방법으로 사용할 수 있는 것을 확인할 수 있습니다.

활성화 하는 방법은 vi 편집기를 이용하여 # 으로 된 주석을 풀거나

내려받아서 수정 후 다시 업로드 하면 되겠죠?






- 텍스트 표시

      : 입력된 text 를 표시해줍니다.


- 서버 내부 전환

      : 서버내 만들어 놓은 페이지로 전환합니다.


- 외부 경로 전환

      : 외부 페이지로 전환합니다.




텍스트 표시와 외부경로전환은 바로 입력하면 되는데 

서버내부전환은 어떻게 할까요?



2. DocumentRoot

서버내부전환은 root path 로 인식하는 DocumentRoot 를 확인합니다.


아파치 서버 설치 시 기본설정값은 아래위치입니다.



DocumentRoot "설치경로/htdocs"



ErrorDocument 404 "/errorpage/error404.html"
ErrorDocument 500 "/errorpage/error500.html"



위와 같이 작성하면 "설치경로/htdocs/errorpage/error404.html" 에 있는 페이지로 전환하게 되니

기본 root path 를 변경하시려면 DocumentRoot 의 경로를 변경해주시면 됩니다.


또한, 예제에서는 double quotation 이 빠진 상태로 나와있는데

RHEL 과 CentOS 에서는 double quotation 을 입력해야 정상적으로 작동을 합니다.



잘 안되거나 추가로 궁금한 부분이 있으면 밑에 댓글 달아주세요^^

반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형



AES 암호화 방식을 통한 DBCP 암호화




암호화 처리는 어떤 데이터를 암호화 하느냐에 따라

일방향 암호화 처리를 할 것인지

양방향 암호화 처리를 할 것인지 결정하게 됩니다.



일방향암호화 : 사용자비밀번호와 같이 시스템관리자도 복호화가 필요없이 비교만 하면 되는 경우

양방향암호화 : 사용자이름, 휴대폰번호, 계좌번호 등 복호화하여 시스템운영상 확인이 필요한 경우


복호화가 필요한 경우는 암호화 했던 비밀키를 입력하여 풀어냅니다.



이번 포스팅에서는 tomcat 을 was 로 사용하는 경우

server.xml 에 평문으로 입력된 DB접속정보가 노출되어

2차 피해는 막자는 취지에서 샘플 소스를 공유합니다.



준비


AES 암호화를 위한 필요라이브러리

https://commons.apache.org/proper/commons-codec/download_codec.cgi



이클립스를 사용하는 경우 

commons-codec-1.11-bin.zip 파일 다운로드 받은 후 commons-codec-1.11.jar 를 java build path 에 등록합니다.




아래는 server.xml 에 등록된 DB 접속정보입니다.

username , password , url 정보가 평문으로 입력되어 있어 

인증되지 않은 사용자가 해당 파일을 오픈 했을 때 서버정보를 쉽게 알 수 있게 됩니다.







목표

username , password , url 와 같은 평문 저장을 원치 않는 속성을 암호화 처리


암호화 처리 방식 : AES-256

( SEED 방식도 많이 쓰니 다음에 정리하겠습니다..)




STEP 1.

라이브러리 등록 : commons-codec-1.11.jar


파일 다운로드 경로 : https://commons.apache.org/proper/commons-codec/download_codec.cgi


commons-codec-1.11-bin.zip 파일을 받아 압축을 풀면

해당 jar 라이브러리 파일이 존재하는데

Java Build Path에 등록합니다.





STEP 2

평문 데이터를 암호화 및 복호화 function 제공 ( AES256Util.java )



package com.encrypt;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class AES256Util {
	private String iv;
	private Key keySpec;
	
	
	public AES256Util(String key) throws UnsupportedEncodingException {
		this.iv = key.substring(0,  16);
		byte[] keyBytes = new byte[16];
		byte[] b = key.getBytes("UTF-8");
		int len = b.length;
		if(len>keyBytes.length){
			len = keyBytes.length;
		}
		System.arraycopy(b,  0, keyBytes,  0 ,  len);
		SecretKeySpec keySpec  = new SecretKeySpec(keyBytes, "AES");
				
		this.keySpec = keySpec;
		
	}
	
	public String encrypt(String str) throws NoSuchAlgorithmException, GeneralSecurityException, UnsupportedEncodingException {
		Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
		c.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes()));
		
		byte[] encrypted = c.doFinal(str.getBytes("UTF-8"));
		String enStr = new String(Base64.encodeBase64(encrypted));
		return enStr;
	}
	
	public String decrypt(String str) throws NoSuchAlgorithmException, GeneralSecurityException, UnsupportedEncodingException {
		Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
		c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes()));
		byte[] byteStr = Base64.decodeBase64(str.getBytes());
		return new String(c.doFinal(byteStr), "UTF-8");
	}
}


작업 내용 : 암호화 / 복호화 처리 함수 제공






STEP 3

서버정보 암호화 내용 확인 ( TestMain.java )




package com.encrypt;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;

public class TestMain {

	public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
		AES256Util aes = new AES256Util("testestestestses");   // 암호화 키 16자리
			
		// 암호화 된 내용
		System.out.println("scott : " + aes.encrypt("scott"));  
		System.out.println("tiger : " + aes.encrypt("tiger"));
		  
	}

}


작업내용 : STEP 2 에서 작성한 함수를 main 을 통해서 테스트할 수 있습니다.



TestMain.java 실행결과



scott : BxoN1jurrKiXrlSSvf0/ng==
tiger : JcHanmccsCwU4Z4NpnYQKg==



복호화 했을 때 입력했던 값을 받을 수 있다면 성공!



System.out.println(aes.decrypt("BxoN1jurrKiXrlSSvf0/ng=="));
System.out.println(aes.decrypt("JcHanmccsCwU4Z4NpnYQKg=="));



STEP 4


암호화 된 정보를 복호화 하는 factory 를 생성


DBCP 의 기본은 org.apache.commons.dbcp.BasicDataSourceFacroty 를 사용하고 있는데

해당 class 파일을 decompile 후 아래와 같이 customizing 했습니다.



package com.encrypt;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp.BasicDataSource;

public class EncryptDataSourceFactory implements ObjectFactory
{

	  private static final String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit";
	  private static final String PROP_DEFAULTREADONLY = "defaultReadOnly";
	  private static final String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation";
	  private static final String PROP_DEFAULTCATALOG = "defaultCatalog";
	  private static final String PROP_DRIVERCLASSNAME = "driverClassName";
	  private static final String PROP_MAXACTIVE = "maxActive";
	  private static final String PROP_MAXIDLE = "maxIdle";
	  private static final String PROP_MINIDLE = "minIdle";
	  private static final String PROP_INITIALSIZE = "initialSize";
	  private static final String PROP_MAXWAIT = "maxWait";
	  private static final String PROP_TESTONBORROW = "testOnBorrow";
	  private static final String PROP_TESTONRETURN = "testOnReturn";
	  private static final String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis";
	  private static final String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun";
	  private static final String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis";
	  private static final String PROP_TESTWHILEIDLE = "testWhileIdle";
	  private static final String PROP_PASSWORD = "password";
	  private static final String PROP_URL = "url";
	  private static final String PROP_USERNAME = "username";
	  private static final String PROP_VALIDATIONQUERY = "validationQuery";
	  private static final String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout";
	  private static final String PROP_INITCONNECTIONSQLS = "initConnectionSqls";
	  private static final String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed";
	  private static final String PROP_REMOVEABANDONED = "removeAbandoned";
	  private static final String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout";
	  private static final String PROP_LOGABANDONED = "logAbandoned";
	  private static final String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements";
	  private static final String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements";
	  private static final String PROP_CONNECTIONPROPERTIES = "connectionProperties";
	  private static final String[] ALL_PROPERTIES = { "defaultAutoCommit", "defaultReadOnly", "defaultTransactionIsolation", "defaultCatalog", "driverClassName", "maxActive", "maxIdle", "minIdle", "initialSize", "maxWait", "testOnBorrow", "testOnReturn", "timeBetweenEvictionRunsMillis", "numTestsPerEvictionRun", "minEvictableIdleTimeMillis", "testWhileIdle", "password", "url", "username", "validationQuery", "validationQueryTimeout", "initConnectionSqls", "accessToUnderlyingConnectionAllowed", "removeAbandoned", "removeAbandonedTimeout", "logAbandoned", "poolPreparedStatements", "maxOpenPreparedStatements", "connectionProperties" };
	  

	
  public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment)    throws Exception  {
	  
    if ((obj == null) || (!(obj instanceof Reference))) {
      return null;
    }
    Reference ref = (Reference)obj;
    if (!"javax.sql.DataSource".equals(ref.getClassName())) {
      return null;
    }
    Properties properties = new Properties();
    for (int i = 0; i < ALL_PROPERTIES.length; i++)
    {
      String propertyName = ALL_PROPERTIES[i];
      RefAddr ra = ref.get(propertyName);
      if (ra != null)
      {
        String propertyValue = ra.getContent().toString();
        properties.setProperty(propertyName, propertyValue);
      }
    }
    return createDataSource(properties);
  }
  
  public static DataSource createDataSource(Properties properties)
    throws Exception
  {
    BasicDataSource dataSource = new BasicDataSource();
    String value = null;
    
    value = properties.getProperty("defaultAutoCommit");
    if (value != null) {
      dataSource.setDefaultAutoCommit(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("defaultReadOnly");
    if (value != null) {
      dataSource.setDefaultReadOnly(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("defaultTransactionIsolation");
    if (value != null)
    {
      int level = -1;
      if ("NONE".equalsIgnoreCase(value)) {
        level = 0;
      } else if ("READ_COMMITTED".equalsIgnoreCase(value)) {
        level = 2;
      } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) {
        level = 1;
      } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) {
        level = 4;
      } else if ("SERIALIZABLE".equalsIgnoreCase(value)) {
        level = 8;
      } else {
        try
        {
          level = Integer.parseInt(value);
        }
        catch (NumberFormatException e)
        {
          System.err.println("Could not parse defaultTransactionIsolation: " + value);
          System.err.println("WARNING: defaultTransactionIsolation not set");
          System.err.println("using default value of database driver");
          level = -1;
        }
      }
      dataSource.setDefaultTransactionIsolation(level);
    }
    value = properties.getProperty("defaultCatalog");
    if (value != null) {
      dataSource.setDefaultCatalog(value);
    }
    value = properties.getProperty("driverClassName");
    if (value != null) {
      dataSource.setDriverClassName(value);
    }
    value = properties.getProperty("maxActive");
    if (value != null) {
      dataSource.setMaxActive(Integer.parseInt(value));
    }
    value = properties.getProperty("maxIdle");
    if (value != null) {
      dataSource.setMaxIdle(Integer.parseInt(value));
    }
    value = properties.getProperty("minIdle");
    if (value != null) {
      dataSource.setMinIdle(Integer.parseInt(value));
    }
    value = properties.getProperty("initialSize");
    if (value != null) {
      dataSource.setInitialSize(Integer.parseInt(value));
    }
    value = properties.getProperty("maxWait");
    if (value != null) {
      dataSource.setMaxWait(Long.parseLong(value));
    }
    value = properties.getProperty("testOnBorrow");
    if (value != null) {
      dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("testOnReturn");
    if (value != null) {
      dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("timeBetweenEvictionRunsMillis");
    if (value != null) {
      dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value));
    }
    value = properties.getProperty("numTestsPerEvictionRun");
    if (value != null) {
      dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value));
    }
    value = properties.getProperty("minEvictableIdleTimeMillis");
    if (value != null) {
      dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value));
    }
    value = properties.getProperty("testWhileIdle");
    if (value != null) {
      dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("password");
    if (value != null) {
      dataSource.setPassword(decryptDBCPProperty(value));
    }
    value = properties.getProperty("url");
    if (value != null) {
      dataSource.setUrl(decryptDBCPProperty(value));
    }
    value = properties.getProperty("username");
    if (value != null) {
      dataSource.setUsername(decryptDBCPProperty(value));
    }
    value = properties.getProperty("validationQuery");
    if (value != null) {
      dataSource.setValidationQuery(value);
    }
    value = properties.getProperty("validationQueryTimeout");
    if (value != null) {
      dataSource.setValidationQueryTimeout(Integer.parseInt(value));
    }
    value = properties.getProperty("accessToUnderlyingConnectionAllowed");
    if (value != null) {
      dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("removeAbandoned");
    if (value != null) {
      dataSource.setRemoveAbandoned(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("removeAbandonedTimeout");
    if (value != null) {
      dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value));
    }
    value = properties.getProperty("logAbandoned");
    if (value != null) {
      dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("poolPreparedStatements");
    if (value != null) {
      dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue());
    }
    value = properties.getProperty("maxOpenPreparedStatements");
    if (value != null) {
      dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value));
    }
    value = properties.getProperty("initConnectionSqls");
    if (value != null)
    {
      StringTokenizer tokenizer = new StringTokenizer(value, ";");
      dataSource.setConnectionInitSqls(Collections.list(tokenizer));
    }
    value = properties.getProperty("connectionProperties");
    if (value != null)
    {
      Properties p = getProperties(value);
      Enumeration e = p.propertyNames();
      while (e.hasMoreElements())
      {
        String propertyName = (String)e.nextElement();
        dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName));
      }
    }
    if (dataSource.getInitialSize() > 0) {
      dataSource.getLogWriter();
    }
    return dataSource;
  }
  
  private static Properties getProperties(String propText)
    throws Exception
  {
    Properties p = new Properties();
    if (propText != null) {
      p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes()));
    }
    return p;
  }
  
  
  private static String decryptDBCPProperty(String encryptStr) throws UnsupportedEncodingException, NoSuchAlgorithmException, GeneralSecurityException {
	  
	  AES256Util aes = new AES256Util("testestestestses");

	  return aes.decrypt(encryptStr);

  }
}






위 소스에서 중요내용

username, password, url 에 해당하는 부분을 복호화하도록 변경



변경전 : dataSource.setPassword(value);
변경후 : dataSource.setPassword(decryptDBCPProperty(value));


눈치빠른 분들은 아시겠지만

server.xml 에서 입력된 username, password , url 을 암호화 하는 부분이었습니다.

( 위 3가지 정보 중 필요한 부분만 암호화 처리해도 됩니다. )


STEP 5

server.xml 에 암호화 된 내용으로 개인정보 변경






여기서 중요한 부분은 STEP 4 에서 만든 factory 를 지정하여

DBCP 할 때 사용할 수 있도록 합니다.

default 값은 BasicDataSourceFactory 로 별도로 지정해주지 않으면

tomcat-dbcp.jar 에 있는 내용을 사용하게 되니

반드시 우리가 만든 factory 를 설정해주시기 바랍니다.




여기까지 작업하면 해당 내용이 암호화 처리가 됩니다!


궁금한점이나 안되는 부분있으면 댓글 달아주세요^^


반응형
블로그 이미지

나남나여

일상 제품리뷰와 맛집/여행/사진을 좋아하고 IT 관련 프로그래밍 초급 & 고급 정보를 공유하는 블로그

,
반응형