사전 특권 포트
개념

특권 포트

gabury1고친 사람 github-actions[bot]

특권 포트는 운영체제가 관리자에게만 맡기는 포트 번호입니다. 관리자가 아닌 프로그램이 이 번호를 열려고 하면 운영체제가 거절합니다. 0번부터 1023번까지가 여기에 듭니다. 그래서 이 번호에서 도는 서버는 관리자가 띄웠다고 믿을 수 있습니다.

쉽고 빠른 이해

특권 포트는 관리자만 열 수 있는 낮은 번호들입니다. 웹 서버가 쓰는 80번과 443번, 원격 접속 서버가 쓰는 22번이 여기에 듭니다.

이 번호들은 널리 약속된 서비스가 쓰는 번호입니다. 아무 사용자나 22번을 열 수 있으면 원격 접속 서버 행세를 하며 남의 비밀번호를 받아 챙길 수 있습니다. 관리자만 열게 하면 이 번호에서 도는 서버는 관리자가 띄운 것이라고 믿을 수 있습니다.

어떻게 도나:

  1. 서버 프로그램이 번호 하나를 자기 것으로 쓰겠다고 운영체제에 청합니다
  2. 운영체제는 그 번호가 1024보다 작은지 봅니다
  3. 작으면 그 프로그램이 관리자 권한을 가졌을 때만 허락합니다

대가는 둘입니다. 낮은 번호로 서비스하려면 어딘가에서 한 번은 관리자 권한을 거쳐야 합니다. 시작할 때만 잠깐 관리자로 띄우거나, 앞에서 받아 주는 프로그램에 그 번호를 맡기는 식입니다. 그리고 이 믿음은 그 컴퓨터 안에서만 통합니다. 노트북 주인은 누구나 자기 노트북의 관리자이기 때문입니다.

상세

서버가 번호를 맡으려 할 때 운영체제가 허락과 거절을 가르는 순서를 먼저 봅니다. 그 순서 안에서 관리자 권한 전체 대신 일부만 주는 방법이 하나 나옵니다.

그다음 이 규칙이 왜 생겼고 무엇까지 지켜 주는지 봅니다. 마지막으로 관리자 권한을 잠깐만 쓰거나 다른 프로그램에 맡겨 낮은 번호로 서비스하는 방법 둘을 봅니다.

포트와 바인드

포트는 한 컴퓨터 안에서 데이터를 받을 프로그램을 가리키는 번호입니다. 주소가 컴퓨터를 찾아가면 포트 번호가 그 안의 프로그램을 찾아갑니다. 번호는 0부터 65535까지 있습니다.

서버 프로그램은 연결을 받기 전에 번호 하나를 자기 것으로 맡습니다. 「이 번호로 오는 데이터는 나에게 달라」고 운영체제에 청하는 것입니다. 이 요청이 바인드입니다. 흔히 「포트를 연다」고도 말합니다.

바인드에는 제한이 하나 붙습니다. 0번부터 1023번까지를 바인드하려면 관리자 권한이 있어야 합니다. 이 제한이 특권 포트 규칙입니다. 그리고 제한이 걸리는 번호들을 특권 포트, 또는 예약 포트(reserved port)라고 합니다.

모든 운영체제가 특권 포트 규칙을 두는 것은 아닙니다. 아래 내용은 유닉스 계열 운영체제의 규칙입니다.

유닉스 계열 운영체제에서 관리자는 보통 root라는 계정입니다. 무엇이든 할 수 있는 사용자라 슈퍼유저라고도 부릅니다.

거절되는 바인드

일반 사용자 계정으로 서버를 80번에 띄우면 어떻게 되는지 파이썬 코드로 봅니다. 소켓은 프로그램이 네트워크로 데이터를 주고받을 때 쥐는 손잡이입니다. 서버는 소켓을 하나 만들고 그 소켓에 번호를 바인드합니다.

Python
import socket

s = socket.socket()
s.bind(("0.0.0.0", 80))    # PermissionError

0.0.0.0 은 이 컴퓨터의 어느 주소로 오든 받겠다는 뜻입니다. 뒤의 80 이 바인드할 번호입니다.

이 코드는 마지막 줄에서 멈춥니다. 운영체제가 권한이 없다는 뜻의 오류 값 EACCES(error 와 access 를 줄인 이름, 접근 거부)를 돌려주기 때문입니다.

