AboutWritten 지나간 발자국 발자국
황현석 | 2003. 06. 23.에 작성되었습니다.

황현석 일지

무료로 충남대학교 인증서 발급받기 본문

무료로 충남대학교 인증서 발급받기

인증서를 무료로 발급받기 위해서는, 학내에 secure shell로 접속 가능한 서버를 보유해야합니다. 보유했다면, 전체를 AI한테 넣어서 요약시키면 됩니다.

 

 

이번에 2026 ACPC 대회를 나가게 되면서 재학증명서가 필요해졌습니다. 근데 요즘 대전 날씨는 정말 쉽지 않습니다. 여름이라 학교까지 가는 것도 덥고 습해서, 집에서 처리할 방법을 먼저 찾아보게 되었습니다.

 

충남대 증명서 발급은 전부터 잘 알고 있었습니다. 학교에서 뽑으면 무료이고, 인터넷 인쇄도 학내 IP에서는 무료입니다. 반면 학외 IP에서 인쇄하면 600원이고, PDF 다운로드는 교내/교외 구분 없이 2,600원이 붙습니다.

 

갑자기 든 생각은 "왜 print랑 pdf출력은 가격이 다르지? pdf는 2,600원이나 하지? 너무 비싸다" 였습니다. 재학증명서 한 장을 PDF로 만드는 데 드는 트래픽과 연산비가 2,600원이 든다고 보기는 어렵기 때문입니다. 서버가 학적 데이터를 조회하고, 문서 템플릿에 값을 넣고, QR이나 원본확인번호를 얹는 정도라면 순수 계산 비용은 거의 의미가 없습니다.

 

그럼에도 가격은 분명하게 갈렸습니다. 같은 증명서인데, 어디서 접속했는지와 어떤 형태로 받는지에 따라 금액이 달라지는 구조였습니다.

 

바로 든 생각은 "보안솔루션이나, 업체가 껴 있을 것이고, 충남대는 학생들의 편의를 위해, 업체랑 계약을 하여, 돈을 내주고 있을 것이다." 였습니다.

 

일단 저는 발급을 받아야 하므로, 브라우저의 요청이 학내 서버를 통해 나가게 하려고 했습니다. 학내에는 수많은 서버 그리고 충남대에서 제공하는 원격 피시들도 많습니다. 일단 서버들은 학교의 아이피를 사용하고 있습니다. 이 조건이면 SSH dynamic forwarding으로 SOCKS5 프록시를 만들 수 있겠다고 봤습니다.

 

전제. 이 글은 접근 권한이 있는 학내 서버와, 본인 계정으로 발급하는 본인 증명서를 기준으로 씁니다. 학교나 서버 운영 정책상 허용되는 접근인지 확인해야 하고, 제출처가 전자증명서 원본을 요구하면 정식 PDF 발급 경로를 쓰는 편이 맞습니다. 무료 발급 방법 자체보다 더 궁금했던 건 브라우저 요청이 어떤 계층을 지나가고, 출력증명서와 전자증명서 PDF의 경계가 어디서 갈라지는지였습니다.

 

결국 돈이 붙는 지점은 증명서 생성이 아니라, 접속 위치와 출력 상품의 경계였습니다.

 


1. 발급받기

 

증명서 발급 사이트 입장에서 관찰할 수 있는 것은 요청을 보낸 마지막 TCP 연결의 출발지 IP입니다. 집 와이파이에서 바로 접속하면 집 인터넷 회선의 공인 IP가 남고, 학내 서버가 대신 접속하면 학교 쪽 출구 IP가 남습니다.

 

여기서 맥북 자체가 실제로 학교 네트워크에 들어가는 것은 아닙니다. 맥북은 계속 집 와이파이에 붙어 있고, 라우팅 테이블도 기본적으로는 집 공유기 쪽을 봅니다. 다만 브라우저의 특정 요청을 학내 서버까지 보낸 뒤, 그 서버가 다시 발급 사이트로 연결하게 만들 수 있습니다. 이때 발급 사이트가 보는 마지막 출구가 바뀝니다.

 
1

그냥 접속하는 경우

브라우저가 OS 네트워크 스택을 통해 집 공유기와 통신하고, 집 인터넷 회선의 공인 IP로 아이서티 서버에 연결합니다. 발급 사이트는 이 요청을 학외 접속으로 봅니다.

 

 
2

