'프로그래밍/시큐어코딩' 카테고리의 글 목록 (4 Page) :: 잡다한 프로그래밍
반응형

1. CSRF의 정의

특정 웹 사이트에 대해 사용자가 인지하지 못한 상황에서 사용자의 의도와 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등) 을 요청하게 하는 공격을 말한다. 사용자로부터 받은 요청에 대해 사용자가 의도한대로 작성되고 전송된 것인지 확인하지 않을때 발생하며, 관리자인경우 관리자 권한의 게시물삭제 사용자 관리 등 임의로 공격이 가능하다

 

2. 안전한 코딩기법

입력화면 form 작성 시 Get 방식을 사용하면 URL에 노출이 되므로 POST방식을 사용하여 해당 입력을 처리하도록한다.

 

3. 예시

 

4. 추가적인 방법

CSRF Token이용하기

CSRF 토큰을 주고받으므로서 요청이 유효한지 확인할 수 있다. 예를들어 게시판 글 쓰기 페이지를 호출할 때 token을 서버에서 브라우저에게 전달하고, 게시판 글쓰기 요청이 왔을 때 토큰이 없다면 유효한 요청이 아니라고 판단하는 것 이다.

이는 Spring security를 사용하면 csrf토큰 내용이 구현되어있다. 유저 form에서 다음과 같이 csrf input을 추가해주면 된다.

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> 

 

CAPTCHA

다음과 같이 추가적인 인증을 하는 CAPTCHA를 이용하여 CSRF에 대비할 수 있다.

 

추가로 비밀번호 요구하기

예를들어 사용자의 회원탈퇴, 정보수정같은 중요한 업무는 비밀번호를 한번 더 요청하는것으로 CSRF에 대비할 수 있다.

반응형
반응형

1. 신뢰되지 않는 URL 주소로 자동연결의 정의

사용자로부터 입력되는 값을 외부사이트의 주소로 사용하여 자동으로 redirect 하는 경우 피싱 공격에 노출되는 취약점을 가질 수 있다.

 

2. 안전한 코딩기법

자동 연결할 외부 사이트의 URL과 도메인은 화이트 리스트로 관리하고, 사용자의 입력값을 통해 주소로 리다이렉트 시키는경우 입력값이 존재하는지 여부를 확인한다.

 

3. 예시

다음과 같은 코드가 존재할 경우에 사용자는 이 링크를 클릭함으로서 피싱사이트로 접근된다.

<a href = "http://bank.example.com/redirect?url=attacker.example.net">Click</a>

이는 이동할 수 있는 리스트를 사전에 정의해놓고 그 리스트에 포함되어있지 않으면 이동시키지 않는 방법이다.

반응형
반응형

1. 위험한 형식 파일 업로드 정의

서버측에서 실행될 수 있는 스크립트파일(asp, jsp, php 파일 등)이 업로드 가능하고, 이 파일을 공격자가 웹을 통해 직접 실행 시킬 수 있는 경우 공격자는 스크립트 파일을 업로드하고, 이 파일을 통해 시스템 내부 명령어를 실행하거나 외부와 연결하여 시스템을 제어할 수 있다.

예를들어 다음과 같은 게시판에 악성 파일을 업로드하고 서버내에서 실행시키면 서버에 영향을 줄 수 있다.

2. 안전한 코딩기법

  • 업로드하는 파일 타입과 크기를 제한하고, 업로드 디렉터리를 웹서버와 분리하여 설정한다.
  • 허용된 확장자만 업로드되도록하고, 확장자도 대소문자 구분 없이 처리하도록 한다.
  • 공격자의 웹을 통한 직접 접근을 차단한다. 또한 파일 실행 여부를 설정할 수 있는 경우, 실행 속성을 제거한다.

3. 예시

사용자가 업로드하는 파일의 유효성을 검사하지 않으면, 위험한 유형의 파일을 공격자가 업로드하거나 전송할 수 있다.