파이썬은 이 값을 PermissionError 로 바꿔 올립니다. 메시지에는 Permission denied 가 찍힙니다.

같은 코드에서 80을 8080으로 바꾸면 잘 뜹니다. 1024 이상은 누구나 바인드할 수 있기 때문입니다. 개발하는 동안 서버를 8080번이나 3000번에 띄우는 일이 흔한 데는 이 까닭도 있습니다.

운영체제가 허락을 가르는 순서

바인드 요청을 받은 운영체제가 무엇을 확인해 허락과 거절을 가르는지 봅니다. 그 전에 알아 둘 낱말이 하나 있습니다.

리눅스는 관리자 권한을 여러 조각으로 쪼개 두었습니다. 이 조각 하나하나를 캐퍼빌리티(capability)라고 부릅니다. 프로그램에 관리자 권한 전체 대신 필요한 조각만 줄 수 있습니다.

특권 포트를 여는 조각은 CAP_NET_BIND_SERVICE 입니다. 이 조각을 가진 프로그램은 root 가 아니어도 1023번 이하를 바인드합니다. 대신 다른 관리자 일은 여전히 못 합니다.

그래서 바인드 요청 하나를 받은 운영체제는 두 번 묻습니다. 먼저 번호가 1024보다 작은지 묻습니다. 작다면 요청한 프로그램이 관리자이거나 그 조각을 가졌는지 묻습니다.

flowchart TD
    A["바인드 요청"] --> B{"번호가 1024보다 작은가"}
    B -->|아니다| OK["허락"]
    B -->|작다| C{"관리자이거나 CAP_NET_BIND_SERVICE 를 가졌나"}
    C -->|그렇다| OK
    C -->|아니다| NO["거절 · EACCES"]

웹 서버에 이 조각 하나만 주면 서버가 뚫려도 공격자가 쥐는 권한이 그만큼 좁습니다. 필요한 만큼만 권한을 주는 원칙을 최소 권한이라고 부릅니다.

관리자에게만 맡기는 까닭

낮은 번호는 널리 약속된 서비스가 쓰는 번호입니다. 웹은 443번에서, 원격 접속은 22번에서 연결을 기다립니다. 이렇게 서비스마다 미리 정해 둔 번호를 잘 알려진 포트라고 부릅니다.

여러 사람이 한 컴퓨터를 같이 쓴다고 해 봅시다. 아무 사용자나 22번을 맡을 수 있으면 진짜 원격 접속 서버가 잠깐 꺼진 틈에 가짜를 띄울 수 있습니다. 접속해 온 사람은 그 가짜에 비밀번호를 넣게 됩니다.

관리자만 이 번호를 맡게 하면 이런 가짜를 막을 수 있습니다. 이 번호에서 도는 프로그램은 관리자가 띄웠거나 관리자가 허락한 것입니다. 접속하는 사람은 번호만 보고도 그 컴퓨터의 관리자를 믿는 만큼 서버를 믿을 수 있습니다.

출발 포트로 상대를 믿던 원격 명령

연결을 거는 쪽도 자기 번호를 하나 씁니다. 답장이 돌아올 번호라서 출발 포트라고 부릅니다. 보통은 운영체제가 높은 번호 하나를 골라 붙여 줍니다.

예전에는 이 출발 포트에도 특권 포트를 일부러 썼습니다. 옛 유닉스의 원격 명령 rsh(remote shell)와 rlogin(remote login)이 그랬습니다. rsh 는 다른 컴퓨터에서 명령을 실행시킵니다. rlogin 은 다른 컴퓨터에 로그인하게 해 줍니다.

거는 쪽은 관리자 권한으로 1023번 이하를 출발 포트로 잡았습니다. 받는 쪽은 출발 포트가 1023번 이하면 상대 컴퓨터의 관리자가 보낸 연결이라고 여겼습니다. 받는 쪽 컴퓨터가 믿을 곳으로 미리 적어 둔 컴퓨터에서 온 연결이면 비밀번호 없이 명령을 받아 실행했습니다. 번호 하나가 신분증 노릇을 한 것입니다.

이 규칙이 지켜 주지 못하는 것

특권 포트가 보장하는 것은 「이 번호의 프로그램은 이 컴퓨터의 관리자가 띄웠거나 허락한 것이다」 하나입니다. 이 보장은 그 컴퓨터 안에서만 통합니다.

