스프링 MVC Test와 Spock 같이 쓰기

시간이 여유치 않아서 길게 포스팅하기는 힘들고..

Spock은 이번 스프링원에서 알게된 테스트 프레임워크인데, 이 프레임워크 관련 세션이 세개나 있었다.

  • Next Level Spock
  • Testing Controllers with Spring MVC Test and Spock
  • Spock Friendly Testing

https://code.google.com/p/spock/wiki/SpockBasics

스팍은 스타트랙에 나오는 캐릭터인데… 멋진 캐릭터이다. 마치 우리팀에 있는 응준대리님같은 캐릭터인데… 자세한건 생략…

스프링 MVC Test는 그동안 많이 떠들었으니까 역시 생략..

둘이 합쳐서 간단한 테스트를 만들어 보면 이렇게 만들 수 있다.

조금 더 복잡하지만 실용적인 테스트는 보통 이런식이 될거다.

[ElasticSearch] 개념 정리

몇가지 용어만 알면 될것 같은데.. 하두 흔히 사용하는 용어를 사용해서 햇갈린다.

http://www.elasticsearch.org/guide/reference/glossary/#id

일단, 데이터베이스에 해당하는 용어가 index. 인덱스는 최소 한개 또는 그 이상의 primary shard에 대응하는 논리적인 이름이다.

테이블에 해당하는게 type.

type에 들어가는 한 데이터(row)는 document. document는 index에 저장되고 type과 id가 있다. 한 document에는 여러 field나 키-값 쌍이 들어있다.

type을 정의하는 스키마에 해당하는게 mapping.

테이블의 컬럼에 해당하는게 filed.

원본 JSON 담고 있는 필드가 source filed로 _source라는 필드가 있다.

document 식별자가 id.

============

text를 analysis해서 term으로 인덱스 시킨다.

text는 글 덩어리?

analysis는 전체 글을 term으로 바꾸는 과정이고 엘라스틱서치는 term을 index시킨다. 풀 텍스트 검색도 결국은 검색어를 term으로 바꿔서 term 검색에 대응되는 녀석 찾아주는 식으로 처리된다.

============

cluster는 동일한 클러스터 이름을 가진 여러 node로 구성된다. 클러스터에는 클러스터가 자동으로 지정한 마스터 노드가 한개 있으며, 마스터 노드가 죽으면 다른걸로 바뀔 수 있다.

node는 클러스터에 속한 실행중인 엘라스틱서치 인스턴스다. 보통 서버당 하나만 사용하지만 테스트용으로 여러개 써보는거야 뭐.. 노드를 실행할 때 클러스터 이름으로 기존 클러스터를 찾아서 그안에 들어갈 수 있는데 이때 unicast나 multicast를 사용할 수 있다.

document를 저장하는 곳이 primary shard인데, primary shard에 먼저 document를 인덱스 시킨다음에 해당 primary shard의 모든 replica shard로 복사한다. 기본값으로 한 index마다 5개의 primary shard를 가지는데, 설정으로 변경 가능. 인덱스를 일단 만들고나면 primary shard 개수를 변경할 수 없다.

routing은 index에 저장할 document를 실제 어떤 primary shard에 저장할지 정하는 과정인데 document의 id값을 해싱한 routing 값을 사용하거나, document가 parent document를 가지고 있다면 해당 parent의 id값을 사용해서 부모랑 자식이 같은 샤드에 들어가게 한다. routing 값 오버라이딩 하거나 mapping할 때 설정하는 방법은 나중에.

replica shard는 두가지 목적으로 사용하는데, 하나는 페일오버로 primary shard가 죽을 때 replica shard를 primary shard로 승격시켜서 사용하기 위함이고 두번째는 성능으로 get이나 search를 primary shard와 replica shard로 처리해서 성능 향상. 기본값으로 primary shard당 한개의 replica shard를 가지고 기존 index에 동적으로 replica shard를 추가할 수 있다. primary shard와 같은 노드에서 시작하지 않는게 좋다.

shard 한개는 루씬 인스턴스 한개다. 엘라스틱서치가 관리하는 저수준의 “worker”이고, 여러 primary 샤드와 replaca 샤드 묶음을 논리적인 이름으로 나타내는게 index다. 샤드 개수 설정할 때 빼고 코딩할 때는 샤드를 직접 지칭할 일은 없고 index를 사용할 것이다. 엘라스틱서치가 클러스터의 여러 노드로 샤드를 분산시킨다. 샤드는 노드가 죽거나 새 노드가 추가될 때 동적으로 다른 노드로 옮겨질 수 있다.