SOCKS5 프록시를 쓰는 경우

브라우저는 목적지 서버에 바로 연결하지 않고, 로컬에 열린 SOCKS5 프록시에게 "이 주소로 연결해달라"고 요청합니다. 로컬 프록시는 SSH 터널을 통해 학내 서버로 그 요청을 넘깁니다.

 

3

아이서티가 보는 요청

아이서티 서버와 실제 TCP 연결을 맺는 쪽은 학내 서버입니다. 그래서 서버는 이 요청을 학내 IP 대역에서 온 요청으로 판단할 수 있습니다.

처음에는 이 정도면 바로 될 것처럼 보였습니다. 하지만 막상 해보면, SSH 터널을 열었다는 사실과 브라우저가 그 터널을 사용한다는 사실은 별개였습니다. 이 지점에서 브라우저, FoxyProxy, SOCKS5, SSH, OS 네트워크 스택을 따로 나눠 봐야 했습니다. 터미널에 SSH 세션 하나가 떠 있다고 해서 모든 앱이 알아서 그 경로를 타는 것은 아니기 때문입니다.


2. ssh -D는 작은 SOCKS 서버를 여는 옵션이었습니다

 

SSH 포워딩을 떠올리면 보통 -L을 먼저 생각합니다. 로컬 포트 하나를 특정 원격 주소 하나에 붙이는 방식입니다. 그런데 브라우저는 증명서 발급 사이트 하나만 여는 것이 아니라 로그인, 정적 파일, 출력 프로그램, 결제 관련 주소까지 여러 목적지로 연결합니다. 목적지가 계속 바뀌는 상황에서는 고정 포워딩보다 -D가 더 맞아보였습니다.

 

OpenSSH ssh(1) 문서-D를 local dynamic application-level port forwarding이라고 설명합니다. 로컬 쪽에 listen socket을 하나 만들고, 그 포트로 연결이 들어오면 애플리케이션 프로토콜을 읽어 원격 서버가 어디로 접속해야 하는지 결정합니다. 현재 OpenSSH는 여기서 SOCKS4와 SOCKS5를 지원하고, ssh 자신이 SOCKS 서버처럼 동작합니다.

 

따라서 ssh -D가 하는 일은 Python이나 Go로 별도 프록시 서버를 띄우는 것이 아닙니다. 로컬의 ssh 프로세스가 SOCKS 요청을 파싱하고, 요청 안에 들어 있는 목적지 주소와 포트를 SSH 연결 안의 TCP forwarding channel로 바꿔 보냅니다. RFC 4254의 direct-tcpip 채널이 이때 쓰이는 형태에 가깝습니다.

 
1

브라우저가 로컬 프록시에 접속합니다

외부 사이트로 바로 TCP 연결을 여는 대신, 맥북 안에서 열린 SOCKS 포트로 먼저 연결합니다.

 

 
2

SOCKS5 협상이 먼저 일어납니다

클라이언트는 지원하는 인증 방식을 보내고, 서버는 그중 하나를 고릅니다. 이번처럼 로컬 터널이면 보통 인증 없음으로 충분합니다.

 

 
3

목적지 주소가 SOCKS 요청 안에 들어갑니다

CONNECT 요청에는 목적지 주소 타입, 주소, 포트가 들어갑니다. IP뿐 아니라 도메인 이름도 목적지로 보낼 수 있습니다.

 

4

SSH가 원격 서버 쪽 연결로 바꿉니다

로컬 ssh는 목적지 주소와 포트를 SSH channel open 요청으로 넘기고, 학내 서버 쪽 sshd가 실제 TCP 연결을 엽니다.

SOCKS5 자체는 RFC 1928에 정의된 1996년 IETF Standards Track 프로토콜입니다. Marcus Leech, Matt Ganis, Ying-Da Lee, Ron Kuris, David Koblas, LaMont Jones가 저자로 올라가 있고, SOCKS4에서 출발해 UDP, 도메인 이름, IPv6, 더 일반적인 인증 방식을 포함하도록 확장됐습니다. 당시 문제의식은 방화벽 안쪽의 클라이언트가 여러 애플리케이션 프로토콜을 더 일반적인 방식으로 방화벽 밖 서버와 통신하게 하는 것이었습니다.

 

