레이블이 security인 게시물을 표시합니다. 모든 게시물 표시
레이블이 security인 게시물을 표시합니다. 모든 게시물 표시

2018-04-19

Byzantine Fault Tolerance 시스템에서 N = 3f + 1인 이유

분산환경 시스템에서는 다른 노드가 보낸 메시지가 정상적이라고 보장할 수 없다. 이때 잘못된 노드가 모두에게 틀린 메시지를 보낸다면 문제가 쉽게 풀린다. 틀린 메시지를 보내는 노드를 차단하면 된다.

하지만 일부 노드에게는 잘못된 메시지를 보내고, 일부 노드에게는 제대로 된 메시지를 보내는 경우는 문제 상황을 찾기 힘들다. 분산 시스템에서 각 노드는 다른 노드의 상태를 모르기 때문이다. 이런 식으로 일부 노드에게만 틀린 메시지를 보내는 노드를 가정하는 모델을 byzantine failure model이라고 부른다.

Byzantine failure model은 네트워크에서 가장 풀기 어려운 모델임과 동시에 실제 네트워크에서 반드시 해결해야 하는 문제다. 특히 다른 노드를 신뢰할 수 없는 p2p에서는 반드시 Byzantine failure model을 가정하고 예외 상황을 처리해야 한다.

그렇다고 인증된 노드만으로 구성된 분산 시스템이라고 byzantine failure model을 가정하지 않아도 된다는 것은 아니다. 노드 자체는 신뢰할 수 있는 사람이 관리하더라도 해킹당했을 수도 있고, 버그로 잘못된 메시지를 보낼 수도 있고, 하드웨어에 문제가 발생할 수도 있다.

Byzantine failure model에서도 정상적으로 돌아가는 시스템을 byzantine fault tolerance (a.k.a. BFT)라고 말한다. 당연히 BFT라고 해도 무한히 많은 faulty 노드에 대해서 동작하지는 않는다. 그래서 보통 어떤 시스템이 BFT라고 말할 때 전체 노드 중 몇 개의 노드에 문제가 있을 때까지 동작하는지를 같이 말한다. 예를 들어 N = 5f라고 말하면, 전체 노드 중 1/5가 byzantine failure일 때 정상 동작하는 시스템이고 N = 3f + 1이라고 말하면, 전체 노드 중 1/3이 byzantine failure일 때까지는 문제없이 돌아가는 시스템을 말한다.

같은 BFT라고 한다면, 감당할 수 있는 faulty 노드의 비율이 더 큰 시스템이 더 안전한 시스템인 것은 당연하다. 하지만 N = 3f + 1. 즉, 1/3 이상의 byzantine failure를 가정하는 시스템은 없다. 이는 1/3이 이론상으로 가질 수 있는 최대치이기 때문이다.

어떤 노드가 byzantine failure일 때 발생할 수 있는 문제 상황은 크게 두 가지다. 첫 번째 문제 상황은 메시지를 보내지 않는 것이다. 즉, 분산 시스템이 N개의 노드 중 byzantine failure f개가 정상적으로 동작하기 위해서는 N - f개의 메시지만으로 합의가 이루어져야 한다. 이것을 다른 말로 quorum을 위해서 N - f개의 노드가 필요하다고 말한다.

두 번째 문제 상황은 byzantine failure인 노드가 악의적으로 다른 메시지를 보내는 것이다. 극단적으로는 quorum을 이룬 N - f개의 노드 중에서 f개가 byzantine failure가 보낸 메시지인 경우를 생각해볼 수 있다. 이 경우도 정상 동작해야 하므로 (N - f) - f 개의 메시지가 byzantine failure인 노드가 보낸 f개의 메시지보다 많아야 한다.

위의 두 경우를 대비하기 위해서 (N - f) - f > f. 즉, N > 3f이므로 f개의 byzantine failure인 노드가 있을 때 최소 3f개보다 많은 노드가 있어야 byzantine fault tolerance한 시스템이고, 이때 가장 작은 N은 3f + 1이다. 따라서 3f + 1개의 노드로 구성된 시스템에서 견딜 수 있는 faulty 노드의 최대 수는 f다.

2018-04-14

2018년 15번째 주

이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.

보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.


실제로 git을 사용하면서 단순히 커맨드를 외워서 사용하는 사람들을 많이 봤다. 보통 그 이유로 크게 두 가지를 든다. 첫 번째로 git의 mental model이 복잡하다는 것이다. git에서 변경된 내용은 크게 다음 상태 중 하나가 된다.

  1. 리모트에 존재하는 상태
  2. 로컬 브랜치에 있는 상태
  3. 브랜치에 머지되지 않았지만 add 돼 있는 상태
  4. 변경은 있지만 add 되지는 않은 상태
  5. stash에 들어있는 상태
  6. 예전에 커밋했었지만 지금은 브랜치로 따라갈 수 없는 상태

이 중에서 내가 수정했던 내용이 어떤 상태인지 모르는 것이 헷갈리게 하는 첫 번째 이유다.
하지만 반대로 왜 이렇게 많은 상태를 가지게 됐을지 생각해보면 git을 사용하는 데 도움이 된다. 이것들은 전부 그냥 추가된 것이 아니다. 애초에 git을 처음 만든 사람은 Linus Torvalds다. 그의 성격상 쓸모없는 것은 추가되지 않았다. 전부 제각각의 목적을 가지고 있다. 이 목적을 이해하는 것이 중요한데 아쉽게도 글로 잘 설명할 자신이 없다. 사실 이걸 이해하는 가장 빠르고 확실한 방법은 svn을 써보는 것이다. 쓰다 보면 불편한 부분들이 자주 생기는데, git에서는 위에서 말한 것들을 이용해 이를 쉽고 빠르게 해결할 수 있다.