public void upload(HttpServletRequest request) throws ServletException {
    MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest) request;
    String next = (String) mrequest.getFileNames().next();
        MultipartFile file =  mrequest.getFile(next);
    // MultipartFile로부터 file을 얻음
    String filename = file.getOriginalFilename();
    // upload 파일에 대한 확장자 체크하지 않음
    File uploadDir = new File("/app/webapp/data/upload/notice");
        String uploadFilePath = uploadDir.getAbsolutePath()+"/" + fileName;
}

 

다음은 업로드 파일의 크기를 제한하고, doc, hwp, pdf, xls 확장자만 저장을 허용하며, 업로드 파일의 디렉터리 위치를 다큐먼트 루트의 밖에 위치시키는 방법이다.

public void upload(HttpServletRequest request) throws ServletException {
    MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest) request;
    String next = (String) mrequest.getFileNames().next();
        MultipartFile file =  mrequest.getFile(next);
    if(file == null)
      return;
      
    //업로드 파일 크기를 제한한다
    int size = file.getSize();
    if(size > MAX_FILE_SIZE) throw new ServletException("에러");
    
    // MultipartFile로 부터 file을 얻음
    String fileName = file.getOriginalFilename().toLowerCase();
    
    // 업로드 파일의 확장자를 체크한다.
    if(fileName != null)
    {
    	if(fileName.endsWith(".doc") || fileName.endsWith(".hwp") 
        || fileName.endsWith(".pdf") || fileName.endsWith(".wls"))
        {
        	파일업로드함
        }
        else throw new ServletException("에러");
    }
    
    //업로드 파일의 디렉터리 위치는 다큐먼트 루트의 밖에 위치시킨다.
    File uploadDir = new File("/app/webapp/data/upload/notice");
    String uploadFilePath = uploadDir.getAbsolutePath()+"/" + fileName;
}

 

4. 추가적인방법

  • 웹 서버 내부 디렉토리에 실행권한을 주지 않는다. 즉 서버 내부에 파일을 실행할 수 없게하여 공격으로부터 대비한다
  • 파일이름 및 확장자를 암호화(난수화) 하여 저장한다. DB에 암호화된 파일명, 원래의 파일명, 파일크기, 확장자 등 정보를 저장한다면 사용자에게 원래의 파일명으로 화면에 뿌려주되 파일명은 암호회되어있어서 사용자는 직접적으로 접근할 수 없다. DB에 원래 파일명을 적어놓으므로 복호화 과정이 필요없어서 해쉬를 사용해도 괜찮을 것 같음(추측)
반응형
반응형

0. 개요

Naming공부를 하다 Enum이 무엇인지 몰라서 정리해보는 계기가 되었다.

1. 정의

상수를 정의할 때 final static string 과 같은 방식으로 상수를 정의를한다. 하지만 이렇게 상수를 정의해서 코딩하는 경우 여러 문제가 발생함. 

따라서 이러한 문제점들을 보완하기 위해 자바 1.5버전부터 추가된 것이 바로 Enum

Enum은 열거형이라고 불리며, 서로 연관된 상수들의 집합을 의미한다.

 

2. Enum 이전의 방법들

#1) 상수 선언하여 사용하기

public class EnumExample {

    private final static int MONDAY = 1;
    private final static int TUESDAY = 2;
    private final static int WEDNESDAY = 3;
    private final static int THURSDAY = 4;
    private final static int FRIDAY = 5;
    private final static int SATURDAY = 6;
    private final static int SUNDAY = 7;

    public static void main(String[] args) {        

        int day = MONDAY;

        switch (day) {
        case MONDAY:
            System.out.println("월요일 입니다.");
            break;
        case TUESDAY:
            System.out.println("화요일 입니다.");
            break;
        case WEDNESDAY:
            System.out.println("수요일 입니다.");
            break;

            /*
             * ... 생략 ...
             */
        }
    }
}

다음과 같은 코드는 만약 Month에 대한 상수가 추가된다면 상수가 너무 많아지고, 만약 같은 이름으로 정의된 상수가 있다면 중복된 이름이라 오류가 발생한다.

 