그래서 SOCKS5는 HTTP 프록시처럼 HTTP 요청을 해석하는 프로토콜이라기보다, 목적지로 향하는 TCP 연결을 대신 열어주는 얇은 약속에 가깝습니다. 연결이 성공하면 그 뒤의 TLS handshake나 HTTP 요청은 SOCKS5가 이해하지 않습니다. 그냥 양쪽 바이트 스트림을 중계할 뿐입니다.

 

여기서 처음에 착각한 부분이 있었습니다. SOCKS5 프로토콜 자체는 암호화를 해주지 않습니다. TCP나 UDP 연결을 대신 열어주는 프로토콜일 뿐이고, 그 안을 지나가는 데이터가 안전하려면 별도의 암호화 계층이 필요합니다. 그래서 처음에는 ssh -D가 원격 서버에 SOCKS5 프록시를 열고, 브라우저가 그 원격 프록시까지 평문 SOCKS5로 통신하는 구조인 줄 알았습니다. 그렇게 되면 SSH를 쓰면서도 SSH의 장점을 버리는 이상한 구조가 됩니다.

 

실제로는 반대였습니다. SOCKS5 프록시 서버는 제 맥북 안의 로컬 ssh 프로세스에 생깁니다. 브라우저와 SOCKS5 프록시 사이의 통신은 loopback 안에서 끝나고, 학내 서버까지 나가는 구간은 SOCKS5가 아니라 SSH 프로토콜이 맡습니다. 로컬 ssh와 원격 sshd가 secure shell의 암호화 채널을 만들고, 그 안에 SOCKS5에서 읽어낸 목적지 연결을 실어 보냅니다. 이후 학내 서버가 아이서티 서버에 연결하는 구간은 HTTPS였기 때문에, 최종적으로 브라우저와 아이서티 사이의 내용은 TLS로 한 번 더 보호됩니다.

 

DNS도 이 지점에서 갈립니다. SOCKS5 요청은 목적지를 IPv4, IPv6, 도메인 이름 중 하나로 표현할 수 있습니다. 브라우저가 로컬에서 DNS를 먼저 풀어 IP를 넘기면 로컬 DNS 경로가 쓰이고, 도메인 이름을 그대로 SOCKS 서버에 넘기면 프록시 쪽에서 최종 연결을 만들 때 이름을 해석합니다. FoxyProxy에서 remote DNS 또는 Proxy DNS 옵션을 확인해야 하는 이유가 여기에 있었습니다.


3. 브라우저는 알아서 프록시를 쓰지 않습니다

 

ssh -D를 켜면 로컬 맥북에 프록시 서버가 생깁니다. 그럼 이제 브라우저가 이 프록시 서버를 사용하게 만들어야 합니다. 기본적으로 아무것도 하지 않으면, OS의 네트워크 경로를 따라 목적지에 직접 연결됩니다.

 

좀 찾아본 결과 가장 머리가 안아픈 해결책은 FoxyProxy 같은 확장 프로그램을 쓰는 방법입니다. FoxyProxy는 브라우저의 프록시 설정 API를 통해 특정 URL 패턴이나 전체 브라우징에 대해 프록시를 지정합니다. 로컬 SOCKS5 프록시를 등록하면, 브라우저는 목적지 서버가 아니라 먼저 로컬 프록시로 연결합니다. 이때부터 브라우저 트래픽만 명시적으로 지정한 경로를 타게 됩니다.

 

이 부분은 VPN과 다릅니다. VPN은 보통 OS 전체 라우팅을 바꿔 여러 앱의 트래픽을 터널로 보냅니다. 반면 FoxyProxy와 SOCKS5 조합은 브라우저 앱 안에서 선택적으로 프록시를 쓰는 방식입니다. 다만 전용 출력 클라이언트처럼 브라우저 밖에서 뜨는 네이티브 프로그램의 요청은 별도라서, 브라우저 화면이 프록시를 탄다는 사실만으로 모든 통신이 같은 경로라고 단정하면 안 됩니다.


4. 발급 사이트 입장에서는 학내에서 온 요청처럼 보였습니다

 

