[OS] 43-1. Cryptography (1)

Park Yeongseo·2024년 4월 19일
0

OS

목록 보기
52/54
post-thumbnail

Operating Systems : Three Easy Pieces를 보고 번역 및 정리한 내용들입니다.

1. Introduction

이전 장들에서는 보안 목표, 보안 정책, 주체 식별을 위한 인증 메커니즘, 주체에 대한 권한 관리를 위한 인가 메커니즘에 대해서 배웠다. 하지만 이것들만으로는 충분하지 않다. OS는 자신이 돌아가는 기기에 대해서만 제어권을 가지며, 다른 기기에서 일어나는 일에 대해서는 제어할 수 없다.

그렇다면 OS가 접근 제어를 할 수 없는 자원들은 어떻게 보호할 수 있을까? 정답은 해당 자원들에서 일어날 수 있는 문제들에 미리 대비하는 것이다. 데이터에는 손실이 일어날 수 있고, 공격자는 데이터를 가로채 변형시킬 수도 있다. 만약 공격자가 자신이 획득한 데이터를 이해하지 못한다면, 그는 이 데이터를 가지고 어떤 일을 하거나, 데이터를 쉽게 변형할 수도 없을 것이다.

여기서 사용되는 기술을 가리켜 암호학(cryptography)라 하며, 이는 데이터를 제어가 가능한 방식으로, 한 형태에서 다른 형태로 변환하는 기술들을 말한다. 이 기술이 제대로 적용되면 공격자는 보호된 형식의 데이터를 가지고서 원래 데이터가 무엇인지를 알 수 없게 된다. 물론 우리가 해당 데이터를 다시 쓸 때에는 데이터를 역으로 원래의 형식으로 바꿀 수 있어야 하고, 이 과정은 공격자에게는 어려워야 한다.

하지만 암호화를 제대로 사용하는 것은 어렵고, 계산적으로도 비싸다. 따라서 암호화는 용도에 따라 선택적으로 사용되어야 하고, 그 구현 방법과 시스템에의 통합 방식에도 신경을 써서 사용해야 한다. 제대로 된 곳에서, 제대로 쓰이는 암호학은 보안에 큰 도움이 된다. 반대로, 잘못 쓰이고 잘못 구현된 경우는 전혀 도움이 되지 않을 뿐만 아니라, 오히려 해로울 수도 있다.

2. Cryptography

암호화에 대해서 다뤄볼 주제는 많지만, 여기서는 그 사용법들에 대해서만 간단히 살펴보도록 하자.

암호화의 기본 아이디어는 보통 키(key)를 사용하는 알고리즘(cipher)으로 데이터를 다른 형태로 변형시키는 것이다. 그 결과로 얻은 새로운 형식의 데이터는 기존 데이터와 전혀 다르게 생겼다.

이를 조금 더 형식화해보자. 시작은 데이터 PP(평문, plaintext), 키 KK, 암호화 알고리즘 E()E()부터다. 이것들을 가지고 얻게 될 결과는 CC이고, 이를 가리켜 암호문(ciphertext)이라 부른다.

C=E(P,K)C = E(P, K)

반대로, 또 다른 알고리즘을 이용해 암호화된 데이터를 기존 데이터로 되돌리기를 원할 수도 있다. 반대 방향으로의 변환은 CC를 입력으로, 복호화 알고리즘 D()D(), 키 KK를 이용해 이루어진다.

P=D(C,K)P = D(C, K)

암호화가 쓰이는 곳은 많지만, 일반적으로는 보내고 받는 메시지를 통해 이야기한다. 여기에서 원문 PP는 보내고자 하는 메시지, 암호문 CC는 해당 메시지의 보호된 버전으로, 실제 세상에 보내지는 메시지다.

암호화가 쓸모가 있어지려면 결정론적이어야 한다. 즉 특정 P,KP, K가 주어지면 그 결과인 CC가 결정론적으로 정해져야 한다. 반대로 특정 C,KC, K가 주어지는 경우에도 PP는 결정론적으로 정해진다. 많은 경우 E()E()D()D()는 같은 알고리즘이지만, 꼭 그래야만 하는 것은 아니다. KK를 알지 못하고 PP에서 CC를 추측하는 일은 어려워야 한다. 이는 불가능한 것이 최선이겠지만, "계산적으로 불가"함에 만족한다.