사람들이 git을 어려워하는 두 번째 이유는 명령어가 복잡하다는 것이다. 이건 어쩔 수 없다. 사실 git의 명령어는 규칙성 없이 만들어졌다. 그래서 외우는 수밖에 없다. 하지만 어떤 상황에서 어떤 명령어를 써야 한다는 식으로 외우면 끝이 없다. 그보다는 각 명령어가 어떤 상태와 연관이 있는지를 보는 것이 좋다. git 명령어는 대부분 변화된 내용을 위의 상태 중 하나로 만들거나, 특정 상태로 로컬에 있는 파일을 변경하는 것이다. 따라서 현재 파일이 어떤 상태에 있고 명령어 이후 어떤 상태로 된다는 것을 인지하고 있으면 조금은 더 쉽게 적응할 수 있다.

News is bad for you – and giving up reading it will make you happier

뉴스가 사람의 정신건강에 해롭다는 기사다. 사람들이 흔히 정보를 얻으려고 뉴스를 읽지만, 실제로는 별 도움도 안 되고 시간만 낭비하게 하고 사람의 창의성을 없애고 수동적으로 만든다는 것이다.

Introduction to zkSNARKs

이더리움의 최근 움직임 중에서 zero-knowledge proof인 zkSNARKs을 도입하고자 하는 움직임이 있다. 위 자료는 이더리움 소속으로, 솔리디티 창시자 중 하나이며 zkSNARKs를 구횬하고 있는 Christian Reitwiessner이 zkSNARKs에 대해 간단히 소개한 발표자료다. 자료 이름은 zkSNARKs이지만 대부분의 내용을 zero-knwledge proof가 무엇인지에 설명하는 데 쓰고 있으므로 zero-knowledge proof가 무엇인지 알고 싶은 사람들이 보기에도 좋은 자료다.

RSS is undead

RSS는 이미 수명이 끝났고, 다시 살아날 리 없다는 글이다. 지난번에 소개했던 It's Time for an RSS Revival이나 Why RSS Still Beats Facebook and Twitter for Tracking News의 반대편에 있는 입장이다.

RSS는 소규모 사이트의 글을 구독하는 데는 적절하지만, 대형 사이트의 특정 카테고리의 글을 구독하는 데는 적당한 UX를 제공하지 못하고, 퍼블리셔 입장에서 사용자의 패턴을 파악할 수 없을뿐더러 광고수익을 얻을 수 없기 때문에 사용자와 퍼블리셔 양쪽에게 손해라는 것이다.

What is a “box model”? (UI Element layout)

Web을 비롯하여 현대의 많은 UI framework들이 box model을 사용하기 때문에 프론트엔드 개발을 하려면 한 번 읽어두는 것이 좋다.

The Science of The Job Search, Part III: 61% of “Entry-Level” Jobs Require 3+ Years of Experience

경력 같은 신입을 원하는 건 우리나라뿐 아니라 전세계 공통인듯하다.

A fork on Github is no fork

Git은 decentralized 된 version control system이기 때문에 fork를 하면 어디서 fork 해왔는지와 완전히 독립적인 repository로 존재할 수 있다. 하지만 Github의 fork는 그 모든 설정과 권한이 원본 repository에 종속적이기 때문에 진짜 fork가 아니라는 글이다.
원칙적으로 맞기는 하지만 Github Enterprise나 private repository 유료 모델을 생각해보면 어쩔 수 없는 선택이었다고 생각한다.

Uninhabited types are ZSTs despite partial initialization being allowed in safe code.

Rust 1.24.0 이후 생긴 memory safety가 깨지는 버그다.

Rust는 무한루프나 내부에서 exit 하기 때문에 return 되지 않는 함수를 표현하기 위해서 uninhabited type을 도입했다. 이 uninhabited type으로 product type을 만들면 이 타입도 uninhabited type이 돼서 크기가 0인 type으로 계산되는데, 이 product type의 다른 field를 접근하는 게 가능해서 주소 계산이 잘못되는 버그가 발생했다. Rust에 zst(zero size type)가 도입됐을 때부터 메모리 주소 계산을 잘못 하는문제가 발생하지 않을까 생각했는데 정말로 터져버렸다.

현재 두 가지 해결 방안 중에서 어느 쪽으로 해결할지 논의 중이라고 하니까 다음 버전인 1.26.0에서는 수정될 것으로 보인다.

Top 10 Bugs in the C++ Projects of 2017

소프트웨어 분석 회사인 viva64에서 2017년 동안 발견했던 버그 중 가장 인상 깊은 버그 10개를 발표하였다. uninitialized memory부터 type error나 단순 실수까지 다양하게 있다.

Don’t Give Away Historic Details About Yourself

자기 정보를 쉽게 제공하지 말라는 글. 특히 우리나라 사람들은 주민등록번호 유출에조차 익숙해져서 그런지 자기 정보를 적는데 아무런 망설임이 없다.

Why Does "=" Mean Assignment?

수학에서는 보통 대입에는 :=를 사용하고 =는 비교에 사용한다. 근데 프로그래밍에서는 왜 =를 대입에 사용하게 됐는지에 관한 글이다. 뭐 지금은 다들 =를 대입연산자로 사용하고, ==를 비교연산자로 사용하는 데 익숙하기 때문에 별 의미는 없지만, 그냥 재미로 읽어봤다.

2018-04-07

2018년 14번째 주

이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.
 보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.

Why you should pick strong consistency, whenever possible

지난번 글에서 CP와 AP 중에서 CP를 AP보다 더 선호해야 한다고 썼었다. 이렇게 생각한 사람이 나만 있는 건 아닌 것 같다. 구글 클라우드 플랫폼 팀에서 발표한 Spanner라는 데이터베이스는 external consistency를 보장한다. 이 포스트는 Spanner가 external consistency를 사용한 이유에 관한 글인데 제목은 strong consistency라고 나와 있지만, 이는 strong consistency가 더 일반적으로 사용되는 용어이기 때문에 제목을 이렇게 쓴 것이지 Spanner는 언제나 최신 데이터를 읽을 것을 보장하는 external consistency를 보장한다.

MantisTek GK2's Keylogger Is A Warning Against Cheap Gadgets

중국 키보드에서 키로거가 검출됐다고 한다. 개인적으로 전자제품 살 때 알리익스프레스를 많이 사용한다. 같은 사양의 제품을 10분의 1도 안 되는 가격으로 살 수 있기 때문이다. 그때마다 친구들과 이거 전부 해킹당하고 있는거 아닌가라는 농담 하면서 구매하고 몇일은 외부로 나가는 네트워크를 감시하고 그랬는데 실제로 키로거 하는 제품이 있었다.