=============

아 놔 이제야 좀 정리가 되네..

그러니까.. 노드는 엘라스틱서치 인스턴스고, 그 안에 인덱스 만들면 샤드라는 루씬 인스턴스가 생기는데 기본값으로 인덱스마다 primary 샤드 5개랑 각 primary 샤드당 replica 샤드가 1개씩 생기니까 기본으로 인덱스 하나 마다 10개의 루씬 인스턴스가 뜨는거네..

흠.. primary 샤드 개수는 어느 정도가 적당한거지?

 

 

스프링 시큐리티 3.2.0.RC1 하이라이트: CSRF 방어

오래만에 공부좀 해볼까

이 글은 다음 원글을 참고, 번역, 편역, 요약한 글이오니 보다 정확한 정보 습득을 원하시는 분께서는 원문을 참고하시기 바랍니다: http://blog.springsource.org/2013/08/21/spring-security-3-2-0-rc1-highlights-csrf-protection/

스프링 시큐리티가 지난 월요일 3.2.0.RC1으로 버전이 올라갔는데 이번 글에서는 그 기능 중에 CSRF 지원 기능을 살펴보고자 한다. 다음글에서는 이번에 추가한 다양한 시큐리티 헤더를 살펴보겠다.

CSRF 공격

스프링 시큐리티가 CSRF 공격에 대한 방어책을 마련했다는데 CSRF 공격이 뭐고 그걸 어떻게 방어하겠다는걸까? 예제를 사용해 이해해보자.

여러분의 은행 웹 사이트가 현재 로그인한 사용자가 다른 은행으로 돈을 보낼 수 있는 폼을 제공한다고 가정해보자. 예를 들어 그 HTTP 요청은 다음과 같이 생겼다고 하자.

“`

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876

“`

이제 여러분이 은행 웹사이트에 인증을 하고나서 로그아웃하지 않고 다른 나쁜 웹사이트에 접속한다고 가정해보자. 그 나쁜 웹사이트는 다음과 같은 폼을 가진 HTML 페이지를 제공한다.

“`
<form action=”https://bank.example.com/transfer” method=”post”>
<input type=”hidden”
name=”amount”
value=”100.00″/>
<input type=”hidden”
name=”routingNumber”
value=”evilsRoutingNumber”/>
<input type=”hidden”
name=”account”
value=”evilsAccountNumber”/>
<input type=”submit”
value=”Win Money!’/>
</form>

“`

Win Money!를 하고 싶어서 서브밋 버튼을 클릭한 순간, 전혀 의도하지 않았던 100달러를 이상한 사람에게 보내게된다. 나쁜 사이트가 여러분의 쿠키를 보지 않더라도, 여러분 은행과 관련있는 쿠키가 요청을 따라 보내지기 때문에 이런 일이 발생한다.

더 안 좋은건 이 모든 절차를 자바스크립트로 자동화해서 실행할 수 있다는 것이다. 즉, 버튼을 클릭하지 않아도 이런 일이 생길 수 있다는 것이다. 그래서 이걸 어떻게 막았냐고?

SYNCHRONIZER TOKEN 패턴

은행 웹사이트의 HTTP 요청 폼과 나쁜 사이트의 요청 폼이 정확히 일치한다는 것이 문제다. 즉 나쁜 사이트에서 보내는 요청은 막으면서 은행에서 온 요청만 받을 방법은 없다는 뜻이다. CSRF 공격을 방어하려면 나쁜 사이트는 주지 못할 무언가가 요청 안에 들어있다는 것을 확인할 수 있어야 한다.

그런 대안 중 하나로 Synchronizer Token 패턴을 사용하는 방법이 있다. 이 방법은 모든 요청에 세션 쿠키와 더불어 랜덤하게 생성되는 토큰을 HTTP 파라메터로 제공하는 것이다. 요청이 오면, 서버는 반드시 그 토큰에 해당하는 값을 가져와서 요청에 있는 실제 값과 비교한다. 값이 맞지 않으면 그 요청은 실패 처리한다.

상태를 변경하는 HTTP 요청에서만 토큰을 사용하도록하여 이런 기대감을 조금은 완화할 수 있겠다. same origin policy로 인해 나쁜 사이트가 응답을 읽어가진 못할테니 그렇게 해도 비교적 안전하다. (즉, READ는 same origin policy로 막으니까 UPDATE만 sychronized token 패턴으로 막아도 된단 소리) 게다가, 랜덤 토큰을 HTTP GET에 넣는건 토큰이 누출될 가능성도 있다.