문제는 암호화가 결정론적이므로 공격자도 D()D()KK를 안다면 CC로부터 PP를 쉽게 알아낼 수 있다는 것이다. 심지어 E()E()D()D()가 약하다면 KK 없이도 PP를 알아낼 수 있다. 하지만 우리는 암호학자가 아니기 때문에, 상대적으로 적은, 무슨 이유에서인지 강하다고 알려진 일부 암복호화 알고리즘들을 사용할 수 밖에 없다. 따라서 E()E()D()D()를 비밀로 하는 일은 어렵고, 암호화를 통해 얻을 수 있는 이익들은 전적으로 키의 비밀성에 의존한다. 그래도 다행인 것은, 강한 암호화 알고리즘을 사용하고 키의 비밀성을 잘 지킬 수 있다면 여전히 그 효과들을 누릴 수 있다는 것이다(다만 실제 시스템에서 키의 비밀성을 유지하는 것은 그렇게 쉽지만은 않다.).

암호화는 인증에서도 사용할 수 있다. 예를 들어 나와 X만이 키 KK를 알고 있다고 해보자. 만약 어딘가로부터 암호화된 데이터를 얻고, 이 데이터를 키 KK로 성공적으로 복호화했다고 하자. 이 데이터는 내가 암호화한 것이 아니므로, X가 암호화한 것임을 알 수 있다.

이렇게 암호화와 복호화에 쓰이는 키가 동일한 암호를 가리켜 대칭 암호(symmetric cryptography, 대칭 키 암호)라고 한다. 오랫동안 이 대칭 암호는 암호학에서 쓰일 수 있는 유일한 형태라 생각되어 왔지만, 그렇지만은 않다.

3. Public Key Cryptography

위의 암호를 통한 인증의 예에서 한 가지 문제점을 찾을 수 있다. 암호화된 정보가 진짜인지를 검증하려면 해당 정보를 암호화하는 데 쓰인 키도 알아야 한다는 것이다. 이를 위해서는 사전에 키를 공유하는 절차가 필요하다.

잠시 우리가 우리의 소프트웨어를 구매한 모든 사용자들에게 스스로를 인증하려한다고 해보자. 오직 하나의 키만을 사용하는 방식은 불가능하다. 수억의 사용자들에게 동일한 키가 주어진다면, 각 사용자들은 해당 키를 가지고 마치 자신이 우리인 양 위장할 수 있기 때문이다.

이와 달리, 각 사용자에게 고유한 키를 주고 중앙에서 이를 관리하는 방법도 가능하겠다. 하지만 이 키들을 각 사용자들에게 비밀스럽게 전달하는 것도 쉽지 않고, 수없이 많아지는 키들을 관리하는 데에 드는 비용도 너무 커지게 된다.

그렇다면 암호화와 복호화에 쓰이는 키가 서로 다르다면 어떨까?

C=E(P,Kencrypt)P=D(C,Kdecrypt)\begin{aligned} C = E(P, K_{encrypt})\\ P = D(C, K_{decrypt}) \end{aligned}

여기서 KdecryptK_{decrypt}는 모두에게 알려주고, KencryptK_{encrypt}는 비밀스럽게 관리한다고 해보자. 이제 우리는 우리의 데이터를 비밀키로 암호화하고, 사용자들은 받은 데이터를 공개키로 검증할 수 있게 된다. 예를 들어 마이크로소프트가 OS 업데이트를 KencryptK_{encrypt}로 암호화하고 모든 사용자에게 보낸다고 해보자. 각 사용자는 이를 KdecryptK_{decrypt}로 복호화한다. 만약 사용자들이 받은 데이터가 제대로 포매팅된 소프트웨어 업데이트로 복호화될 수 있다면, 사용자는 이것이 마이크로소프트에 의해 만들어진 것임을 알 수 있다. 누구도 개인 키를 알지 못하기 때문에, 다른 누군가가 해당 업데이트를 만들었을 가능성이 없어지기 때문이다.

이러한 방식의 암호화를 가리켜 공개 키 암호화(public key cryptography)라고 말하는데, 암호화 키와 복호화 키 둘 중 하나가 완전히 공개되어 있기 떄문이다. 이렇게 모두에게 공개된 키는 공개 키(public key), 그리고 나머지의, 소유자만이 알고 있는 키는 개인 키(private key)라 부른다.

공개 키 암호화를 사용하면, 비밀 키를 안전하게 분배할 수 있어야 한다는 앞서의 문제가 해결될 수 있다. 여기서 개인 키는 따로 한 사람이 직접 만들어 보관하며, 다른 누구에게도 분배되지 않는다. 공개 키는 분배되어야 하지만 이 키는 누구든 알아도 괜찮기 때문에 비밀 키를 분배하는 것보다는 쉽게 분배할 수 있다.

