SSH(Secure Shell) 프로토콜은 네트워크 상에서 보안을 유지한 상태로 데이터 전송을 가능하게 하는 프로토콜이다. 프로토콜의 기본 흐름은 주로 클라이언트와 서버 간에 암호화된 세션을 생성하고 유지하는 데 초점을 맞춘다. 여기서는 SSH 프로토콜의 세부적인 흐름을 단계별로 설명하겠다.

1. 클라이언트와 서버 간 연결 수립 (Connection Establishment)

SSH 프로토콜은 전송 계층 보안(TCP/IP)을 바탕으로 클라이언트와 서버 간에 암호화된 연결을 설정한다. 이 단계는 주로 다음 두 가지로 구성된다.

1.1 TCP 연결 생성

클라이언트는 서버의 SSH 포트(기본적으로 22번 포트)로 TCP 연결을 시도한다. 서버는 해당 요청을 수신하고 응답하며, 이로 인해 양쪽 간의 기본 통신 경로가 생성된다.

1.2 SSH 버전 교환

서버와 클라이언트는 서로가 지원하는 SSH 프로토콜의 버전을 교환한다. 클라이언트가 서버에 TCP 연결을 성공적으로 시도하면 서버는 자신이 지원하는 SSH 프로토콜 버전 정보를 클라이언트에 전송한다. 클라이언트는 해당 정보를 기반으로 서버와 통신할 버전을 결정한다.

서버가 클라이언트에게 보내는 메시지 형식은 다음과 같다:

SSH-2.0-<Software_Version_Information>

클라이언트는 자신이 지원하는 SSH 버전을 다음과 같은 형식으로 응답한다:

SSH-2.0-<Client_Software_Version_Information>

이렇게 서로의 SSH 버전 정보를 교환함으로써, 양측은 사용할 SSH 프로토콜의 버전을 확정한다.

2. 암호화 및 세션 키 협상 (Key Exchange)

클라이언트와 서버 간의 안전한 데이터 전송을 보장하기 위해, SSH 프로토콜은 세션 키를 협상하여 데이터를 암호화한다. 이 과정은 주로 Diffie-Hellman(DH) 키 교환 방식을 기반으로 수행되며, 세션 키는 각 통신 세션마다 새롭게 생성된다.

2.1 Diffie-Hellman 키 교환

Diffie-Hellman(DH) 알고리즘을 통해 클라이언트와 서버는 서로의 비밀 값을 교환하고, 이를 통해 공통의 세션 키를 생성한다. 이때 클라이언트와 서버는 각각 임의의 값 ab를 생성하고, 다음의 수식을 통해 공통 키를 계산한다:

g^{a} \mod p
g^{b} \mod p

여기서 g는 사전에 합의된 원시수, p는 큰 소수이다. 클라이언트와 서버는 서로에게 g^{a} \mod pg^{b} \mod p 값을 전송하며, 이 두 값을 사용하여 공통의 세션 키 K를 다음과 같이 계산한다:

K = (g^{b} \mod p)^{a} \mod p = (g^{a} \mod p)^{b} \mod p

이렇게 클라이언트와 서버는 세션 키 K를 공유하게 된다. 이 세션 키는 이후에 데이터 전송 시 암호화와 복호화에 사용된다.

2.2 키 교환 완료 및 암호화 알고리즘 선택

키 교환이 완료되면, 클라이언트와 서버는 사용할 암호화 알고리즘을 협상한다. 여기서 선택된 암호화 알고리즘은 주로 AES, 3DES 등의 대칭 키 암호화 방식이 사용된다. 이후의 데이터 전송은 이 알고리즘으로 암호화된 형태로 이루어진다.

3. 서버 인증 (Server Authentication)

SSH 프로토콜은 클라이언트가 서버를 인증할 수 있도록 다양한 인증 방식을 제공한다. 일반적으로 서버는 자신의 공개키를 클라이언트에 제공하고, 클라이언트는 서버의 신뢰성을 확인한다.

3.1 서버 공개키 제공

서버는 클라이언트에게 자신의 공개키를 제공한다. 이 공개키는 서버의 식별 정보로, 클라이언트가 서버의 진위 여부를 검증하는 데 사용된다. 클라이언트는 서버의 공개키를 로컬에 저장된 신뢰할 수 있는 공개키 리스트와 비교하여 서버가 신뢰할 수 있는 서버인지 확인한다.

공개키는 다음과 같은 형식으로 제공된다:

K_{pub} = (n, e)

여기서 K_{pub}는 서버의 RSA 공개키, n은 모듈러스, e는 공개 지수이다. 클라이언트는 이 공개키를 통해 서버가 이전에 신뢰된 서버인지 판단할 수 있다.

3.2 서버 서명 검증

클라이언트는 서버가 전송한 공개키에 서명을 요청한다. 서버는 자신의 비밀키 K_{priv}로 서명된 메시지를 클라이언트에게 전송하며, 클라이언트는 서버의 공개키 K_{pub}를 이용해 서명을 검증한다. 이 과정은 서버의 신원을 확증하는 데 중요한 역할을 한다.

