본문 바로가기

Programming Language/Java & Scala

Java 시큐어 코딩 - 공격 종류 및 해결방안

1. SQL injection


취약점 발생 원인

외부입력값을 동적으로 SQL실행문을 만들어서 사용할때 주로 나타남.

/* SQL injection에 취약한 코드*/
String userId=request.getParameter("userId");
String password=request.getParameter("password");
...
Statement stmt = conn.createStatement();
ResultSet result =
   stmt.executeQuery("select count(*) as count from student where userid='"+userId+"' and password='"+password+"'");


userid에 admin' OR '1'='1 을 입력하면 그대로 아래와 같이 실행된다.

select count(*) from member where userid='admin' OR '1'='1' and password = 'a'


혹은 악의적으로 drop table, drop database도 가능하다.


해결방안

- 유저에게 받은 값을 그대로 넘기지 않는다.

- 클라이언트측에서 값을 입력받을 때 regex 등으로 검증하여 입력받는다.

- 서버측에서 클라이언트에서 넘어온 값을 regex 등으로 검증하여 입력받는다.


Spring boot JPA에서는?

ORM으로 주로 쓰이는 Spring boot JPA에서도 injection공격이 통할까?

stackOverFlow에 따르면 입력받은 값이 그 자체로 JPA에서는 value로 사용되기 때문에 injection이 불가하다고 한다.


Are SQL injection attacks possible in JPA?(답변 바로가기)


그러나 JPA native query는 어떨까? Native query를 사용하니 그대로 전달되지 않을까?

@Query(query="select count(*) as count from student where userid=:userId", nativeQuery = true) 
int findUser( @Param("userId") String userId);


userId value로 injection code를 심어서 수행한 결과를 debug log로 찍었다. 결과는 아래와 같다.