이 상태에서 충남대학교 인터넷 증명발급 사이트에 들어가면, 사이트가 보는 출구는 집 IP가 아니라 학내 서버의 출구 IP가 됩니다. 확인한 안내 기준으로는 인터넷 인쇄가 교내 IP에서는 무료이고, 교외 IP에서는 600원입니다. 실제 화면에서도 이 구조에서는 프린터 출력 상품이 무료로 잡혔습니다.

 

다만 여기서 인증을 뚫은 것은 아닙니다. 여전히 본인 계정으로 로그인하고, 본인 학적 정보에 대한 증명서를 발급합니다. 달라진 것은 사용자가 어디서 접속했다고 볼 것인가, 그 네트워크 경계뿐입니다. 이 차이를 구분하지 않으면 이야기가 이상해집니다. 개인정보 탈취나 계정 권한을 넘긴 것이 아니라, IP 기반 요금 정책과 출력 경로의 전제를 이용한 쪽에 가깝습니다.

여기서 보인 한계. 사이트는 "학내 IP에서 접속한 사용자는 학내 환경에 있다"고 봅니다. 이번 방식에서는 학내 서버를 프록시로 쓰면서 IP가 접속 위치의 대리값으로 쓰인다는 점이 드러났습니다. 학적 인증을 건드린 것은 아니지만, 접속 위치를 기준으로 한 요금 판단이 네트워크 출구에 의존한다는 것을 보게 된 셈입니다.

이 지점이 재미있었습니다. 평소에는 IP 주소를 그냥 주소처럼 생각하지만, 실제 서비스에서는 IP가 가격 정책의 입력값이 될 수 있습니다. 같은 계정, 같은 증명서, 같은 발급 사이트인데도 마지막 TCP 연결의 출발지가 어디냐에 따라 결제 금액이 달라집니다. 네트워크 계층 하나가 행정 서비스의 가격표와 연결되는 순간이었습니다.


5. 프린터 출력은 왜 파일 저장과 다르게 취급될까

 

그 다음에 걸린 것은 출력이었습니다. 아이서티 출력 프로그램은 문서를 브라우저에서 곧바로 PDF로 내려주지 않습니다. 전용 출력 클라이언트와 프린터 경로를 거칩니다. 처음에는 불필요한 제약처럼 보였지만, 다시 보면 목적은 비교적 분명했습니다. 사용자가 문서를 받는 방식을 출력 경로 안에 묶어두려는 것입니다.

 

웹페이지에서 그냥 PDF를 열어주면 사용자는 저장, 복사, 재출력, 편집, 가상 프린터 출력 같은 일을 쉽게 할 수 있습니다. 반대로 전용 출력 프로그램을 쓰면 프린터 목록을 검사하고, 출력 횟수를 제한하고, 가상 프린터를 차단하려고 시도할 수 있습니다. 기술적으로 끝까지 통제한다기보다는, 사용자가 지나가야 하는 경로를 좁히는 작업에 가깝습니다.

 

물론 사용자 단말에서 최종적으로 보이거나 인쇄되는 순간, 끝까지 통제하기는 어렵습니다. DRM이나 출력 보안은 복제를 불가능하게 만드는 기술이라기보다, 복제 난이도와 정책 위반 비용을 올리는 기술에 가깝다고 보는 편이 맞아보였습니다.

프린터 출력

paper-oriented

한 번 출력되고 끝나는 물리 문서에 가깝습니다. 문서번호와 QR을 통해 제출처가 원본대조를 할 수 있지만, 파일 자체를 전자 원본으로 유통하는 모델은 아닙니다.

PDF 다운로드

file-oriented

파일 자체가 원본으로 돌아다니는 상품입니다. 그래서 업체는 전자서명, 해시 검증, 원본확인번호, 전용 뷰어, 보관 기간 같은 기능을 붙여서 별도 상품으로 취급합니다.

이 단계에서는 프린터 출력 경로를 다시 PDF로 받았습니다. App Store의 Print to PDF 류 앱은 보통 시스템에 가상 프린터처럼 등록됩니다. 사용자가 인쇄 대화상자에서 그 프린터를 고르면, 앱은 실제 종이에 잉크를 뿌리는 대신 macOS 인쇄 시스템이 넘긴 인쇄 작업을 받아 파일로 저장합니다.

 