There's a biological reason you're bored at work

회사 생활에 질리고 권태감이 드는 게 생물학적으로 당연하다고 한다. 사람의 뇌는 언제나 새로운 것을 추구하도록 진화됐다고 한다. 이는 사람뿐만 아니라 동물들에게도 마찬가지다. 동물들도 주어진 먹이를 먹는 것보다 숨겨진 음식을 찾아서 먹는 것을 더 좋아한다. 숨겨진 것을 찾아내고 새로운 것을 알아가면 뇌가 자극받아 도파민을 분비하고 이게 기쁨을 느끼게 한다는 것이다.

문제는 현대의 회사 시스템은 이런 도전 정신을 자극할만한 것이 없다는 것이다. 그래서 결국 주기적으로 팀을 옮기거나 조금 더 적극적인 경우 회사를 옮기거나 회사 생활에서 못 받는 자극을 취미에서 찾거나 하는 것 같다.

개인적으로는 이런 권태감을 개인 프로젝트로 푸는 편이다. 특히 회사 일과 같은 것을 하지 않기 위해서 개인 프로젝트를 할 때는 회사에서 쓰는 것과 다른 특성의 언어를 사용하여 회사 일과 상관없는 분야를 한다.

Known-planitext attack

KPA라고 불리는 암호학 공격기법이다. 이름 그대로 공격자가 암호화되기 전의 평문을 알고 있는 암호문이 몇 개 있을 때, 이를 기반으로 어떤 알고리즘으로 암호화된 것인지, 혹은 알고리즘을 이미 알고 있다면, 어떤 키로 암호화됐는지 찾아내는 공격방법이다. 찾아낸 알고리즘이나 키를 이용하여 후에 들어오는 암호문을 복호화하는 것이 목적이다.

It's Time for an RSS Revival

지난번에 소개했던 Why RSS Still Beats Facebook and Twitter for Tracking News와 비슷한 글이다. 최근 페이스북에서 터진 이슈 때문에 많은 사람이 대안을 찾고 있어 RSS에 주목하는 글이 많이 나오고 있는 듯하다.

Metcalfe's law

네트워크의 효용성은 노드 간 컨넥션 수가 높을 수록 높으므로 노드 수의 제곱에 비례하게 된다는 법칙이다. 하지만 현실적으로는 모든 노드가 서로 컨넥션을 갖지 않는다. 따라서 크기가 작은 네트워크에서는 메칼프의 법칙이 성립하지만, 크기가 커지면 성립하지 않는다. 일반적으로 노드 수가 많은 네트워크는 complete graph도 아니고 각 컨넥션이 모두 같은 가치를 갖는것도 아니기 때문이라고 한다.

2018-03-29

[보안] Alice와 Bob

네트워크 프로토콜을 설명하는 글을 읽으면 AliceBob, Carol, Eve, Mallory 등 많은 이름이 등장한다. 보통 이 이름들은 일정한 규칙을 가지고 부여되기 때문에 각 이름이 무슨 의미를 가지는지 안다면 그 프로토콜이 무엇을 어떻게 풀려고 하는지 조금은 더 이해하기 쉬워진다. 이번 글에서는 많이 네트워크 프로토콜에서 많이 사용되는 이름들이 무슨 의미를 가지는지 간략하게 정리해보았다.

네트워크 참여자 - Alice, Bob

보통 AliceBob은 서로 통신하려는 사람을 의미한다. 이때 통신하는 메시지는 보통 암호화하여 통신하는 메시지를 의미하는데 비대칭 키를 사용할지, 대칭 키를 사용할지는 그때그때 다르다.

다자 간 통신 참여자 - Carol, Dave, Erin, Frank, Gray

CarolDaveAliceBob과 함께 통신에 참여하는 경우 사용된다. CarolDave 말고도 CharlieDavid 같은 이름을 사용하기도 하지만 보통 CarolDave를 많이 사용한다.

5명 이상의 참여자가 필요한 프로토콜을 묘사할 때는, E, F, G로 시작하는 이름들을 사용한다. 보통 Erin, Frank, Gray 등의 이름이 사용되는데 어떤 이름을 사용할지는 딱히 정해져 있지 않다. 다른 용도로 사용되는 EveFaythe, Grace 등을 제외하고 아무 이름이나 사용된다.

공격자 - Eve, Mallory, Oscar, Trudy

Eve는 보통 네트워크를 감시하여 패킷을 도청하는 공격자를 의미한다. 다만 Eve는 능동적으로 공격을 하지는 않고 AliceBob이 주고받는 대화를 도청하여 무슨 대화를 주고받는지 알아내는 공격자에게 붙이는 이름이다. 이름의 유래는 eavesdrop에서 나왔다.

Trudy라는 이름은 Eve보다 조금 더 적극적인 공격자를 지칭할 때 사용된다. AliceBob 사이에 끼어들어 AliceBob의 메시지를 가로채 변조하여 보내거나 지우고 자신이 보내고 싶은 메시지를 보내는 공격자를 의미한다. 이름은 intruder에서 유래했다.

조금 더 포괄적인 공격자를 부르는 이름으로 Mallory가 있다. 이름의 유래는 malicious라는 단어에서 나왔다. EveTrudy와 구분 없이 공격자는 전부 합쳐 Mallory라고 부르기도 하므로 Mallory라는 이름이 나오면 이 사람이 어떤 공격을 하는지는 상황에 맞게 해석해야 한다. 이와 비슷한 의미로 사용되는 Oscar라는 이름도 있다.

감시자 - Walter, Wendy

Walter 혹은 Wendy라는 이름은 참여자를 감시하는 합법적인 감시자를 의미한다. 이들이 하는 일은 Mallory를 비롯한 공격자들과 비슷하지만, WendyWalter라는 이름이 나오면 AliceBob이 악의적인 사용자가 된다. 하지만 검열 감시를 피하는 것을 목적으로 하는 프로토콜을 설명할 때도 WalterWendy라는 이름을 사용한다. 예를 들면 검열하는 정부나 기업을 Wendy라고 이름 붙이고 이를 피하기 위한 참여자를 AliceBob이라고 이름하기도 한다.