이번에는 반대로 암호화 키를 공개 키로, 복호화 키를 개인 키로 사용한다고 해보자. 이 경우에는 암호화를 인증에 쓸 수는 없다. 누구든 공개 키로 메시지를 암호화할 수 있기 때문에, 메시지 송신자를 식별할 방법이 없기 때문이다. 다만 이 메시지는 개인 키를 가지고 있는 수신자만이 복호화할 수 있다. 이 경우 검증된 사용자만이 메시지를 확인할 수 있게 되기 때문에 통신의 비밀성을 지킬 수 있게 된다. 요컨대 개인 키를 통한 암호화는 인증, 공개 키를 통한 암호화는 비밀 통신에 사용될 수 있다.

인증과 비밀 통신을 모두 원하는 경우에는 어떨까? 이를 위해서는 두 개의 키 쌍이 필요하다. Alice와 Bob이 있다고 하자. Alice는 자신의 메시지를 Bob만이 받을 수 있게 하면서, 동시에 메시지를 통해 Bob에게 스스로를 인증하고 싶다. Alice, Bob이 모두 각자의 공개 키 쌍을 가지고 있고, 상대편의 공개 키도 알고 있다고 하자. Alice가 자신의 메시지를 개인 키로 암호화한다면 Bob은 해당 메시지가 Alice로부터 온 것임을 알 수 있지만, 이 메시지는 공개 키로 복호화할 수 있으므로 통신의 비밀성은 얻을 수 없다. 만약 Alice가 이 메시지를 Bob의 공개 키로 재 암호화한다면 통신의 비밀성도 얻을 수 있게 된다. 이 메시지를 복호화할 수 있는 사람은 Bob 밖에 없으며, 그렇게 얻은 메시지를 Alice의 공개 키로 또 복호화하면 이 메시지가 Alice로부터 온 것임도 알 수 있기 때문이다.

하지만 공개 키 암호화에는 단점이 있다. 대칭 키 암호화에 비해 많은 연산을 필요로 한다는 것이다. 공개 키 암호화를 수행하는 데에는 대칭 키 암호화보다 수백 배의 시간이 더 걸릴 수 있다. 따라서 공개 키 암호화를 모든 곳에 쓸 수는 없다. 적절하게 쓸 수 있을 곳을 찾아서 쓰는 게 중요하다.

다른 중요한 이슈도 있다. 위에서는 Alice와 Bob이 서로의 공개 키를 알고 있다고 가정했지만, Alice와 Bob은 원래 자신이 가지고 있는 키 쌍에 대해서만 알고 있고, 암복호화를 위해 상대편의 공개 키가 필요한 경우, 이를 상대에게 요청해야만 한다. 이때 Alice, Bob의 사이에 공격자 Eve가 있다고 하자. Eve는 Alice에게는 Bob인 척, Bob에게는 Alice인 척하며 메시지를 변조한다.

Alice가 Bob에 메시지를 보내기 위해 공개 키를 요청한다고 하자. 이때 둘 사이에 있는 Eve는 이 요청을 가로채고 자신의 공개 키를 Alice에게 보낸다. Alice는 이 공개 키로 자신의 메시지를 암호화하고, Bob이라 생각하는 Eve에게 보낸다. Eve는 이 메시지를 자신의 개인 키로 복호화하고 변조한다. 이후 Eve는 Alice인 척, Bob에게 공개 키를 요청하고, 해당 공개 키로 변조된 메시지를 암호화 해 Bob에게 보낸다. Bob은 해당 메시지가 자신의 개인 키로 복호화되므로 변조된 메시지가 Alice로부터 온 것이라 생각하게 된다.

위 문제를 중간자 공격(MITM, Man In The Middle)이라고 하며, 이를 해결하기 위한 방법에 대해서는 이후의 분산 시스템 보안을 다루는 장에서 논의하게 될 것이다.

4. Cryptographic Hashes

암호화는 데이터 무결성을 지키는 데에 쓸 수 있다. 변형된 암호문은 제대로 복호화되지 않을 것이기 때문이다. 이러한 무결성을 확인하는 데 드는 비용은 전체 데이터를 암호화하지 않고, 데이터를 해싱한 후 그 해시값만 암호화함으로써 줄일 수 있다. 하지만 아무 해시 함수나 사용할 수 있는 것은 아니다. 해시 함수는 본질 상 해시 충돌의 가능성을 가지고 있기 떄문이다. 만약 공격자가 정상적인 비트 패턴과 동일한 해시값을 갖는 비트 패턴을 발견할 수 있다면, 데이터 무결성은 지킬 수 없게 된다.