네트워크 너머의 상대에게는 이 보장이 약합니다. 노트북 한 대만 있으면 누구나 그 노트북의 관리자입니다. 그 노트북에서 1023번 이하를 출발 포트로 잡아 연결을 거는 것은 어렵지 않습니다.

그래서 출발 포트로 상대를 믿는 방식은 쓰이지 않게 됐습니다. 원격 접속은 SSH(Secure Shell)로 대부분 바뀌었습니다. SSH 는 암호 기술로 상대를 확인합니다.

번호는 서비스의 정체도 보증하지 않습니다. 브라우저가 은행 사이트 주소의 443번에 붙었다고 상대가 그 은행의 진짜 웹 서버라는 뜻은 아닙니다. 상대가 누구인지는 인증서처럼 암호 기술을 쓰는 장치가 확인합니다.

권한을 내려놓는 서버

관리자 권한을 잠깐만 쓰고 낮은 번호로 서비스하는 첫째 방법입니다. 관리자로 시작해서 번호만 맡고 곧바로 권한을 내려놓습니다.

이게 되는 까닭은 권한 검사가 바인드하는 순간에만 일어나기 때문입니다. 이미 맡은 번호는 권한을 내려놓아도 계속 쥐고 있습니다. 권한을 일반 사용자로 바꾸는 호출이 setuid입니다.

sequenceDiagram
    participant 서버
    participant 운영체제
    participant 브라우저
    서버->>운영체제: 관리자 권한으로 443번 바인드
    운영체제-->>서버: 허락
    서버->>운영체제: 일반 사용자로 바꿔 달라
    운영체제-->>서버: 바꿈
    Note over 서버: 443번은 계속 쥐고 있다
    브라우저->>서버: 443번으로 연결

그림에서 관리자 권한은 맨 앞 두 줄 동안만 쓰입니다. 브라우저의 연결이 들어올 때 서버는 이미 일반 사용자입니다. 요청을 처리하다 뚫려도 공격자는 관리자 권한을 얻지 못합니다.

앞에서 받아 넘기는 프로그램

둘째 방법은 낮은 번호를 다른 프로그램에 맡기는 것입니다. 애플리케이션은 1024 이상의 번호로 뜹니다. 낮은 번호로 들어온 요청은 앞의 프로그램이 받아 넘겨줍니다.

리버스 프록시는 서버 앞에서 요청을 대신 받아 뒤쪽 서버로 넘기는 프로그램입니다. 리버스 프록시가 443번을 맡습니다. 애플리케이션 서버는 그 뒤에서 8080번으로 뜹니다. 관리자 권한이 필요한 것은 앞의 리버스 프록시 하나뿐입니다.

번호만 바꿔 잇는 방법도 있습니다. 바깥에서 80번으로 들어온 데이터를 안쪽 8080번으로 돌려 보내도록 운영체제에 설정해 둡니다. 이 설정이 포트 포워딩입니다. 관리자 권한은 설정할 때 한 번만 쓰고 애플리케이션은 일반 사용자로 8080번에 뜹니다.

컨테이너는 한 컴퓨터 안에서 프로그램을 따로 떼어 돌리는 실행 환경입니다. 컨테이너를 띄울 때 바깥 번호와 컨테이너 안쪽 번호를 이어 주는 포트 매핑도 같은 생각입니다.

관련 항목

특권 포트와 함께 포트 번호를 나누는 구간

포트 · 잘 알려진 포트 · 등록 포트 · 임시 포트 · 포트 번호

특권 포트를 맡고 거는 소켓 호출과 그 오류 값

소켓 · 바인드 · listen · accept · connect · EACCES · EADDRINUSE

특권 포트를 맡는 데 필요한 권한과 그 원칙

권한 · root · 슈퍼유저 · capabilities · CAP_NET_BIND_SERVICE · setuid · 최소 권한 · 권한 분리 · 권한 상승

특권 포트를 직접 안 맡고 낮은 번호로 서비스하는 수단

리버스 프록시 · 애플리케이션 서버 · 포트 포워딩 · 포트 매핑 · 방화벽 · 컨테이너 · NAT

출발 포트를 신원 확인에 쓰던 원격 명령과 그 후계

rsh · rlogin · rcmd · rresvport · SSH · 인증서

특권 포트 규칙을 강제하는 운영체제와 그 커널

유닉스 · 유닉스 계열 · 리눅스 · BSD · 커널

다른 이름: privileged port · privileged ports · reserved port · 예약 포트