WalterWendy는 크게 구분 없이 사용되는데 굳이 구분하면 Wendy는 허락되지 않은 정보를 주고받을 때 패킷을 검열하여 보내지 않도록 하는 역할을 하지만 Walter는 패킷을 검열하지는 않고 감시하는 역할만 할 때 사용된다. 보통 메시지를 감시하는 사람이 검열도 하기 때문인지 Wendy라는 이름이 더 많이 사용된다.

신뢰할만한 제삼자 - Trent, Ted

지금까지 설명한 이름들은 AliceBob의 통신을 방해하는 역할에 주어지는 이름이었다. 하지만 지금 설명할 TrentAliceBob의 통신을 도와주는 역할을 한다. 구체적으로 어떤 역할이 있지는 않고, AliceBob의 통신을 도와주는 역할을 한다. AliceBob이 통신할 shared secret을 생성, 관리해주기도 하고, 어떨 때는 AliceBob이 직접 통신하는 것이 아닌 대신 통신할 수 있는 암호화 된 채널을 만들어주기도 한다. 비대칭 키를 사용하는 경우, AliceBob의 public key를 저장하는 저장소를 Trent라고 부르기도 한다. Trent 대신 Ted라고 부르기도 하며 이름의 유래는 trust다.

2018-03-26

이더리움과 Eclipse attack

p2p 네트워크는 많은 취약점을 가지고 있는데 대표적인 것이 Eclipse attack이다. Eclipse attack은 네트워크 전체를 공격하는 것이 아니라 목표로 하는 노드의 Routing table을 공격하여 목표로 하는 노드와 전체 네트워크 사이에 악의적인 노드를 집어넣는 공격이다. Routing table을 공격하는 방법이기 때문에 routing-table poisoning이라고도 불린다.

이더리움도 p2p 네트워크를 사용하여 메시지를 주고받기 때문에 eclipse attack이 가능하리라 생각은 했는데 지난 3월 1일 발표 된 페이퍼에 따르면 단 2개의 노드만으로 하나의 노드를 완전히 고립시키는 것이 가능하다고 한다. 이 페이퍼는 올해 1월 진행됐던 바운티 프로그램에서 나온 문제점들을 정리한 페이퍼로 크게 세 가지 공격방법으로 나눌 수 있다.

우선 첫 번째 문제는 이해하기 위해 이더리움이 p2p 네트워크를 어떻게 관리하는지 이해해야 한다. 네트워크 그래프 구성에 가장 중요한 것은 다른 노드를 어떻게 찾을 것인가 하는 것이다. 이를 흔히 node discovery라고 하는데 이더리움은 node discovery를 위해 DHT(Distributed Hash Table) 프로토콜 중 하나인 Kademlia의 일부를 수정해서 사용한다.
Kademlia가 다른 DHT와 다른 가장 큰 특징은 노드 간의 거리를 XOR distance로 측정한다는 것이다. XOR distance의 거리는 symmetric 하므로 노드 아이디만 알고 있으면, 노드 A가 생각하는 노드 B까지의 거리나, 노드 B가 생각하는 노드 A까지의 거리나, 노드 C가 생각하는 노드 A와 노드 B 사이의 거리가 같다. 따라서 각 노드는 자신이 알고 있는 노드 중에서 자신과 가까운 노드들과만 통신하면 적은 연결 수로도 큰 네트워크를 구성할 수 있다는 장점이 있다. Kademlia 페이퍼에는 대략 노드의 개수를 N이라고 할 때 각 노드는 O(log(N))개의 연결만 유지하면 된다고 말한다.
이더리움이 Eclipse attack에 취약했던 근본적인 원인이 여기에 있다. Kademlia 프로토콜에서는 거리를 노드 아이디를 기반으로 구하는데, 어떤 노드의 아이디가 무엇인지 네트워크 안에 있는 모든 노드가 같은 값으로 알고 있어야 한다. 그래야 Kademlia가 주장하는 효율성을 가져올 수 있다. 문제는 분산 환경에서 각자의 아이디를 동의하는 방법이 없기 때문에 연결을 요청하는 노드가 자신의 public key를 알려주면 그 public key를 노드 아이디를 Keccak hashing 한 값을 노드 아이디로 준다. 즉, 각 노드가 자신의 아이디를 정하여 알려주는 것이다. 따라서 하나의 머신에서 여러 개의 노드 아이디를 생성해낼 수 있고, 악의적인 사용자가 하나의 머신을 여러 개의 노드인 것처럼 꾸미는 Sybil attack이 가능해진다.
이를 막기 위해 IP 주소와 노드 아이디를 기억하여 하나의 주소에서 여러 개의 키를 사용 못 하게 하는 것이 필요하지만, 아직 구현된 솔루션은 존재하지 않는다.

두 번째 공격 방법은 서버의 시간을 조정하는 것이다. 이더리움은 replay-attack을 막기 위해서 메시지에 보낸 시간을 적는다. 그리고 받은 메시지의 시간이 현재 시각과 20초 이상 차이가 나면 replay-attack으로 받은 메시지로 가정하고 무시한다. 문제는 replay-attack인지 판단하기 위해 메시지에 적힌 시간을 사용할 뿐, 메시지를 보낸 실제 시간은 알 수 없다는 것이다. 예를 들어 두 노드 사이에 시간이 20초 이상 난다면, replay-attack이 아닌 정상적인 메시지도 절대 처리되지 않는다.
현대 서버 환경에서 이럴 일은 거의 없다. 보통 서버 시간을 수동으로 설정하지 않고 NTP(Network Time Protocol)를 이용해 주기적으로 동기화하기 때문이다. 따라서 모든 서버의 시간은 길어봐야 몇 초 이내의 오차를 가질 것이라고 가정해도 된다.
하지만 이는 정상적인 상황에서만 가능한 가정이다. 만약 희생자가 될 노드가 사용할 NTP 서버를 해킹하거나 NTP 메시지를 가로채 동기화하지 못 하는 상황을 만들면 어떻게 될까. 아니면 조금 더 적극적으로 NTP 메시지를 가로채 실제 시간과 20초 이상 앞으로 당기면 어떻게 될까.
이 경우 희생자 노드는 네트워크상의 모든 노드와 시간 차이가 나게 될 테니, 다른 노드들은 모두 이 노드에서 보내는 메시지를 무시할 것이다. 그렇게 되면 희생자 노드는 네트워크에서 잊히게 되고 결국 희생자와 통신하는 것은 NTP 서버를 해킹했던 공격자 노드밖에 남지 않게 된다.
이는 메시지에 시간을 적는 방식으로 replay-attack을 막으려고 했던 이더리움 프로토콜의 문제다. 이런 공격을 막기 위해서는 세션별 혹은 메시지 별로 nonce를 사용하여 sign 하는 방법을 이용해야 한다. 하지만 이에 관한 솔루션도 아직 이더리움에는 구현된 것은 없는 것으로 보인다. 따라서 이더리움 노드를 돌리고 있는 서버는 시간이 잘 동기화되고 있는지 주기적으로 확인하는 방법밖에 없다.