발급 서버와 클라이언트 흐름에서는 전자 PDF 다운로드가 아니라 인쇄 경로를 탄 결과였습니다. 다만 가상 프린터 사용이 발급 서비스의 이용 조건이나 제출처 요구와 맞는지는 별도 문제입니다.

 

macOS에서는 앱이 그리는 페이지가 Quartz 인쇄/PDF 계층을 거쳐 스풀 작업으로 만들어질 수 있습니다. 물리 프린터라면 그 작업이 드라이버를 거쳐 프린터가 이해하는 데이터로 내려가고, 가상 프린터라면 그 데이터를 캡처해 PDF 파일로 만듭니다. 실제로 이번에 만든 파일의 메타데이터에도 iCertiPrintClient, macOS Quartz PDFContext, PDF로 인쇄 앱 이름이 같이 남아 있었습니다.


6. QR이 살아 있으면 공식 PDF와 같은 걸까

 

여기서 남은 질문이 하나 있었습니다. 출력물을 다시 PDF로 만든 파일에 QR과 증명 문구가 살아 있다면, 이 파일은 공식 PDF와 같은 것일까. 처음에는 거의 같은 것 아닌가 싶었습니다. 제출처가 QR을 찍어서 원본대조를 할 수 있다면, 실질적으로 문제가 없지 않나 하는 생각이었습니다.

 

직접 확인해보니 절반만 맞았습니다. 이번에 만든 PDF의 QR은 실제로 읽혔고, 충남대 아이서티 원본조회 URL로 연결됐습니다. 원본번호 조회도 결과 화면을 반환했습니다. 출력증명서 안에 들어 있는 원본대조 장치는 살아 있었던 것입니다.

 

하지만 파일 자체는 공식 전자증명서 PDF로 보이지 않았습니다. PDF 서명은 없었고, 암호화도 없었고, 공식 전자증명서가 넣을 수 있는 원본 메타데이터도 보이지 않았습니다. 아이서티의 파일 해시 검증 경로에도 맞지 않았습니다.

 

그러니까 이 파일은 "QR이 살아 있는 출력증명서의 PDF 사본"에 가깝습니다. "정식 PDF 전자증명서 원본"이라고 부르기에는 검증 계층이 하나 부족했습니다.

현재 결론. 제출처가 QR/원본번호 조회만 요구한다면 기술적으로 확인 화면은 열릴 수 있습니다. 하지만 그것이 행정적으로 접수된다는 뜻은 아니고, 공식 전자증명서 PDF 원본 파일 자체를 요구하는 제출처라면 출력물을 다시 PDF로 만든 파일은 거절될 수 있습니다. 겉모습은 비슷해도 검증 계층이 다르기 때문입니다.

이 구분이 중요했습니다. 사람 눈에는 같은 문서처럼 보여도, 시스템 입장에서는 원본번호 조회와 파일 해시 검증이 다른 층에 있습니다. 원본번호/QR 조회는 서버가 보관한 발급 정보와 화면의 문서 내용을 대조하는 층에 가깝고, 파일 검증은 업로드한 PDF 바이트열이 발급 시스템이 만든 전자 원본과 같은지를 보는 층에 가깝습니다.


7. 작은 SOCKS5 서버를 보면 구조가 보입니다

 

여기까지 보고 나니 VPN이나 프록시 서버도 조금 덜 추상적으로 보였습니다. 결국 클라이언트가 목적지에 직접 연결하지 않고, 중간 서버에게 "이 주소로 대신 연결해달라"고 말하는 구조입니다. 차이는 그 중간 경로를 OS 전체 라우팅으로 잡느냐, 브라우저 프록시로 잡느냐, SSH 암호화 채널 안에 넣느냐에 있습니다.

 

다만 글의 핵심이 프록시 서버 구현은 아니므로, 전체 구현을 길게 설명할 필요는 없다고 봤습니다. 대신 SOCKS5가 코드의 어디에 녹아 있는지만 확인하면 충분했습니다. 아래 코드는 실사용 프록시라기보다, greeting, CONNECT 요청, 주소 타입, 성공 응답, relay가 어떤 바이트 처리로 나타나는지 보기 위한 최소 예제입니다.

 

import select
import socket
import struct
import threading