예제가 어떻게 바뀌는지 살펴보자. 랜덤하게 생성된 토큰을 _csrf라는 HTTP 파라메터로 넣는다고 가정하자. 가령, 송금 요청은 다음과 같다.

“`

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded

amount=100.00&routingNumber=1234&account=9876&_csrf=<secure-random>

“`

임의의 값을 가진 _csrf 파라메터를 추가한걸 볼 수 있다. 이제 나쁜 사이트는 _csrf 파라메터에 적절한 값을 추측할 수 없으니 서버가 실제 토큰과 기대한 토큰을 비교해보고 송금 요청은 실패하게 된다.

스프링 시큐리티 CSRF 기능 사용하기

그래서 스프링 시큐리티로 웹 사이트를 CSRF 공격으로부터 방어하려면 뭘 해야하냐? 다음 단계를 통해 스프링 시큐리티 CSRF 방어를 사용할 수 있다.

  • 적절한 HTTP 동사 사용하기
  • CSRF 방어 설정하기
  • CSRF 토큰 추가하기

적절한 HTTP 동사 사용하기

CSRF 공격을 방어하는 첫걸음은 웹사이트가 적절한 HTTP 동사를 사용하고 있는지 확인하는 것이다. 구체적으로, 스프링 시큐리티 CSRF 기능을 사용하기 전에, 상태를 변경하는 요청일때 PATCH, POST, PUT, DELETE를 적절히 사용하고 있는지 확인해야한다. 이것은 스프링 시큐리티의 제약사항이 아니라 적절한 CSRF 방어에 기본으로 필요한 것이다.

CSRF 방어 설정하기

다음 단계는 스프링 시큐리티의 CSRF 방어를 애플리케이션에 추가하는 것이다.  XML 설정을 사용하고 있다면, <csrf/> 엘리먼트를 사용하면 된다.

“`

<http …>

<csrf />
</http>

“`

CSRF 방어는 자바 설정에서 기본으로 사용하게된다. 궁금해 하실 분들을 위해 보여주자면, 자바 설정은 다음과 같이 생겼다.

“`

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.and()
…;
}
}

“`

CSRF 토큰 추가하기

마지막 단계는 CSRF 토큰을 PATCH, POST, PUT, DELETE 메서드에 추가했는지 확인하는 것이다. _csrf 라는 요청 애트리뷰트를 사용해서 현재 CsrfToken을 가져와 사용할 수 있다. JSP에서 사용하는 예제는 다음과 같다.

“`

<c:url var=”logoutUrl” value=”/logout”/>
<form action=”${logoutUrl}”
method=”post”>
<input type=”submit”
value=”Log out” />
<input type=”hidden”
name=”${_csrf.parameterName}”
value=”${_csrf.token}”/>
</form>

“`

스프링 MVC가 제공하는 <form:form> 태그를 사용하면 CsrfRequestDataValueProcessor를 사용하여 CsrfToken이 자동으로 들어간다.

CSRF 주의할 것

CSRF를 적용할 때 몇가지 주의할 것이 있다.

타임아웃

기대했던 CSRF 토큰이 HttpSession에 저장되기 때문에 HttpSession이 만려되면  AccessDeniedHadnelr가 InvalidCsrfTokenException을 박데된다. 기본 AccessDeniedHandler를 사용하면 브라우저는 HTTP 403을 받게되고 조악한 에러 메시지를 보여줄 것이다.

실제 사용자 경험을 조금 더 완화 시킬 수 있는 간단한 방법으로는 자바스크립트를 사용하여 사용자에게 세션이 만료됐다는 사실을 사용자에게 알려주는 것이다. 사용자는 버튼을 클릭해서 세션을 “계속” 하거나 “갱신” 할 수 있다.

또 다른 대안으로는, 커스텀한 AccessDeniedHandler를 제공하여 InvalidCsrfTokenException을 원하는 방법으로 다루는 것이다. AccessDeniedHandler를 커스터마이징하는 방법은 XML이나 자바 설정으로 제공된 링크를 참고하도록 하자.

CSRF를 쿠키에 넣는건 어때?

다른 도메인이 쿠키를 세팅할 수 있기도 하고, 뭔가 위험해졌을 때 강제적으로 토큰을 제거하지도 못한다는 담점이 있다.