서명 검증 과정은 다음 수식을 따른다:

\text{verify}(K_{pub}, \text{signature}, \text{message})

이 과정에서 클라이언트가 서버의 공개키로 서명을 성공적으로 검증하면 서버의 신원이 확인된 것이며, 그렇지 않으면 세션이 중단된다.

4. 클라이언트 인증 (Client Authentication)

서버는 클라이언트를 인증하여 클라이언트가 실제로 접근 권한이 있는 사용자임을 확인해야 한다. 이 단계에서는 다양한 인증 방식이 사용될 수 있으며, 가장 일반적인 방식으로는 비밀번호 인증과 공개키 인증이 있다.

4.1 비밀번호 인증

가장 기본적인 인증 방식으로, 클라이언트는 서버에 사용자 이름과 비밀번호를 전송하여 인증을 수행한다. 서버는 해당 비밀번호를 로컬에서 저장된 사용자 정보와 비교하여 클라이언트의 신원을 확인한다.

4.2 공개키 인증

비밀번호 인증보다 더 안전한 방식인 공개키 인증은 클라이언트가 사전에 생성한 공개/비밀키 쌍을 사용한다. 클라이언트는 자신의 공개키를 서버에 미리 등록하고, 세션 중 클라이언트는 비밀키를 이용해 서명을 만들어 서버에 전송한다.

서버는 클라이언트의 공개키를 사용하여 서명을 검증하고, 서명이 올바를 경우 클라이언트를 인증한다. 이 과정은 다음과 같은 수식으로 요약될 수 있다:

\text{sign}(K_{priv}, \text{message}) \quad \text{and} \quad \text{verify}(K_{pub}, \text{signature}, \text{message})

서명이 성공적으로 검증되면 클라이언트는 인증된 상태가 되며, 인증에 실패하면 세션이 중단된다.

5. 암호화된 세션 생성 (Encrypted Session Establishment)

클라이언트와 서버가 서로를 인증한 후, SSH는 안전한 데이터 전송을 위해 암호화된 세션을 설정한다. 이 세션은 앞서 생성한 세션 키를 기반으로 데이터를 암호화하여 전송한다.

5.1 암호화된 데이터 전송

세션이 설정되면 클라이언트와 서버는 서로 데이터를 주고받을 수 있다. 이때 모든 데이터는 사전에 협상한 암호화 알고리즘(AES, 3DES 등)을 사용해 암호화된 형태로 전송된다. 데이터 전송은 주로 패킷 단위로 이루어지며, 각 패킷에는 다음과 같은 정보가 포함된다:

\text{Packet} = \{\text{Data}, \text{MAC}, \text{Padding}\}

여기서 \text{Data}는 실제 전송될 데이터, \text{MAC}는 메시지 인증 코드로 데이터의 무결성을 보장하며, \text{Padding}은 암호화 블록의 길이를 맞추기 위한 추가 정보이다.

5.2 데이터 무결성 검증

전송된 데이터가 도중에 변조되지 않았음을 확인하기 위해 메시지 인증 코드(MAC)를 사용한다. 데이터는 전송 전에 MAC 값을 계산하여 패킷에 포함하며, 수신 측은 다시 MAC 값을 계산하여 전송된 값과 일치하는지 확인한다. MAC은 주로 다음과 같이 계산된다:

\text{MAC}(K, \text{Data})

여기서 K는 세션 키이며, \text{Data}는 전송할 데이터를 의미한다. 서버와 클라이언트는 동일한 세션 키를 사용하므로, MAC 값이 일치하면 데이터가 변조되지 않았음을 확인할 수 있다.

6. 대칭키 암호화 (Symmetric Encryption)

SSH 프로토콜에서 데이터 전송은 대칭키 암호화를 사용하여 이루어진다. 대칭키 암호화 방식은 클라이언트와 서버가 동일한 세션 키를 사용해 데이터를 암호화하고 복호화하는 방식이다. 세션 키는 앞서 Diffie-Hellman 키 교환을 통해 생성되었다.

6.1 대칭키 암호화 방식

대칭키 암호화는 빠르고 효율적이기 때문에 SSH 프로토콜에서 주로 사용된다. 일반적으로 SSH에서는 다음과 같은 암호화 알고리즘이 사용된다:

이때 대칭키 암호화는 데이터가 전송되기 전에 다음 수식을 통해 암호화된다:

C = E(K, M)

여기서 C는 암호화된 데이터(암호문), K는 세션 키, M은 원본 데이터(평문)이며, E는 암호화 함수이다.

6.2 대칭키 복호화

데이터를 수신한 측은 대칭키를 사용하여 암호화된 데이터를 복호화한다. 복호화 과정은 암호화 과정의 역순으로 진행되며, 복호화 수식은 다음과 같다:

M = D(K, C)

여기서 D는 복호화 함수이며, M은 복호화된 원본 데이터이다. 대칭키 암호화는 동일한 키를 사용하여 암호화와 복호화를 모두 처리하기 때문에 양측이 동일한 세션 키를 보유하고 있어야 한다.