def read_exact(sock, size):
    # SOCKS5 메시지는 길이가 정해진 필드가 많아서,
    # 원하는 바이트 수가 모일 때까지 정확히 읽어야 합니다.
    data = b""
    while len(data) < size:
        chunk = sock.recv(size - len(data))
        if not chunk:
            raise ConnectionError("closed")
        data += chunk
    return data


def pipe(left, right):
    # SOCKS5 협상이 끝난 뒤에는 프로토콜을 더 해석하지 않습니다.
    # 클라이언트와 목적지 서버 사이의 바이트를 양방향으로 복사할 뿐입니다.
    sockets = [left, right]
    while True:
        readable, _, _ = select.select(sockets, [], [])
        for src in readable:
            data = src.recv(16384)
            if not data:
                return
            dst = right if src is left else left
            dst.sendall(data)


def handle(client):
    try:
        # 1. greeting
        # 클라이언트가 "나는 SOCKS version 5이고, 이런 인증 방식을 지원한다"고 보냅니다.
        # 첫 바이트는 version(0x05), 두 번째 바이트는 인증 방식 개수입니다.
        version, nmethods = read_exact(client, 2)
        if version != 5:
            return
        # 예제에서는 인증 방식을 실제로 검사하지 않고 읽어서 버립니다.
        read_exact(client, nmethods)
        # 서버 응답: version 5, method 0x00(no authentication)을 선택합니다.
        client.sendall(b"\x05\x00")  # SOCKS5, no authentication

        # 2. request
        # 클라이언트가 "이 목적지로 연결해달라"고 요청합니다.
        # command 0x01은 TCP CONNECT입니다. 0x02는 BIND, 0x03은 UDP ASSOCIATE입니다.
        # atyp은 목적지 주소 형식입니다. IPv4(1), domain name(3), IPv6(4)가 올 수 있습니다.
        version, command, _, atyp = read_exact(client, 4)
        if version != 5 or command != 1:
            # 0x07은 command not supported 응답입니다.
            client.sendall(b"\x05\x07\x00\x01" + b"\x00" * 6)
            return

        # 3. destination address
        # 여기부터가 SOCKS5가 HTTP proxy와 달라지는 지점입니다.
        # HTTP 요청을 읽는 것이 아니라, 목적지 주소와 포트만 읽습니다.
        if atyp == 1:
            # IPv4 주소는 4바이트입니다.
            host = socket.inet_ntoa(read_exact(client, 4))
        elif atyp == 3:
            # 도메인 이름은 길이 1바이트 뒤에 문자열이 이어집니다.
            length = read_exact(client, 1)[0]
            host = read_exact(client, length).decode()
        elif atyp == 4:
            # IPv6 주소는 16바이트입니다.
            host = socket.inet_ntop(socket.AF_INET6, read_exact(client, 16))
        else:
            # 0x08은 address type not supported 응답입니다.
            client.sendall(b"\x05\x08\x00\x01" + b"\x00" * 6)
            return

        # 포트는 network byte order(big endian) 2바이트입니다.
        port = struct.unpack("!H", read_exact(client, 2))[0]

        # 4. proxy connect
        # 프록시 서버가 목적지로 실제 TCP 연결을 엽니다.
        # ssh -D에서는 이 부분이 직접 create_connection이 아니라
        # SSH direct-tcpip 채널 생성으로 바뀐다고 보면 됩니다.
        remote = socket.create_connection((host, port), timeout=10)

        # 성공 응답: version 5, reply 0x00(success), IPv4 0.0.0.0:0 바인딩 주소.
        client.sendall(b"\x05\x00\x00\x01" + b"\x00" * 6)

        # 5. relay
        # 이제부터는 SOCKS5 헤더가 아니라 실제 애플리케이션 데이터가 흐릅니다.
        # HTTPS라면 TLS handshake와 암호화된 HTTP 바이트가 여기로 지나갑니다.
        pipe(client, remote)
    finally:
        client.close()


server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 이 예제는 안전하게 로컬에서만 듣습니다.
# 외부 인터페이스에 열면 인증 없는 공개 프록시가 되어버립니다.
server.bind(("127.0.0.1", 18080))
server.listen()
print("SOCKS5 proxy listening on 127.0.0.1:18080")