로그인

강제 로그인 요청을 방어하려면 로그인 폼도 CSRF 공겨을 방어해야한다. CsrfToken이  HttpSession에 저장되니까, HttpSession이 그 즉시 생성되야 한다는걸 뜻한다. 이 말은RESTful이나  stateless 아키텍처에는 안좋게 들리겠지만, 현실적으로 실질적인 보안책을 구현하려면 상태를 필요로 한다. 상태 없이는 토큰이 정상인지 확인할 방법이 없다. 현실적으로 말해서, CSRF 토큰은 매우 작은 크기이고 아키텍처에 끼치는 영향은 무시해도 될정도의 수준으로 그쳐야 한다.

로그아웃

CSRF를 사용하면 LogoutFilter가 HTTP POST만 사용하도록 바뀐다. 로그아웃할 때 CSRF 토큰을 사용하고 나쁜 사용자가 다른 사용자를 강제로 로그아웃 시킬 수 없게 한다.

한가지 방법은 로그아웃할 때 폼을 사용하는 것이다. 링크를 사용하고 싶다면 자바스크립트로 POST를 수행하는 링크를 사용하자(히든 폼으로). 자바스크립트를 사용하지 않는 브라우저에서는 선별적으로 POST 요청을 보내는 로그아웃 확인 페이지로 이동시킬 수 있겠다.

HiddenHttpMethodFilter

이 필터가 스프링 시큐리티 필터보다 먼저 등록되어 있어야 한다. In general this is true, but it could have additional implications when protecting against CSRF attacks.

Note that the HiddenHttpMethodFilter only overrides the HTTP method on a POST, so this is actually unlikely to cause any real problems. However, it is still best practice to ensure it is placed before Spring Security’s filters.

나머진 패스.

기본 설정 덮어쓰기

스프링 시큐리티의 목적은 사용자 폼이 노출되는 걸 막는 기본값을 제공하는 것이다. 그렇다고 해서 모두가 그 기본값을 사용하도록 강요하려는 것은 아니다.

가령, 커스텀한 CsrfTokenRepository를 제공하여 CsrfToken이 저장되는 곳을 변경할 수있다.

또한, 커스텀한 RequestMatcher를 설정하여 어떤 요청을 CSRF로 방어할지 결정할 수 있다(예를 들어, 로그아웃은 적용할 필요가 없다던지). 즉, 만약 스프링 시큐리티의 CSRF 방어가 여러분이 원하는 것과 정확히 맞지 않다면 여러분이 그 동작을 커스터마이징 할 수 있단 말이다.

결론

이제 CSRF가 뭔지 이해하고 스프링 시큐리티를 사용해서 애플리케이션을 CSRF 공격으로부터 어떻게 방어하는지 이해했을 것이다.

다음 글에서는 스프링 시큐리티의 헤더 기능을 사용해서 clickjacking 같은 공격으로부터 애플리케이션을 보호하는 방법을 살펴보겠다.

 

흥미진진 하구만~

호주 이민 딜레마

일단 가장 최소한의 조건은 갖춘지 언 두달 됐다.

그동안 이민 준비는 놀았다. 이민 도와주는 업체 사람을 만나 미팅을 잠깐 해서 대충 견적을 뽑아봤지만 그것만으로는 이민 갈지 말지 안정적으로 살 수 있을지 없을지 알기 어려웠다.

일단 가장 걱정되는건 직장을 구할 수 있겠느냐인데 쉽지 않다. 직장을 구하려면 최소한 영주권이 있거나 시민권자여야 한다. 정말 정말 아주 아주 드물게 스폰서쉽까지 제공하며 채용하는 경우도 있긴하겠지만 사막에서 바늘 찾아서 거기로 낙타를 통과시키는 정도쯤 되려나. 현재 내가 가진 능력으로는 거의 불가능할듯하다. 그 사람들이 입장에서 난 그렇게 매력적인 인력이 아니니까. (그린팩토리에서 돈을 지불하면서까지 아프리카 출신 직원을 뽑아오는 것과 비슷한 수준이려나)

암튼 딜레마는 단순하다.

영주하려면 직장이 필요한데 직장을 구하려면 영주권이 필요하다.

이 고리를 끊을 방법은 돈과 능력이다.

돈으로 영주권을 사고 능력으로 직장을 구한다.

둘 다 안되면 한국에 잔류하거나 가서 거지된다.