암호화
고친 사람 github-actions[bot]
암호화는 데이터를 열쇠 없이는 읽을 수 없는 모양으로 바꿔 둡니다. 열쇠를 쥔 쪽만 원래 내용으로 되돌립니다. 데이터가 지나가는 길과 머무는 장비를 전부 믿을 수는 없으니, 내용 자체를 잠가서 보냅니다.
쉽고 빠른 이해
암호화는 데이터를 열쇠 가진 쪽만 읽을 수 있게 잠가 둡니다. 노트북을 잃어버려도 그 안의 파일을 주운 사람이 열지 못하는 것이 이 잠금이 해 주는 일입니다.
이게 없으면 데이터를 손에 넣는 것과 읽는 것이 같은 말이 됩니다. 케이블을 타고 가는 동안에도, 디스크에 누워 있는 동안에도 내용이 드러난 채입니다.
어떻게 도는가:
- 잠그는 계산에 원래 데이터와 열쇠를 같이 넣습니다
- 뜻을 알아볼 수 없는 바이트 덩이가 나옵니다
- 같은 열쇠나 짝이 되는 열쇠를 넣어야 원래 데이터가 돌아옵니다
대가도 있습니다. 잠그고 여는 계산에 시간이 듭니다. 그리고 지켜야 할 비밀이 데이터에서 열쇠로 옮겨 갑니다. 열쇠가 새면 잠근 것이 소용없습니다. 열쇠를 잃으면 내 데이터도 못 읽습니다.
그래서 아무거나 잠그지는 않습니다. 나중에 원문을 도로 읽어야 하는 것만 잠급니다. 맞는지만 확인하면 되는 비밀번호는 잠그지 않고 뭉갭니다.
상세
자물쇠가 달린 상자에 편지를 넣어 부친다고 해 봅니다. 배달하는 사람은 상자를 들여다볼 수 없고, 열쇠를 가진 받는 사람만 편지를 꺼냅니다.
다만 상자는 힘으로 부수면 열립니다. 잠긴 데이터에는 부술 곳이 없어서, 열쇠를 알아내는 것 말고는 들어갈 길이 없습니다.
암호화는 읽을 수 있는 데이터를 정해진 계산 절차와 키에 함께 넣어, 뜻을 알아볼 수 없는 바이트 덩이로 바꾸는 일입니다. 앞에서 열쇠라고 부른 것이 이 키입니다. 바꾸기 전의 데이터를 평문, 바꾼 뒤의 데이터를 암호문이라고 부릅니다. 암호문을 평문으로 되돌리는 일은 복호화입니다.
flowchart TD
P["평문"] -->|"암호화 · 키를 함께 넣는다"| C["암호문"]
C -->|"복호화 · 같은 키를 다시 넣는다"| P
그림의 화살표가 한 바퀴를 돌아 평문으로 돌아옵니다. 되돌릴 수 있다는 것이 암호화의 조건입니다. 남이 못 읽게만 하려면 지워 버리면 그만입니다. 나중에 내가 다시 읽어야 하기 때문에 지우는 대신 잠급니다.
믿을 수 없는 구간을 지나는 데이터
데이터는 자기 손을 떠나 여러 곳을 지납니다. 사무실 공유기와 통신사 장비를 거치고, 클라우드 업체의 디스크에 눕고, 백업 테이프에 복사됩니다.
그 구간과 장비를 하나하나 믿을 수 있는지는 확인할 방법이 없습니다. 암호화는 그 많은 믿음을 키 하나를 지키는 일로 줄여 줍니다. 키만 내 손에 있으면 나머지 구간은 넘겨다봐도 읽을 것이 없습니다.
키가 지는 비밀
암호를 만드는 계산 절차는 대개 공개돼 있습니다. 여러 사람이 오래 두들겨 보고도 안 깨진 절차만 쓸 만하다고 인정받기 때문입니다. 절차를 숨기면 그 검증을 받을 길이 없어집니다.
그래서 비밀은 절차가 아니라 키 하나로 몰립니다. 같은 절차라도 키가 다르면 전혀 다른 암호문이 나옵니다.
키를 모르는 쪽에 남은 길은 하나뿐입니다. 키는 정해진 길이의 비트 열입니다. 그 길이로 만들 수 있는 값을 하나씩 다 넣어 보는 것입니다. 이것을 무차별 대입 공격이라고 합니다.
그래서 키가 길수록 넣어 봐야 할 경우의 수가 불어납니다. 다 해보는 데 걸리는 시간도 감당할 수 없게 커집니다.
같은 키로 잠그고 여는 방식
대칭키 암호는 한쪽 끝에서 잠글 때 쓴 키를 다른 쪽 끝에서 그대로 써서 엽니다. 잠그는 사람과 여는 사람이 같은 키 하나를 나눠 갖습니다.
계산이 단순해서 큰 데이터를 다룰 때도 부담이 적습니다. 디스크 한 장이나 통신 한 세션처럼 양이 많은 것은 대개 대칭키로 잠급니다.
문제는 키를 어떻게 건네느냐입니다. 상대에게 키를 먼저 보내야 합니다. 그런데 그 키를 보내는 길이 안전하다면 애초에 데이터를 그냥 보내도 됐을 것입니다. 이 맞물림을 키 교환 문제라고 부릅니다.
서로 다른 두 키를 쓰는 방식
공개키 암호는 키를 한 쌍으로 만듭니다. 한쪽으로 잠근 것은 다른 쪽으로만 열립니다.
한 쌍 중 하나는 아무에게나 뿌려도 되는 공개키입니다. 다른 하나는 혼자 쥐고 있는 개인키입니다.
나에게 보낼 것이 있는 사람은 내 공개키로 잠급니다. 그렇게 잠긴 것은 내 개인키로만 열립니다. 가는 길에 누가 가로채도 읽지 못합니다.
flowchart TD
subgraph 같은키하나["같은 키 하나 · 대칭키 암호"]
S1["평문"] -->|"키로 잠근다"| S2["암호문"]
S2 -->|"같은 키로 연다"| S3["평문"]
end
subgraph 키한쌍["키 한 쌍 · 공개키 암호"]
A1["평문"] -->|"공개키로 잠근다"| A2["암호문"]
A2 -->|"개인키로만 열린다"| A3["평문"]
end
같은키하나 ~~~ 키한쌍
위 그림은 잠글 때와 열 때가 같은 키를 가리킵니다. 아래 그림은 잠근 키와 여는 키가 다릅니다.
이렇게 하면 키를 미리 건네지 않아도 됩니다. 대신 계산이 대칭키 방식보다 훨씬 느립니다. 그래서 큰 데이터를 통째로 이 방식으로 잠그지는 않습니다.
두 방식을 겹쳐 쓰는 꼴
실무에서 오가는 통신은 둘을 이어 붙여 씁니다. 받는 쪽이 공개키를 먼저 알립니다. 보내는 쪽은 키를 건네는 짧은 구간만 공개키로 처리합니다. 본문은 대칭키로 잠급니다.
이때 쓰는 대칭키는 한 번의 대화에만 쓰고 버립니다. 이것을 세션 키라고 합니다. 대화가 끝나면 버리므로 나중에 그 키가 새도 다음 대화는 영향을 안 받습니다.
sequenceDiagram
participant 보내는쪽 as 보내는 쪽
participant 받는쪽 as 받는 쪽
받는쪽-->>보내는쪽: 공개키를 알린다
Note over 보내는쪽: 세션 키를 하나 만든다
보내는쪽->>받는쪽: 공개키로 잠근 세션 키
Note over 받는쪽: 개인키로 세션 키를 꺼낸다
보내는쪽->>받는쪽: 세션 키로 잠근 본문
그림에서 공개키가 하는 일은 세션 키 하나를 건네는 것뿐입니다. 본문이 아무리 길어도 그 부분은 대칭키가 맡습니다.
암호화가 주지 않는 보장
암호화가 주는 것은 하나입니다. 키 없는 쪽이 내용을 못 읽는다는 것입니다. 그 밖의 것은 따로 챙겨야 합니다.
첫째, 내용이 도중에 바뀌지 않았는지는 말해 주지 않습니다. 암호문 몇 바이트를 뒤집어 놓는 장난은 암호화만으로 못 막습니다. 바뀌지 않았음을 확인해 주는 것은 메시지 인증 코드나 전자 서명이 하는 일입니다.
둘째, 보낸 쪽이 누구인지도 말해 주지 않습니다. 내 공개키는 누구나 가져다 쓸 수 있습니다. 잠겨서 왔다는 것만으로는 보낸 사람을 알 수 없습니다. 그 확인은 인증이 맡습니다.
셋째, 내용 말고는 대부분 드러납니다. 누가 누구와 언제 얼마나 주고받았는지는 암호문 바깥에 그대로 남습니다.
넷째, 쓰는 동안은 평문입니다. 프로그램이 값을 계산하려면 메모리에서 풀어야 합니다. 그 순간에는 잠금이 걸려 있지 않습니다.
해시·인코딩과 가르는 선
암호화·해시·인코딩 셋은 자주 섞여 쓰입니다. 갈라 두면 남의 코드를 읽을 때 헷갈릴 일이 줄어듭니다.
해시 함수는 데이터를 정해진 길이의 값으로 뭉갭니다. 되돌리는 길이 없는 것이 목적이라, 되돌리려고 만든 암호화와는 쓰임이 반대입니다. 비밀번호를 보관할 때 암호화가 아니라 해시를 쓰는 이유가 이것입니다. 서버도 원래 비밀번호를 알 이유가 없습니다.
인코딩은 데이터를 다른 표기법으로 옮겨 적는 일입니다. 키가 없고 규칙만 알면 누구나 되돌리니 숨기는 일과는 상관이 없습니다.
셋을 한자리에 놓으면 이렇게 갈립니다. 가르는 기준은 되돌릴 수 있나 하나입니다.
| 되돌릴 수 있나 | 키가 필요한가 | 무엇을 하려고 | |
|---|---|---|---|
| 암호화 | 된다 | 필요하다 | 키 없는 쪽이 못 읽게 |
| 해시 | 안 된다 | 없어도 된다 | 같은 값인지 확인하려고 |
| 인코딩 | 된다 | 필요 없다 | 다른 형식으로 옮겨 적으려고 |
오가는 데이터와 놓여 있는 데이터
암호화를 거는 대목은 크게 둘로 나뉩니다. 네트워크를 타고 움직이는 동안 거는 것과, 디스크에 누워 있는 동안 거는 것입니다.
움직이는 동안 거는 것은 대개 TLS(Transport Layer Security, 전송 계층 보안)가 맡습니다. 연결을 맺을 때 세션 키를 정합니다. 그 뒤로 오가는 바이트는 그 키로 잠급니다.
누워 있는 동안 거는 것은 디스크나 데이터베이스, 백업 파일이 대상입니다. 여기서는 잠그는 것보다 키를 어디에 둘지가 더 성가십니다. 잠긴 데이터 바로 옆에 키를 같이 두면 잠근 뜻이 없어집니다.
flowchart TD
M["앱 메모리 · 평문"] --> N["네트워크 구간 · TLS 로 잠김"]
N --> D["디스크 · 데이터베이스 · 잠김"]
D --> B["백업 파일 · 잠김"]
평문으로 있는 구간은 앱 메모리 하나뿐입니다. 데이터가 손을 떠난 뒤로는 구간마다 잠긴 채로 지납니다.
백엔드 코드가 암호화를 만나는 때
암호 절차를 직접 짜는 일은 거의 없습니다. 검증된 라이브러리를 부르는 것이 보통입니다. 손으로 짠 암호는 어디가 샜는지도 모른 채 새기 때문입니다.
실제로 정하게 되는 것은 키를 어디에 두고 언제 바꾸느냐입니다. 소스 코드나 설정 파일에 키를 적어 두면 코드를 보는 사람이 전부 키를 갖게 됩니다. 그래서 키는 시크릿 관리로 따로 뺍니다.
그리고 잠글 것과 뭉갤 것을 가릅니다. 나중에 원문이 필요한 것은 암호화합니다. 맞는지만 확인하면 되는 비밀번호는 해시합니다.
관련 항목
암호화가 쓰는 계산 절차의 갈래
대칭키 암호 · 공개키 암호 · 블록 암호 · 스트림 암호 · AES · RSA · ChaCha20 · 타원 곡선 암호 · 운용 모드
암호화에 쓰는 열쇠를 만들고 지키는 장치
키 · 암호화 키 · 공개키 · 개인키 · 세션 키 · 키 교환 · 키 유도 · 키 회전 · 키 관리 서비스 · 하드웨어 보안 모듈 · 봉투 암호화 · 시크릿 관리
암호화를 실어 나르는 프로토콜
TLS · HTTPS · QUIC · SSH · VPN · 종단 간 암호화
암호화가 못 주는 보장을 메우는 수단
해시 함수 · 메시지 인증 코드 · 전자 서명 · 인증서 · 인증 기관 · 무결성 · 인증 암호
암호화와 헷갈리는 이웃 개념
해싱 · 인코딩 · 체크섬 · 난독화 · 압축 · 토큰화
저장해 둔 데이터에 암호화를 거는 방식
디스크 암호화 · 데이터베이스 암호화 · 투명 데이터 암호화 · 필드 수준 암호화 · 백업과 복구
암호화를 깨뜨리려는 공격
무차별 대입 공격 · 중간자 공격 · 재전송 공격 · 사이드 채널 공격 · 레인보우 테이블 · 다운그레이드 공격
암호화가 입력으로 받는 무작위 값
암호화가 속하는 상위 분야와 이웃 통제
보안 엔지니어링 · 인증 · 인가 · 접근 제어 · 최소 권한 · 감사 로그
비밀번호를 다룰 때 암호화 대신 쓰는 수단
다른 이름: encryption · 인크립션 · 암호화하기