[main] hello.Application                        : InjectionTest:
[main] org.hibernate.SQL                        : select count(*) as count from student where userid = ?
Hibernate: select count(*) as count from student where userid  = ?
[main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [admin' OR '1'='1]


내부적으로 PreparedStatement 처럼 동작하여 injection으로 동작하는 것이 아닌 value로 값이 들어가게 되어 안전한 상태임을 확인 할 수 있다.


PreparedStatement를 사용하는 경우 파라미터로 들어가는 값을 바인딩하여 사용한다. 바인딩데이터는 SQL문법이 아닌 컴파일언어로 처리하기 때문에 문법적 의미를 가질 수 없으므로, 바인딩 변수에 SQL injection query를 넣더라도 의미있는 쿼리로 동작하지 않는다.



2. 인증 탈취

인터넷 서비스에서 인증(Authentication)과 권한(Authorization) 관리를 철저히 해야하는건 당연지사이다. 인증이나 세션 관리와 관련된 애플리케이션의 기능이 올바르게 구현되지 않은 경우 공격자는 쉽게 다른 사용자로 가장할 수 있다.


취약점 발생원인

- 세션 ID 추측 : 추측가능한 ID를 무작위 대입하는 경우, 리버스 엔지니어링이 쉬운 알고리즘으로 생성된 세션ID값의 경우

- 세션 ID 훔치기 : XSS 취약점을 가진 사이트에 올려진 게시물을 클릭할 경우 자바스크립트를 통해 세션정보를 공격자에게 전달

- 세션 ID 고정 : 로그인 후에 저장된 동일 세션을 복사하여 사용

- 세션 관리 정책 미비 : 유요기간이 잘 관리되지 않은 세션ID의 경우 쿠키 스니핑, 프록시 서버의 로그취득을 통해 공격자에게 정보 전달


해결방안

- 브라우저의 XSS필터 정책을 활성화하여 세션정보 탈취 방어

- 서버 설정을 통한 세션 ID쿠키값을 httponly로 설정하여 스크립트에서 읽지 못하도록 방어

- 세션ID가 url에 포함되지 않도록(노출되지 않도록)


Spring security ACL(Access Control List)을 사용한 인증체크

웹 서비스에 있어서 user별 권한 체크는 필수불가결하다. 이 때 Spring security에서 제공하는 기술 중 인증, ROLE기반 접근제어 방식인 ACL을 활용하면 더욱 효과적으로 적용 가능하다.


먼저 필요한 정의는 롤(ROLE)이다. 사용자에게 롤을 정의하고 어떤 메소드(Where)를 어떻게(What)할 것인지 까지 정의 가능하다.

// Role define example
// QnA 게시판은 누구나(any) 읽기(read), 쓰기(write) 가능
// 자료실 게시판은 누구나(any) 일기(read) 가능. 관리자(admin)만 쓰기(write) 가능


Spring security의 acl사용을 위해 아래와 같은 라이브러리 추가가 필요하다.

<dependency>
    <groupid>org.springframework.security</groupid>
    <artifactid>spring-security-core</artifactid>
    <version>4.1.3.RELEASE</version>
    <scope>compile</scope>
</dependency>  
<dependency>
    <groupid>org.springframework.security</groupid>
    <artifactid>spring-security-web</artifactid>
    <version>4.1.3.RELEASE</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupid>org.springframework.security</groupid>
    <artifactid>spring-security-config</artifactid>
    <version>4.1.3.RELEASE</version>
</dependency>
<dependency>
    <groupid>org.springframework.security</groupid>
    <artifactid>spring-security-acl</artifactid>
    <version>4.1.3.RELEASE</version>
    <scope>test</scope>  
</dependency>


스크립트를 통해 아래와 같은 table이 생성된다.


# ACL_SID : 롤, 사용자에 대한 키 정보가 저장

# ACL_CLASS : 도메인 객체 클래스에 대한 정보 저장

# ACL_OBJECT_IDENTITY : 도메인 객체 인스턴스 정보 저장

# ACL_ENTRY : 접근 권한 데이터 저장 테이블


기타 ACL을 위한 configuration을 선언하고 나면 아래와 같이 접근에 대한 정의를 method별로 선언 가능하다.

@PostFilter("hasPermission(filterObject, 'READ')")
List<NoticeMessage> findAll();
     
@PostAuthorize("hasPermission(returnObject, 'READ')")
NoticeMessage findById(Integer id);
     
@PreAuthorize("hasPermission(#noticeMessage, 'WRITE')")
NoticeMessage save(@Param("noticeMessage")NoticeMessage noticeMessage);


자세한 내용은 Introduction to Spring Security ACL(바로가기)에서 확인 가능




3. XSS(크로스 사이트 스크립팅)

외부 입력값이 충분한 검증 없이 동적으로 생성되는 응답 페이지에 사용 되는 경우 해당 페이지를 사용하는 다른 클라이언트들이 공격자로 부터 공격 받을 수 있다.


XSS 취약점은 공격 유형에 따라 Reflective XSS, Stored XSS, DOM XSS로 구분가능하다.


취약점 발생 원인

- Reflective XSS : 공격자가 악성 스크립트가 포함된 URL을 클라이언트에 노출시키게 하여 클릭을 유도하여 정보탈취, 피싱사이트 리다이렉트 등의 공격을 함.

- Stored XSS : 악성 스크립트를 DB에 저장해 해당 DB정보를 이용하는 애플리케이션을 통해 시스템을 사용하는 모든 사용자들이 해당 스크립트를 실행하게함.

- DOM XSS : AJAX프로그램에서 사용되는 자바스크립트를 이용해 브라우저에 수신된 데이터를 다시 잘라내서 Document에 write하는 작업을 수행할 경우 XSS 공격이 가능하게 한다.


해결방안

- 입/출력 값에 대해 필터링 적용

  - Black list 방식 : 간단함. 모든 방어에 대해 준비가 불가능함.

  - White list 방식 : 지속적으로 list를 추가해줘야함. 강력함.

  - 오픈소스 라이브러리 사용 : 네이버 개발자 그룹에서 개발한 자바기반의 lucy-xss Filter(바로가기)을 사용하여 Spring boot MVC의 Controller 컴포넌트에 적용 가능. 화이트리스트 정책 사용



4. 파일 업/다운로드

취약점 발생 원인

웹서버에 업로드 된 파일에 악성코드가 포함된 경우. 웹서버에 암호화되지 않은 정보(주민등록증, 여권 등)가 웹서버에 저장되는 경우 공격자가 파일 경로를 조작하여 확인절차 없이 다운로드를 허가될 수도 있다.


해결방안

- 파일을 굳이 웹서버에 올려야하는지 정책을 다시 확립한다.

- 반드시 업로드 해야 한다면 업로드되는 파일의 타입을 제한하고, 외부에서 직접 접근 가능하지 않은 경로에 파일을 저장한다.

- 업로드되는 파일 개수, 크기에 대해 제약한다.


Object storage에서는?

<기존 파일시스템과 object storage를 잘 비교한 그림>


Public cloud 서비스에서 주로 쓰이는 object storage는 안전할까? 


Object storage로는 유명한 aws의 s3(Simple Storage Service), ibm의 COS(Cloud Object Storage)가 있다. Instance(서버)를 scalable하게 사용하는 cloud public 특성상 서버에 데이터를 저장하는 경우에는 file system에 의존하기 힘든 경우가 생기는데, 이런 경우 파일 저장소로 object storage를 많이 쓴다. 


Object storage도 보안에 취약하긴 마찬가지이다. 권한과 접근에 대해 정확한 인지 없이 사용하게 된다면 공격자에게 공격당할 수 있다. 


대부분의 사례는 버킷 단위로 public하게 객체 권한을 열어주는데서 생기는데 이와 같은 경우 공격자는 public으로 설정된 모든 파일(object)들에 접근가능하다. 그러므로 각 public cloud별 object storage에서 제공하는 권한방법에 대해서 확인하고 서비스에 맞는 정책을 세워 불필요한 데이터는 외부로 노출되지 않게 하는 것이 최고의 방어 방법이다.



End of Document.