마지막 공격 방법은 일종의 타이밍 어택이다. 이더리움은 네트워크에 너무 많은 리소스를 사용하지 않기 위해 통신할 최대 연결 수를 정해놓고 있다. 현재 최대 연결 수는 25개로 25개 이상의 연결이 맺어지면 더는 새 연결을 요청하거나 받아주지 않는다.
반대로 말해서 어떤 노드의 연결을 25개 점유할 수 있으면 이 노드는 더 이상 새 연결을 요청하지도 받아주지도 않을 것이다. 그렇다면 이것은 어떻게 가능할까?
간단하다. 연결이 0개인 순간을 노리면 된다. 어떤 서버라도 서버가 처음 시작했을 때는 연결이 0개인 상태가 된다. 이 타이밍을 노려서 25개의 연결을 먼저 맺으면 된다. 그렇다면 서버가 재시작되는 순간은 어떻게 찾을까.
하드웨어 문제든 OS 업데이트든 이더리움 클라이언트를 업데이트하든 서버가 재시작될 이유는 많이 있다. 하지만 조금 더 공격적으로 DoS 공격을 하여 이더리움 클라이언트를 죽이는 방법도 있다. 보통 이더리움 마이닝 노드는 서버가 종료되면 바로 다시 시작하게 돼 있기 때문에 DoS 공격으로 서버를 죽이고 나면 얼마 지나지 않아 서버가 재시작할 것이고 이때 25개 이상의 연결을 빠르게 요청하면 된다.
이를 막는 방법은 앞에서 설명한 두 방법보다 쉽다. 앞의 두 방법은 프로토콜 자체를 바꿔야 하지만 이 공격은 구현체를 수정하는 것만으로 쉽게 막을 수 있다. 전체 연결 개수 제한과 별개로 외부에서 들어오는 연결의 개수를 제한하는 것이다. 즉, 최소 몇 개의 연결은 내가 요청하는 노드와 연결이 되도록 하면 누군가 다른 사람에 의해 내 연결이 독점 당할 위험은 사라진다. 이 솔루션은 2월에 릴리즈 된 go-ethereum 1.8 버전부터 적용된 것으로 보인다.

2018-03-04

2018년 9번째 주

 이 포스팅은 그냥 지난 한 주간 읽었던 것들을 정리하는 포스트입니다. 그냥 예전에 봤던 글 중 나중에 필요한데 뭐였는지 기억 안 나는 글들이 있어서 쓰기 시작했습니다.
 보통 하는 일과 관련된 글들이 올라오겠지만 딱히 정해둔 주제는 없고, 그때그때 관심 있었던 것을 읽었기 때문에 지난주에 쓰인 글일 수도 있고 몇 년 전에 쓰인 글일 수도 있습니다.

Lifetime Safety: Preventing Leaks and Dangling

 Herb Sutter와 Neil MacIntosh가 쓴 C++에서 memory leakdangling pointer를 어떻게 없앨 수 있는지에 관한 글이다. 일반적으로 C++의 포인터는 매우 강력하기 때문에, memory leak이나 dangling pointer를 없애기 위해서 C++의 기능을 일부 제한하거나 새로운 문법을 추가하거나 한다. 하지만 이 글에서는 언어를 바꾸지 않으면서 런타임 오버헤드 없이 컴파일 타임에 분석할 수 있는 알고리즘을 제시한다.
 특히 이 알고리즘은 프로그램 전체를 분석하는 것이 아니라 함수 단위, 정확히는 블록 단위로 적용할 수 있고, 변수 재사용 등 많은 스타일 가이드에서 권하지 않지만 실제로는 많이 사용되는 패턴들에 대해서도 고려돼있기 때문에 레거시 코드에 적용하기도 좋다.
 기계적인 작업이기 때문에 툴을 만드는 것이 가장 좋을 것이다. 하지만 툴을 만들 여유가 없더라도 포인터나 레퍼런스를 어떻게 써야 안전한지 보여주는 좋은 글이기 때문에 일단 읽어보는 것을 추천한다.

GitHub DDoS 공격당함

 지난 2018년 2월 28일, GitHubDistributed Denial-of-Service(a.k.a. DDoS) 공격을 당해 약 10분 정도 서비스가 멈췄었다. 이 공격은 memcached를 이용한 공격으로 중국의 0kee team이 찾은 Deluge라는 기법을 이용한 공격이었다.
 Deluge는 다른 서버에 설치된 memcached에 데이터를 요청할 때 source IP를 목표가 되는 서버의 IP로 수정하여 보내, memcached 서버가 목표가 된 서버로 데이터를 보내게 만드는 공격이다.
 이런 부류의 공격을 IP spoofing이라고 하는데, 이는 Internet protocol(a.k.a. IP)OSI 7 layer에서 말하는 session layer에 관한 부분이 없기 때문에 생기는 근본적인 취약점을 이용한 것이기 때문에, 네트워크 프로토콜을 IP 위에 올린다면, 프로토콜을 작성하는 차원에서 세션 처리를 신경 쓰지 않았다면 근본적으로 막을 방법은 없다.
 IP spoofing 자체는 흔하게 사용되는 공격법이지만 memcached를 사용하는 Deluge는 그중에서도 큰 획을 그었다. 단순히 GitHub을 공격했기 때문이 아니라 적은 패킷으로 많은 데이터를 공격하게 할 수 있기 때문이다. 이 비율을 bandwidth amplification factor라고 하는데 Deluge는 지금까지 알려진 IP spoofing 공격 중에서 가장 높다.