데이터 무결성을 지키기 위해서는 암호학적 해시 함수(cryptographical hash function)를 사용해야 한다. 이는 특별한 종류의 해시 함수로, 다음과 같은 여러 중요한 속성들을 가진다.

  • 같은 해시 값을 가지는 서로 다른 입력을 찾는 것이 계산적으로 불가능해야 한다.
  • 입력에서 일어난 변화는 결과 해시값의 예상할 수 없는 변화로 이어져야 한다.
  • 해시값을 통해서만 입력의 어떤 성질을 추측하는 것은 계산적으로 불가능해야 한다.

통신 비밀은 신경 쓰지 않고 데이터 무결성에만 관심이 있다면, 암호학적 해시 함수의 위 성질들을 이용할 수 있다. 데이터를 암호학적 해시 함수로 해싱하고, 암호화한 후, 이렇게 암호화된 해시와 암호화되지 않은 데이터를 함께 보내자. 이때 복호화된 해시와 암호화되지 않은 데이터의 해시값 사이의 불일치가 발견되면, 중간에 공격자가 메시지를 가로채 조작했음을 알 수 있게 된다.

이를 조금 더 형식화해보자. 평문 PP와 해싱 알고리즘 H()H()를 가지고 있다고 해보자. 꼭 키가 있을 필요는 없다.

S=H(P)S = H(P)

암호학적 해시도 해시이므로, 일반적으로 SSPP보다 훨씬 짧을 것이라 기대할 수 있다. 이에 따라 서로 다른 평문 PPPP'이 같은 SS를 가질 수도 있다. 하지만 암호학적 해시 함수의 특성을 생각하면 공격자가 이러한 충돌을 이용하기는 쉽지 않다. SSPP를 모두 알더라도, 같은 해시값 SS를 갖는 평문 PP'를 찾기는 어려워야 한다. 물론 평문 PP'의 해시값 SS'가 어떻게 될지는 PP'에 해시 알고리즘을 직접 적용해보면 쉽게 알 수 있다. 하지만 PP'가 평문 PP와 1비트만 다르더라도 그 결과는 예상할 수 없을 만큼 달라지며, 따라서 SS로부터 같은 그것과 같은 해시값을 갖는 PP'를 찾는 일은 어렵다.

암호학적 해시는 암호화된 데이터의 무결성을 보장하는 목적 외에도 사용될 수 있다. 예를 들면 앞서 인증 파트에서와 같이 패스워드에 솔트를 추가하고 해싱해 저장할 때 유용하게 쓰일 수 있고, 저장된 파일이 변형됐는지를 결정하는 데에도 쓰일 수 있다.

이는 프로세스가 요청을 제출하기 전에 특정한 작업들을 수행하도록 할 때에도 쓰일 수 있다. 이를 가리켜 작업 증명(proof of work)이라 하는데, 여기서 요청 제출자는 특정 암호학적 해시를 이용해 특정 값으로 해싱되는 요청을 보내야 한다. 그 특정 값을 찾기 위해서는 많은 연산이 일어나야 하고, 많은 요청들도 보내져야 한다. 각 해시 작업에는 시간이 걸리기 때문에 적절한 요청을 보내는 데에는 일정량의 작업이 필요해지고, 따라서 이 방법을 이용하면 특정 요청을 보내기 전에 일정량의 작업을 하도록 강제할 수 있다. 이 방식은 원래 스팸 메일 방지를 위해 만들어졌지만, 이후 비트코인 등 암호화폐에도 쓰이게 된다.

5. Cracking Cryptography

암호화를 사용하는 시스템은 어떻게 공격할 수 있을까? 암호화 알고리즘을 직접 만든 경우라면 걱정해야 하겠지만, 표준 알고리즘들 중 하나를 사용했다면 암호화 알고리즘 자체에 문제가 있을까 고민할 필요는 거의 없다. 현대 표준 알고리즘들의 경우, 소수의 중요치 않은 예외들을 제외하고는 키를 획득하지 않고 암호화된 데이터를 읽는, 알려진 방법은 없기 때문이다. 표준 알고리즘을 쓴다면, 이 알고리즘들이 뚫릴까 걱정할 필요는 거의 없다.