#2) interface를 이용한 방법

interface DAY{  
    int MONDAY = 1;
    int TUESDAY = 2;
    int WEDNESDAY = 3;
    int THURSDAY = 4;
    int FRIDAY = 5;
    int SATURDAY = 6;
    int SUNDAY = 7;
}

interface MONTH{  
    int JANUARY = 1;
    int FEBRUARY = 2;
    int MARCH = 3;
    int APRIL = 4;
    int MAY =5;
    int JUNE = 6;
    int JULY = 7;
    int AUGUST = 8;
    int SEPTEMBER = 9;
    int OCTOBER = 10;
    int NOVEMBER = 11;
    int DECEMBER = 12;
}

public class EnumExample {

    public static void main(String[] args) {

        if(DAY.MONDAY == MONTH.JANUARY){ //이부분에러
            System.out.println("두 상수는 같습니다.");
        }       

        int day = DAY.MONDAY;

        switch (day) {
        case DAY.MONDAY:
            System.out.println("월요일 입니다.");
            break;
        case DAY.TUESDAY:
            System.out.println("화요일 입니다.");
            break;
        case DAY.WEDNESDAY:
            System.out.println("수요일 입니다.");
            break;

            /*
             * ... 생략 ...
             */
        }
    }
}

다음과 같이 인터페이스를 사용하게되면 이름이 같은 상수도 정의하여 사용할 수 있지만, if문을 보면 Day 의 Monday와 Month의 January부분이 같다고 인식하게되는 에러가 발생한다.

 

3. Enum의 사용이유

앞선 문제를 Enum을 사용하면 다음과 같이 해결할 수 있다. Enum을 사용하게 되면 static final의 반복이 사라져 가독성이 좋고 앞선 if문의 문제를 해결할 수 있다.

enum Day{  
    MONDAY,TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

enum Month{  
    JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, 
    AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER;
}

public class EnumExample {

    public static void main(String[] args) {        

        Day day = Day.MONDAY;

        switch (day) {
        case MONDAY:
            System.out.println("월요일입니다.");
            break;
        case TUESDAY:
            System.out.println("화요일입니다.");
            break;
        case WEDNESDAY:
            System.out.println("수요일입니다.");
            break;

            /*
             * ... 생략 ...
             */
        }
    }
}

 

 

또한 다음과같이 이용할 수 있다.

enum Type {
    WALKING, RUNNING, TRACKING, HIKING
}
public class Shoes {
    public String name;
    public int size;
    public Type type;
     