2018-02-22

Cloudflare의 Flexible SSL을 쓰면 안 되는 이유

 Cloudflare의 서비스 중 Flexible SSL이라는 것이 있다. SSL 인증서가 없는 서버에 있는 웹페이지도 https를 이용해 접근할 수 있도록 해주는 서비스다. 자신이 인증서를 설치할 수 없는 서버나 서비스를 사용할 때도 https를 사용할 수 있게 해주기 때문에 blogger처럼 커스텀 도메인에 https를 지원 안 하는 서비스를 이용하는 사람들이 많이 사용한다. 이 블로그도 blogger에서 custom domain을 이용하고 있기 때문에 https를 지원하려면 Cloudflare의 Flexible SSL이 사실상 유일한 옵션이다. 하지만 https를 포기하고 Flexible SSL을 사용하지 않고 있다. 왜냐하면, Flexible SSL이 아무런 이득이 없는, 오히려 위험하기만 한, 존재해서는 안 되는 서비스이기 때문이다. Flexible SSL을 엄청 디스한 것 같은데 어째서 그런지 Flexible SSL이 동작하는 방식을 보면 쉽게 이해할 수 있다.

 위의 도표는 Flexible SSL이 어떻게 동작하는지를 그림으로 표현한 것이다. Cloudflare의 DNS는 요청된 도메인에 대해서 원래 서버의 주소를 주지 않고, Cloudflare 서버의 주소를 준다. 그러면 클라이언트의 브라우저는 원래 서버가 아닌 Cloudflare의 서버로 접속한다. 그러면 Cloudflare의 서버는 원래 서버로 다시 요청을 보내고, 받은 결과를 클라이언트에게 돌려준다. 이때 클라이언트와 Cloudflare 사이의 통신은 암호화된 https로 이루어지고, Cloudflare와 원래 서버 사이의 통신은 암호화되지 않은 http로 이루어진다.

 이를 보고 "최소한 Cloudflare와 클라이언트 사이에는 https를 사용하기 때문에 안전하지 않은가"라고 생각하는 사람도 있다. 하지만 아니다. 보안에서 자주 사용되는 격언에 "A chain is only as strong as its weakest link."라는 말이 있다. 어떤 시스템이 해킹당할 위험도는 그 시스템을 구성하는 요소 중에서 가장 취약한 요소가 해킹당할 위험도와 같다는 말이다. 설령 클라이언트와 Cloudflare 사이의 통신이 암호화됐다고 해도, Cloudflare 서버와 원본 서버 사이의 통신이 암호화됐지 않았기에 Man-in-the-middle attack을 당해 조작된 페이지를 받거나 요청된 데이터가 해킹당할 위험도는 Cloudflare 서버를 통하지 않고 원본 서버와 바로 http로 통신했을 때와 같은 위험도를 가진다.

 사실 Flexible SSL은 사용 안 하는 것보다 더 위험하다. Flexible SSL을 사용하지 않으면, 기본적인 지식이 있는 사용자라면 지금 페이지가 안전한 페이지인지 판단하여 민감한 정보를 보낼지 말지 결정할 수 있다. 하지만 Flexible SSL을 이용하면 모든 페이지가 암호화된 것처럼 보이기 때문에 사용자가 안전한 페이지인지 판단할 수 없다. 게다가 최신 브라우저의 경우 보안을 위해서 안전하지 않은 페이지와 안전한 페이지를 자동으로 구분해서 https 페이지에서 http 페이지로 데이터를 보내거나 http 페이지에서 콘텐츠를 가지고 오는 것을 막는다. 하지만 Flexible SSL을 사용하면 이런 기능을 전혀 이용할 수 없다.
 따라서 Flexible SSL을 사용하면 해킹 위험도는 그대로 가져가면서, 오히려 브라우저의 보안 기능을 못 사용하게 될 수 있기 때문에 더 위험해질 수 있다. 특히나 사용자에게 위험도를 속인다는 점에서 Flexible SSL을 사용하면 사용자를 기만하게 된다고 말할 수 있다.


이 글을 쓸 때는 몰랐는데 작년 12월에 이미 blogger도 custom domain에 대한 https 지원을 시작했었다. 올해 계속 바빠서 확인을 제대로 못 했던 것은 있는데, 이 정도 변화면 레딧이든 해커 뉴스든 어디선가 관련된 글이 올라왔을 것 같은데 왜 못 보고 넘어갔는지 모르겠다.

2018-02-21