그렇다면 어떻게 암호화를 공격할 수 있을까? 한 가지 방법은 시스템 내 소프트웨어의 결함을 이용하는 것이다. 이 경우에도 암호화 자체에는 별 영향을 줄 수 없지만, 이를 이용하면 키, 혹은 암호 관리에 있어서의 또 다른 취약점을 찾을 수 있을지도 모른다. 키를 만들고 사용할 때의 소프트웨어 결함이 흔히 일어나는 문제이며, 분산 환경에서 키를 공유하기 위해 쓰이는 방법들도 이용될 수 있는 취약점이다.

Peter Gutmann은 잘못된 암호 관리에서 자주 일어나는 문제들의 종류에는 무엇이 있는지 조사했는데, 다음과 같은 것들이 그 사례들이다.

  • 여러 사람들이 공유하는 소프트웨어에서의 비밀키 분배
  • 키의 평문을 네트워크로 잘못 전송하는 경우
  • 키를 너무 적은 선택지에서 선택(이론적으로 더 많은 선택지가 있음에도)

더 최근에는 원격 컴퓨터의 메모리에서 OpenSSL 세션의 키를 얻을 수 있는, 하트블리드(Heartbleed) 공격도 있었다. 암호화 알고리즘 자체와 그 구현, 키 선택 과정에는 문제가 없었지만, 공격자는 이 공격을 통해 전체 세션을 복호화할 수 있었고, HTTPS를 사용하는 모든 사이트의 1/41/4에서 1/21/2의 트래픽을 읽을 수 있었다.

또 다른 방법으로는 키를 추측하는 것이다. 이 방법 또한 암호화 자체를 공격하는 것은 아니다. 암호화 알고리즘은 키를 알지 못하는 사람들이 비밀을 얻는 것을 방지한다. 그러나 키를 아는 사람이라면, 복호화하는 것은 그리 어려운 문제가 아니다.

공격자는 간단하게 가능한 모든 키 조합을 시도해볼 수 있다. 이런 브루트 포스 공격을 방지하기 위해서 키는 충분히 길어야 한다. 예를 들어 AES는 최소 128비트의 키를 사용하는데, 브루트 포스 공격의 경우, 공격자는 평균 21272^{127}번의 추측을 해야한다. 이는 충분히 많고, 시간도 오래 걸린다. 하지만 만약 시스템 결함으로 인해 이렇게 가능한 키들 중 극히 일부만을 키 후보로 삼게 된다면, 브루트 포스 공격은 쉽게 성공할 수 있다.

공개 키 시스템의 경우 2K에서 4K 비트의 긴 키를 사용한다는 얘기를 들어본 적이 있을 것이다. 이렇게 긴 키를 사용하면 안전할 것처럼 보인다. 하지만 사실 이 2K ~ 4K 비트로 만들 수 있는 키들 중 임의로 하나를 선택해 사용할 수 있는 것은 아니고, 이 중 극히 일부만 키로 사용할 수 있다. 그 이유는 공개 키와 개인 키는 서로 수학적인 연관성을 가지고 있어야 하며, 이러한 연관성을 만족하는 키 쌍이 그렇게 많지는 않기 때문이다. 또한 공개 키로부터 개인 키를 추측해내는 것도 어려워야 한다는 제약을 생각한다면 그 수는 더 줄어든다. 공개 키와 개인 키가 서로 수학적인 관계에 있으므로, 원리적으로는 공개 키를 가지고 개인 키를 찾아낼 수 있으며, 2K ~ 4K의 키를 사용하는 것은 이를 계산적으로는 불가능하게 만들기 위한 결과일 뿐이다.

지금까지 키가 안전하다, 그렇지 않다에 대해 얘기했지만, 이것들도 사실 개인 키를 비밀스럽게 관리할 때에만 할 수 있는 얘기다. 지금까지 이야기해 온 것을 생각하면 개인 키를 비밀스럽게 관리해야 한다는 게 당연하게 들릴지도 모른다. 하지만 공개 키 방식을 사용하는 임베디드 장치의 제작자들이 특정 모델의 장치에 동일한 개인 키를 너무 자주 썼고, 이 때문에 개인 키는 더 이상 "개인" 키가 아니게 된 적이 있었다. 2016년 9월, 한 연구에서는 450만 개의 임베디드 장치들이 이러한 개인 키를 사용하고 있음을 발견했다.

요컨대, 암호화 시스템을 공격하는 것은 보통 "키"에 대해 알아내는 것일 뿐이다. 암호화를 통해 얻을 수 있는 이익은 전적으로 키의 비밀성에 의존한다.

마지막 챕터인 줄 알았는데, 사실 한 챕터가 더 남아있었다.

0개의 댓글