while True:
    client, _ = server.accept()
    # 연결 하나마다 SOCKS5 협상과 relay를 처리합니다.
    threading.Thread(target=handle, args=(client,), daemon=True).start()

 

이 예제와 ssh -D의 차이는 중간 구간입니다. 위 코드는 로컬 프로세스가 목적지 서버에 직접 연결합니다. 반면 ssh -D는 로컬 ssh가 SOCKS5 요청을 읽은 뒤, 목적지 정보를 SSH 암호화 채널 안에 넣어 원격 sshd에게 넘깁니다. 그래서 목적지 서버가 보는 출구 IP가 로컬 맥북이 아니라 학내 서버 쪽으로 바뀌었습니다.


8. 그럼 2,600원은 왜 붙는가

 

이제 다시 처음 질문으로 돌아오면, 2,600원은 증명서 한 장을 생성하는 순수 기술 원가라고 보기는 어렵습니다. 다만 여기서부터는 기술 검증처럼 단정할 수 있는 영역이 아니었습니다. 공개 문서와 서비스 구조를 보고, 가격이 생기는 방향을 추정하는 쪽에 가까웠습니다.

 

충남대학교의 공개 개인정보 처리방침에는 인터넷 증명발급 수탁자로 ㈜아이서티가 적혀 있었습니다. 실제 발급 도메인도 cnu.icerti.com입니다. 학교는 학적 데이터와 발급 권한을 가지고 있고, 아이서티는 인터넷 발급 사이트, 출력 클라이언트, 결제, 고객센터, QR/원본대조, PDF 검증 같은 주변 시스템을 맡습니다. 적어도 가격표가 순수 연산비보다 위탁 발급 상품 구조와 더 강하게 연결되어 있다는 정도는 확인할 수 있었습니다.

 
1

학교

학적 데이터와 증명서 발급 권한을 가지고 있습니다. 교내 발급을 무료로 둘지, 인터넷 발급에 수수료를 붙일지는 학교 정책과 계약 조건에 영향을 받습니다.

 
2

아이서티

인터넷 발급 화면, 본인 인증 흐름, 문서 렌더링, 출력 제어, 결제, QR/원본대조, 고객센터를 운영합니다. 이 부분이 대행수수료의 명목이 됩니다.

3

사용자

교내 출력, 교외 출력, PDF 다운로드처럼 상품 경계에 따라 다른 금액을 냅니다. 같은 재학증명서라도 전달 방식이 다르면 가격이 달라집니다.

PDF가 더 비싼 것도 이 구조에서 어느 정도 설명됩니다. 프린터 출력은 종이 문서 중심의 상품이고, PDF는 파일 자체가 원본처럼 돌아다니는 상품입니다. 업체는 여기에 전자 원본 검증, 해시, 메타데이터, 유효기간, 재다운로드, 제출처 전달 같은 기능을 붙여 출력과 다른 가격대를 만들 수 있습니다.

 

다만 이 말이 "PDF 한 장의 원가가 2,000원 더 든다"는 뜻은 아닙니다. 이 부분은 구축비, 운영비, 고객지원, 결제 수수료, 업체 이윤, 학교와 업체의 계약 조건이 섞인 가격으로 보는 편이 더 자연스러웠습니다. 기술 원가 하나로 환원하기는 어려웠습니다.


9. 시장 구조는 참고 정도로만 봐야 했습니다

 

조금 더 찾아보니, 대학 증명서 발급 시장은 단순히 "보안 솔루션이 비싸서 어쩔 수 없다"로 끝나는 영역은 아니었습니다. 아이서티, 써트피아, 웹민원센터 같은 업체들이 대학 증명서 발급, 무인발급기, 인터넷 발급, 전자증명서, 원본대조 같은 표면을 맡고 있었습니다.

 

학교 입장에서는 직접 구축하기보다 외주를 주는 편이 편합니다. 학사 시스템 연동, 결제, 장애 대응, 출력 프로그램, 브라우저 호환성까지 직접 들고 가는 것은 행정 조직 입장에서도 부담이 큽니다. 그래서 학교는 발급 권한과 데이터를 가지고, 실제 인터넷 발급 표면은 업체가 운영하는 식으로 나뉩니다.

 