Diffie-Hellman Key Exchange - 공개된 정보만으로 secret key 만들기

 네트워크상의 두 노드가 암호화된 통신을 하기 위해선 먼저 두 노드가 어떤 암호화 방식으로 어떤 키를 이용해서 암호화할지 합의해야 한다. 보통 암호화 방식은 사용하는 애플리케이션에 따라 고정된 방식을 사용하거나 두 노드가 처음 통신을 시작할 때 암호화하지 않은 패킷을 이용해 합의하거나 한다. 이후 패킷은 양쪽 노드밖에 모르는 암호키를 이용해 암호화할 것이기 때문에 암호화 방식은 암호화되지 않은 방식으로 합의를 해도 안전하다. 하지만 어떤 키를 사용할지는 암호화되지 않은 방식으로 합의해선 안 된다. 키가 공개되면, 이 비밀키를 이용해서 제삼자가 패킷을 위조할 수 있기 때문이다. 그렇다면 이 비밀키는 어떻게 안전하게 교환할 수 있을까?
 이에 대한 해답으로 나온 것 중 하나가 Diffie-Hellman key exchange(a.k.a. DH)다. 사실 이외에도 다른 방법들이 많이 있지만, 개인적으로 생각하기에 가장 범용적으로 안전하게 사용할 수 있는 것은 DH라고 생각한다. 또한, 이후 이것에 대해 많은 변종이 나왔지만, DH만 이해하면 나머지는 이해하는 데 별문제 되지 않는다. 그렇다면 DH는 어떻게 동작할까?

 우선 DH가 성립하기 위해서는 특별한 수학적 성질을 만족하는 generator가 필요하다. 이 generator는 하나의 입력을 받아 하나의 출력을 내뱉는다. 이 generator가 g라고 하고, 입력 x에 대해서 출력 Y를 내뱉는 Y=g(x)가 있을 때, x로부터 Y를 가지고 오는 것은 빠르고 쉽게 계산할 수 있지만, Y로부터 x를 가지고 오는 것은 어려운 일이어야 한다. 즉, 수학적으로는 역함수가 없는 함수여야 하고, 결괏값의 스페이스가 매우 커서 brute-force로 찾는 것이 매우 힘들어야 한다. 사실 이외에도 만족해야 할 수학적 성질이 여러 개 있지만 이번 포스팅에서는 그에 대한 설명은 생략하고 넘어가겠다.

 DH가 처음으로 제시한 방법은 generator로 modular exponentiation을 사용했다. modular exponentiation에서는 입력을 x라고 했을 때, 출력 Y를 다음과 같이 계산한다. Y:=bxmodm. 이때 bm은 적절한 수학적 성질을 만족하는 값으로, bm의 pair가 generator g가 된다.
 이후 여러 변종이 나왔고, 요새 많이 사용되는 것은 elliptic curve를 이용하는 Elliptic-curve Diffie–Hellman(a.k.a. ECDH)이다. 이 경우 사용하는 곡선의 종류가 generator g가 된다.

 어떤 generator를 사용할지 결정되고 나면 DH는 다음의 과정을 거쳐서 진행된다.


AliceBobEve
1GGG
2ab
3A=G(a)B=G(b)
4BAA, B
5s=B(a)s=A(b)

 우선 위의 표를 설명하자면, AliceBob은 서로 통신하기 원하는 사용자고, EveAliceBob 사이에 무슨 내용을 통신하는지 알기 원하는 도청자다. AliceBob은 공개된 공간에 있기 때문에 둘 사이의 통신하는 내용은 제삼자인 Eve도 알 수 있다. 그리고 위의 표에서 푸른색으로 표시된 문자는 자신이 생성한 데이터고, 붉은색으로 표시된 문자는 다른 사람이 생성하여 통신을 통해서 듣게 된 데이터다. 또한, 대문자와 소문자도 다른 의미를 가지는데, 대문자는 외부에 공개해도 되는 public 한 데이터고, 소문자는 자신만 알고 있어야 하는 private 한 데이터다. 따라서 붉은색 데이터는 모두 Eve도 알게 되고, 붉은색 데이터와 푸른색 데이터를 조합하여 AliceBob은 알지만, Eve는 모르는 데이터를 만드는 것이 DH의 목적이다.

1GGG

 우선 첫 단계는 어떤 generator를 사용할지 결정하는 단계다. generator를 결정하는 단계도 여러 방법으로 구현할 수 있지만 그다지 중요한 내용은 없으니 이번 포스트에서는 결정됐다고 가정하고 넘어가도록 하겠다.

2ab

 다음 단계에서 AliceBob은 각자 private key를 만든다. 이 private key를 어떻게 만드는가는 DH에서 중요한 요소가 아니지만, replay 공격을 막기 위해 private key는 랜덤하게 생성하는 것이 좋다.

3A=G(a)B=G(b)

 그리고 AliceBob은 generator G를 이용해서 각자 public key를 생성한다. 여기까지 진행되면 AliceBob은 자신의 private key와 public key만 알고 있고, Eve는 아무 키도 모르고 있는 상태가 된다.

4BAA, B

 다음 단계는 AliceBob이 각자 자신의 public key를 말하는 단계다. 이 단계를 지나면 AliceBob의 public key 두 개는 Eve를 포함한 모든 사람이 알고 있는 정보가 된다.

5s=B(a)s=A(b)

 다음 단계에서 AliceBob의 public key를 기반으로 generator를 만들어 자신의 private key를 사용하여 secret key를 만들고, BobAlice의 public key를 기반으로 generator를 만들어 자신의 private key를 사용하여 secret key를 만든다. 이때 public key를 사용해서 generator를 만드는 것은 어떤 generator를 사용하는가에 따라 다르다. modular exponentiation을 사용하는 경우 m는 기존의 generator의 m을 그대로 사용하고, 새로 받은 public key를 b로 사용한다. elliptic curve를 사용하는 경우 타원 곡선은 그대로 사용하고, public key를 시작점으로 계산한다.
 어쨌든 전달받은 public key를 기반으로 generator를 만들어 자신의 private key를 input으로 만들면, 그 결과 나온 secret key는 놀랍게도 AliceBob이 같은 값을 가지게 된다. 이는 아까 설명을 생략했던 generator의 몇 가지 수학적 성질 중 하나 덕분에 가능한 것이다. 이 secret key s를 알기 위해서는 최소 한 개의 private key가 필요하므로 Eve는 secret key s를 알 수 없다. 이제 AliceBob은 공유하지만, Eve는 모르는 값이 생겼으니, 이후 메시지는 전부 secret key s를 이용해서 암호화하면 된다.

 이상이 Diffie-Hellman Key Exchange를 이용하여 두 노드가 안전하게 비밀키를 만들어 공유하는 방법을 설명하였다. 사실 DH를 직접 구현해야 할 경우는 거의 없다. 하지만 네트워크 통신의 기본이 되는 내용이고, 많은 프로토콜이 컨넥션을 시작하는 단계에서 DH를 사용하기 때문에 서버 프로그래머라면 알아 두는 것이 좋다.

2014-12-18