    public static void main(String[] qrgs) {
        Shoes shoes = new Shoes();
         
        shoes.name = "아디다스";
        shoes.size = 270;
        shoes.type = Type.RUNNING;
         
        System.out.plintln("신발 이름 = " + shoes.name);
        System.out.plintln("신발 사이즈 = " + shoes.size);
        System.out.plintln("신발 종류 = " + shoes.type);
    }
}
반응형
반응형

1. 운영체제 명령어 삽입의 정의

다음 그림과 같이 적절한 검증절차를 거치지 않은 사용자 입력값이 운영체제 명령어의 일부 또는 전부로 구성되어 실행될 경우, 의도하지않은 시스템 명령어가 실행되어 부적절하게 권한이 변경되거나 시스템 동작 및 운영에 악영향을 미칠 수 있다.

 

2. 안전한 방법

웹 인터페이스를 통해 서버내부로 시스템 명령어를 전달시키지 않도록 구성하고, 외부에서 전달되는 값을 그대로 시스템 내부 명령어로 사용하지 않는다. 또는 외부입력에 따라 정해진 명령어를 미리 생성하여 선택하여 사용하게한다.

 

3. 예시

사용자의 입력을 검증하지않고 그대로 사용하여 cmd.exe를 사용한 rmanDB.bat 배치 명령어가 수행되는 경우 (배치파일이란 여러명령어를 한번에 실행하기위한 파일을 의미)

다음은 사용자의 입력을 통해 미리 만들어놓은 인자값을 선택하게 만든방법이다.

반응형
반응형

1. 크로스 사이트 스크립트란?

페이지에 악의적인 스크립트를 포함시켜 사용자 측에서 실행되게 유도할 수 있다. 다음 그림과 같이 악성 스크립트를 서버측에 저장하면 다른사용자는 악성스크립트가 포함된 페이지를 응답받고 정보유출이 될 수 있다.

2. 안전한 코딩기법

일반적으로 사용자가 문자열에 스크립트를 삽입하여 실행하는 것을 막기 위해 사용자가 입력한 문자열에서 <, >, &, " 같은 문자열을 replace같은 메소드를 사용하여 치환하거나, 보안성이 검증되어 있는 API를 사용하여 위험한 문자열을 제거한다.

 

3. 예시

다음은 외부 입력값을 name으로, 특별한 처리과정없이 결과 페이지 생성에 사용하고 있다. 만약 다음과 같은 악의적인 스크립트(<script> url = "http://devil.com/attack.jsp;</script> 를 공격자가 넣으면, attack.jsp가 수행되며, 희생자의 쿠키정보 유출 등의 피해를 주게 된다.

 

따라서 다음과 같이 replaceAll()메소드를 사용하여 <, >같은 스크립트 생성에 사용되는 문자열을 &lt; &gt; 같은 형태로 변경함으로써 스크립트 수행위험을 줄일 수 있다.

 

 

다음 예제의 경우 OSWASP에서 제공하는 보안 API를 사용하고 있다. 보안 API를 사용하면 특수 문자를 이용한 공격 스크립트와 같은 외부 입력 문자열을 이용한 공격을 차단할 수 있다.


4. 추가적인 방법

앞선 부분은 pdf에서 제공하는 부분으로 API와 replace를 이용한 방법은 예전 방식의 느낌이 나서 찾아보았다.

 

다음은 EL <%= %>표현이 아닌 JSTL의 c:out을 활용한 방법이다. c:out을 당연하게 사용해 왔지만 c:out이 XSS를 대비한 방법이라는것을 알았다.

 

c:out은 기본적으로 escapeXml이 true로 되어 있기 때문에  < > & ' " 문자들을 각각 &lt; &gt; &amp; &#039; &#034; 로 출력한다. 이러한 방법은 앞서 replace를 사용한 방법의 상위 방법으로, 따라서 악성 스크립트가 실행되는것을 방지한다.

<%
 String greeting = "<b>hello, world!</b>";
 request.setAttribute("greeting", greeting);
%>
<h2>escapeXml false</h2>
<c:out escapeXml="false" value="${greeting}" />
<h2>escapeXml true</h2>
<c:out escapeXml="true" value="${greeting}" />
<h2>default</h2>
<c:out value="${greeting}" />

이는 다음과 같이 false가 되어있는 부분은 태그를 인식하여 출력하고, true로 되어있는 부분은 String 그대로 출력하는것을 확인할 수 있다.

 

hello, world!

<b>hello, world!</b>

 

Vue에서의 XSS방지

vue의 기본적으로 XSS를 방지하기위해 이를 자동으로 이스케이프 한다. 즉 기본적으로 XSS에 대한 방지를 지원한다.

userProvidedString = '<script>alert("hi")</script>'
<h1>{{ userProvidedString }}</h1>

따라서 이는 다음과 같이 출력될것이다.

&lt;script&gt;alert(&quot;hi&quot;)&lt;/script&gt;

만약 html이 내부에 들어가야한다면 다음과 같이 이용할 수 있다.

<div v-html="userProvidedHtml"></div>

 

lucy-xss-servlet-filter적용

https://github.com/naver/lucy-xss-servlet-filter

 

naver/lucy-xss-servlet-filter

Contribute to naver/lucy-xss-servlet-filter development by creating an account on GitHub.

github.com

참고

반응형
반응형

1. 자원삽입이란?

외부 입력값을 검증하지 않고 시스템 자원에 대한 식별자로 사용하는 경우, 입력값 조작을 통해 시스템이 보호하는 자원에 임의로 접근하거나 수정할 수 있는 보안적 이슈

 

2. 안전한 코딩기법

외부의 입력 자원을 식별자로 사용할 경우, 적절한 필터링 및 검증을 거치거나, 사전에 정의한 리스트에서 선택하도록 만든다.

 

3. 예시

사용자가 입력 소켓 번호로 -2920값을 입력할 경우 80이 되어 포트 충돌이 일어나는 상황

...

String service = props.getProperty("Service No");
int port = Integer.parseInt(service);

if(port != 0)
	serverSocket = new ServerSocket(port + 3000);
else
	serverSocket = new ServerSocket(def + 3000);

 

다음과 같이 미리 리스트를 정해주고 사용자는 입력값을 번호로 하여 내부적으로 할당하도록 처리한다.

..

if(in != null && in.available() > 0){
	props.load(in);
    service = props.getProperty("Service No");
}

if("".equals(service)) service = "8080"

int port = Integer.parseInt(service);

switch(port)
{
	case 1:
    	port = 3001; break;
    case 2:
    	port = 3002; break;
    default:
    	port = 3000;
}

serverSocket = new ServerSocket(port);
반응형
반응형

1. Statement / preparedStatement란

#1) PreparedStatement의 동작방식

 

preparedStatement는 다음과 같이 4가지 단계로 이루어져있다. 이중 parse 부분을 컴파일한채로 캐시에 저장하고 데이터가 bind될때까지 기다린다 이후 patch부분까지 실행이 완료되고 sql을 재사용할때 parse를 다시 실행하지않고 캐시에 저장되어있는 부분을 가져다 나머지 3단계만 실행한다. 따라서 sql이 반복적일때 효율적이다.

 

또한 바인딩 된 데이터는 SQL문법이 아닌 내부의 인터프리터나 컴파일 언어로 처리하므로, 문법적인 의미를 가질 수 없다. 이것이 의미하는것은 parse부분에서 이미 쿼리의 문법적인 처리부분이 선 수행되었으므로 바인딩 단계에서 입력된 부분은 쿼리로 인식하지 않는다는 의미이다. 따라서 sql injection에 좋다.

 

사용 방법

String sql = "SELECT NAME, AGE FROM TABLE WHERE userID = ?"
PreparedStatement stmt = conn.prepareStatement(sql);
pstmt.setInt(1, userID);
ResultSet rst = pstmt.executeQuery();

 

#2) Statement의 동작방식

Statement는 prepare방식과 달리 4가지단계를 매번 실행하여 결과 값을 반환한다. 만약 반복적으로 쿼리를 수행할경우 PreparedStatement가 DB에 적은 부하를 주며, 성능이 더 좋다.

하지만 sql을 전체를 한눈에 볼 수 있다는 장점이 있다.

String sql = "SELECT NAME, AGE FROM TABLE WHERE USERID = " + userID
Statement stmt = conn.credateStatment();
ResultSet result = stmt.executeQuery(sqlstr);

 

2. SQL 삽입

사용자가 입력 폼 및 URL 입력란에 SQL을 삽입하여 DB로부터 정보를 열람하거나 조작할 수 있는 보안적 약점을 의미한다.

이를 대비하기위해 PreparedStatement를 이용하는것이 바람직하다 예를들어 다음과 같은 statement 쿼리문을 작성했다고 가정할때 사용자가 name값을 name' OR 'a' = 'a라고 입력할경우 모든 테이블이 조회가 되는 문제가 생긴다.

String query = "SELECT * FROM " + tableName + "WHERE Name = " + name;

따라서 이를 PreparedStatement(데이터 바인딩) 으로 처리하면 안전하게 SQL 삽입으로부터 대비할 수 있다. 보다 안전한 이유는 PreparedStatement를 보면 알 수 있다.

String query = "SELECT * FROM ? WHERE Name = ?";
stmt = con.prepareStatement(query);
stmt.setString(1, tableName);
stmt.setString(2, name);
rs = stmt.executeQuery();
반응형

+ Recent posts