하지만 사용자는 선택권이 거의 없습니다. 충남대 증명서가 필요하면 충남대가 계약한 발급 경로를 써야 합니다. 아이서티가 마음에 들지 않는다고 다른 발급업체를 고를 수 있는 구조가 아닙니다. 이 지점에서 일반적인 소비자 시장과 달라집니다. 사용자는 가격을 보고 업체를 선택하는 사람이 아니라, 학교와 업체의 계약 결과를 따라갑니다.

학교 입장

outsourcing

학사 시스템 연동과 발급 운영을 외부 업체에게 맡기면 내부 개발과 고객지원 부담이 줄어듭니다. 교내 무료 정책도 계약 조건이나 학교 보조로 처리할 수 있습니다.

업체 입장

fee model

발급 인프라와 출력 보안, 원본대조, 결제, 고객센터를 운영하고 건당 수수료나 계약 비용으로 회수합니다. PDF는 전자 원본이라는 이름으로 출력과 다른 가격대를 만들기 쉽습니다.

학생 입장

locked path

증명서가 필요하면 정해진 경로를 써야 합니다. 가격이 마음에 들지 않아도 다른 업체를 고르는 경쟁 소비자가 아니라, 학교가 정한 경로의 이용자에 가깝습니다.

심지어 이 시장은 공정거래 이슈도 있었습니다. 2024년 공정거래위원회 제재 보도에 따르면 아이앤텍, 한국정보인증, 씨아이테크 3개 사업자가 2015년부터 2022년까지 인터넷 증명발급 대행수수료와 발급기 가격 등을 담합한 사건이 있었습니다.

 

이 사건 하나로 충남대의 현재 가격이나 아이서티의 가격 정책을 직접 설명할 수는 없습니다. 다만 대학 증명발급 시장이 순수한 기술 원가 경쟁만으로 굴러간다고 보기는 어렵다는 참고 사례로는 볼 수 있었습니다.

 

그래서 처음의 불만은 절반쯤 맞았습니다. PDF 하나를 만드는 데 2,600원의 순수 연산비가 드는 것은 아닙니다. 동시에 아무 비용도 없는 것도 아닙니다. 학교 시스템 연동, 인증, 결제, 출력 제어, 원본대조, 고객센터, 유지보수는 실제 비용입니다. 다만 그 비용이 건당 2,600원으로 직접 대응한다기보다, 외주화된 발급 경로에서 사용자에게 부과되는 가격이라고 정리하는 편이 맞았습니다.


10. 정리하면

 

이번 일은 단순한 요금 절약 문제가 아니라, 여러 계층이 한 번에 만나는 실험이었습니다. 브라우저는 기본적으로 집 네트워크로 나가고, FoxyProxy는 브라우저의 프록시 경로를 바꾸고, SOCKS5는 목적지별 TCP 연결을 대신 열어줍니다.

 

SSH는 그 요청을 학내 서버까지 암호화된 채널로 운반합니다. 아이서티는 마지막 출구 IP를 보고 학내 접속으로 판단합니다. 그리고 출력 프로그램은 프린터 출력과 PDF 전자증명서를 다른 상품으로 나눕니다.

 

이번 방식은 학적 시스템을 뚫은 것이 아니라, 학내 IP 기반 요금 판단과 출력 경로의 전제를 관찰한 쪽에 가깝습니다. 그리고 결과 PDF는 QR 원본대조가 가능한 출력물 PDF 사본이지, 공식 전자증명서 PDF 원본이라고 단정하면 안 됩니다. 이 경계가 이 글에서 제일 중요했습니다.

 

결국 2,600원은 보안 원가라기보다 제도와 계약, 외주 시스템, 전자문서 상품화가 합쳐진 금액으로 보였습니다. 기술적으로는 네트워크 출구가 요금 판단에 영향을 줄 수 있었지만, 문서의 법적/행정적 성격은 출력증명서와 전자증명서 PDF가 다를 수 있습니다.

 

처음에는 그냥 아까운 돈이라고 생각했는데, 막상 파보니 IP, SOCKS5, SSH, 브라우저 프록시, 프린트 스풀러, 전자문서 검증, 시장 구조가 한 줄로 이어져 있었습니다. 이 시장은... 좀 너무 옛날 모델 같습니다..


참고한 문서