file URI와 same-origin policy

 modern web browser에는 보안을 위한 여러 가지 기능들이 들어있다. 그중 가장 대표적인 기능이 same-origin policy다. same-origin policy 덕분에 (개발자 입장에서는 약간 짜증 나기는 하지만) 특별히 신경을 쓰지 않아도 보안에 관해 상당히 많은 부분을 커버할 수 있다. same-origin policy의 원칙은 매우 간단하다. 내 사이트가 다른 사이트에서 호스팅 되는 리소스에 의존하는 것을 금지해서, 내 사이트가 오염되거나 다른 사이트에 의해 공격당하는 것을 막는 것이다.

 same-origin인지 결정하는 것은 매우 간단한데 프로토콜, 호스트, 포트가 같은 URI를 same-origin이라고 판단한다. 요새는 대부분 개인 서버와 개인 도메인을 사용하기 때문에, 프로토콜과 포트까지 같은지 판단하는 것은 너무 빡빡한 기준이라고 생각할 수도 있지만, 워크스테이션이나 공용 서버에서 작업하는 일도 많다는 것을 생각하면 포트와 프로토콜까지 고려하는 것은 역시 상식적인 판단이라고 할 수 있다.

 브라우저에서 많이 쓰이는 http나 https에서는 이 규칙이 상식적이라고 할 수 있다. 문제는 file URI에서의 동작이다. file URI에 대해서는 어떤 URI를 same-origin이라고 할 것인지 정해진 것이 없고, 브라우저마다 알아서 자신이 옳다고 생각하는 방식으로 구현했다.

 우선 오페라는 file URI도 다른 URI와 같은 정책으로 처리한다. 따라서 file URI로 접근한 페이지에서는 읽기 권한이 있는 모든 파일을 읽어서 리소스로 활용할 수 있다. 어차피 파일은 OS가 access list로 보호하고 있으니, 로컬 파일에 대한 보안을 OS에 맡겨 버린 것이라고 할 수 있겠다. 약간 무책임한 것 같지만, 오페라의 구현이 가장 웹서버 없이 웹 페이지를 테스트하기 편한 구현이다.

 반면 크롬은 modern browser 중에 가장 빡빡한 규칙을 적용한다. 크롬에서는 file URI로 들어오는 요청에 대해 무조건 다른 origin인 것으로 처리한다. 심지어 host name을 명시적으로 입력해서 겉으로 보기에는 protocol, host name, port가 같아서 같은 origin으로 보이는 경우도 다른 origin인 것처럼 처리한다. 따라서 크롬에서는 웹 서버 없이 웹 페이지를 테스트하는 것이 불가능한 경우도 생긴다.
 이렇게 빡빡한 규칙을 적용한 이유는 찾을 수 없었다. 아마도 크롬이 Chrome OS와 코드를 공유하기 때문으로 추측된다. Chrome OS의 파일 권한이 앱별로 있는 것이 아니라서 다른 앱에서 로컬에 있는 파일을 읽는 것을 막기 위해서 file URI를 모두 다른 origin으로 취급해야 했던 게 아닐까 싶다.

 파이어폭스는 크롬과 오페라의 중간 정도 되는 정책을 취한다. 파이어폭스에서 file URI를 쓰면 현재 directory를 기준으로 자신보다 아래 경로에 있는 파일은 읽을 수 있도록 허용한다.1) 덕분에 파이어폭스도 웹 서버 없이 테스트하지 못하는 웹 페이지가 생기기도 한다. 하지만 크롬보다는 상황이 조금 나은 게 최소한 자신의 것이 확실한 (자기 디렉토리 아래에 있는)리소스는 마음껏 사용할 수 있다.

 크롬 브라우저에는 file URI를 이용하면서도 다른 파일을 같은 origin인 것처럼 사용하는 방법도 있다. 크롬을 실행할 때 --allow-file-access-from-files switch를 이용하면 file URI에서 모든 파일에 접근할 수 있도록 해준다. 주의해야 할 것은 기존에 실행시켰던 크롬이 백그라운드로 떠 있으면, 새로 크롬을 실행시켜도 백그라운드 프로세스로 떠 있던 크롬이 포크 되며 실행되기 때문에 switch가 반영되지 않는다. 따라서 switch를 주면서 크롬을 실행시킬 때는 기존의 프로세스가 전부 꺼졌는지 확인하고 실행해야 한다.
 파이어폭스도 file URI에 대한 동작을 변경할 수 있다. security.fileuri.strict_origin_policy 설정을 이용하면 오페라와 같이 모든 로컬파일을 리소스로 쓸 수 있게 된다.

 이런 식으로 크롬이나 파이어폭스를 이용해도 switch나 preference를 통해서 file URI로 로컬에 있는 페이지에 접근해서 테스트할 수도 있다. 하지만 테스트를 위해서가 잠시 값을 바꿔서 쓰는 게 아니라 기본값을 바꿔서 계속 사용하는 것은 좋은 방식이 아니라고 생각한다. same-origin policy라는 것의 목적은 보안을 위한 것이다. 보안을 위한 기능을 편의를 위해 끄고 쓰는 것은 열쇠 가지고 다니기 귀찮다고 문 열어놓고 다니는 것과 마찬가지의 행동이다.
 게다가 file URI를 통해 간단한 테스트는 가능할 수도 있지만, file URI는 http나 https와 동작이 달라서 실제 웹앱과 완벽하게 같은 테스트는 안된다. 따라서 실제로 웹 서버를 띄우는 것이 가장 확실한 방법이다.

 하지만 구태여 웹 페이지를 file URI로 테스트하려고 했던 것은, 현재 머신에 웹서버를 설치하기 싫거나, 설치할 수 없거나, 설정하기 귀찮은 경우일 것이다. 그런 상태에서 웹 서버를 설치하고 테스트하라고 말해봐야 보안에 구멍이 생기더라도 브라우저 설정을 바꾸는 것을 택할 것이다. 그런 사람들을 위해 간단하게 웹서버 돌리는 방법을 소개해 주겠다.

 어디든지 실행시키고 싶은 웹 앱의 최상단 경로에서 python3 -m http.server {port}를 입력하면 python으로 웹 서버가 실행된다. 아무 기능도 없지만, 최소한 file URI로 테스트하려던 수준의 웹 페이지라면 충분히 테스트할 수 있다.

1) https://developer.mozilla.org/en-US/docs/Same-origin_policy_for_file:_URIs