7. 키 재협상 (Key Re-Exchange)

SSH 프로토콜에서는 일정한 주기마다 세션 키를 재협상하여 보안성을 높이는 메커니즘을 가지고 있다. 장시간 세션을 유지할 경우, 세션 키가 도중에 노출될 위험을 방지하기 위해 새로운 세션 키를 주기적으로 생성하고 교환한다.

7.1 키 재협상 과정

키 재협상은 초기 연결 수립과 비슷한 방식으로 진행되며, 클라이언트와 서버는 새로운 Diffie-Hellman 키 교환 과정을 거친다. 이때 새로운 g, a, b 값을 생성하고, 이를 통해 새로운 세션 키를 계산한다:

g^{a'} \mod p
g^{b'} \mod p

클라이언트와 서버는 새로운 a', b' 값을 교환하며, 이를 기반으로 새로운 세션 키 K'를 다음과 같이 계산한다:

K' = (g^{b'} \mod p)^{a'} \mod p

7.2 새로운 세션 키로 전환

새로운 세션 키 K'가 생성되면, 양측은 이전 세션 키를 더 이상 사용하지 않고, 새로 생성된 세션 키로 데이터를 암호화하기 시작한다. 이로써 세션의 보안성을 유지할 수 있다.

8. 데이터 압축 (Data Compression)

SSH 프로토콜은 데이터를 효율적으로 전송하기 위해 압축 기능을 지원한다. 클라이언트와 서버는 세션 초기 설정 시 압축 여부를 협상할 수 있으며, 데이터 전송 중에 압축 알고리즘을 적용해 전송 효율을 높인다. 압축을 통해 대역폭 사용을 줄이고 전송 속도를 향상시킬 수 있다.

8.1 압축 방식 선택

SSH는 데이터 압축을 위한 여러 알고리즘을 지원한다. 가장 흔히 사용되는 압축 알고리즘은 zlib으로, 데이터를 효율적으로 압축하는 데 사용된다. 클라이언트와 서버는 압축을 적용할지 여부와 사용할 알고리즘을 협상한다.

클라이언트와 서버 간에 주고받는 협상 내용은 다음과 같다:

클라이언트가 압축을 사용하도록 설정할 경우, 각 데이터 패킷은 전송되기 전에 압축된다.

8.2 압축 과정

압축 알고리즘을 사용하는 경우, 전송할 데이터를 먼저 압축한 후 암호화하여 전송한다. 압축 과정은 다음과 같은 수식으로 나타낼 수 있다:

\text{Compressed\_Data} = \text{Compress}(M)

여기서 M은 원본 데이터(평문)이며, \text{Compress}(M)는 데이터 압축 함수이다. 이로써 전송해야 할 데이터 크기를 줄여 네트워크 대역폭을 절약할 수 있다.

8.3 압축 해제

데이터를 수신한 측은 데이터를 복호화한 후 압축을 해제한다. 압축 해제 과정은 다음과 같다:

M = \text{Decompress}(\text{Compressed\_Data})

이로써 클라이언트와 서버는 원본 데이터를 복구할 수 있다. 데이터 압축과 해제는 SSH 프로토콜에서 자동으로 처리되며, 이는 투명하게 작동하여 사용자에게 별도의 작업을 요구하지 않는다.

9. 세션 종료 (Session Termination)

SSH 프로토콜은 세션이 더 이상 필요하지 않을 때, 안전하게 세션을 종료하는 메커니즘을 제공한다. 세션이 종료될 때, 클라이언트와 서버는 서로의 연결을 정리하고, 모든 암호화된 통신을 중단한다.

9.1 세션 종료 요청

클라이언트가 세션을 종료하려면, 먼저 서버에 세션 종료 요청을 전송한다. 세션 종료 요청은 일반적으로 "SSH_MSG_DISCONNECT" 메시지를 통해 전달된다. 이 메시지는 다음과 같은 구조를 갖는다:

\text{SSH\_MSG\_DISCONNECT} = \{\text{reason\_code}, \text{description}, \text{language\_tag}\}

여기서 \text{reason\_code}는 종료 이유를 나타내며, \text{description}은 이유에 대한 설명, \text{language\_tag}는 설명의 언어 정보를 포함한다.

9.2 세션 정리

서버는 세션 종료 요청을 수신하면, 클라이언트와의 연결을 정리하고 남아 있는 모든 데이터가 처리되었는지 확인한다. 이후 클라이언트와 서버는 서로 간의 TCP 연결을 종료한다. 세션 종료는 양측이 서로 연결이 종료되었음을 확인할 때까지 계속된다.

9.3 비정상 세션 종료

간혹 네트워크 오류나 다른 이유로 인해 세션이 비정상적으로 종료될 수 있다. 이 경우 클라이언트나 서버는 일정 시간 동안 연결이 유지되지 않으면 자동으로 세션을 종료한다. 이는 TCP 프로토콜의 타임아웃 메커니즘에 의해 처리된다.