1.0 파일 다운로드 후 로컬에 저장하기
브라우저가 파일을 어떻게 처리할지 결정하는 것은 확장자가 아니라 서버가 보낸 MIME 타입이었다. 이미지 파일 링크를 브라우저에서 열었을 떄, image/png라면 이미지 파일로 보고 브라우저에서 표시한다. PDF 파일도 인라인 표시가 된다. 이것이 기본 동작이다.
예를 들어, 서버 응답에 Content-Disposition 헤더가 있으면, 브라우저는 다운로드 대화상자를 표시하고 파일을 저장한다. filename으로 지정된 파일명이 다운로드 대화 상자에 기본값으로 표시된다.
Content-Disposition: attachment; filename=filename.xlsx
Content-Disposition 헤더를 이용한 다운로드 기능은 HTTP를 위해 만들어진 것은 아니다. 이메일의 첨부 파일을 위해 RFC 1806에서 정의된 규격이다. HTTP/1.0과 초기 버전 HTTP/1.1(RFC 2068)에는 없었고, 2년 후 개정된 HTTP/1.1 (RFC 2616)에서 언급되었다. HTTP의 RFC 규격에는 없었지만, 몇몇 브라우저에서 이미 구현된 이 기능은 첫 HTTP/1.0이 나오고 15년 후인 2011년에 RFC 6266에서 정식 사양으로 정해졌다.
2.0 다운로드 중단과 재시작
파일 크기가 크면 클 수록 다운로드 시간이 오래 걸리고, 다운로드 시간이 길어지면 통신이 불안정해지거나 다운로드 도중에 실패할 확률이 높아진다. 요즘 회선 환경에서는 실패하지 않고 다운로드를 마치는 경우가 많겠지만, 중단된다면 중단된 지점부터 다시 시작하는 방법을 제공한다. 오리지널 사양은 RFC 2616, 최신 사양은 RFC 7233이다.
재시작은 지정한 범위를 잘라내 다운로드 한다는 뜻이다. 서버가 범위 지정 다운로드를 지원하는 경우는 Accept-Ranges 헤더를 응답에 부여한다.
Accept-Ranges: bytes
위는 bytes 단위로 범위 지정 다운로드를 하는 것이다. bytes와 none만이 존재하는데, none은 범위 지정을 하지 않는다. 범위 지정을 하는 경우
HTTP/1.1 206 Partial Content
Content-Length: 1000
Content-Ranges: 1000-1999/5000
위와 같은 형식으로 원하는 범위를 받을 수 있다. 위는 응답헤더이고 요청 헤더는 아래와 같다.
Range: bytes=1000-1999
2.2 병렬 다운로드
서버가 세션마다 대역을 제한할 경우, 영역을 나눠 세션마다 Range 헤더를 이용해 HTTP 접속을 하는 것으로 병렬로 다운로드할 수 있다. 다운로드한 데이터 조각을 이후에 결합하면, 전체 다운로드 시간이 줄어든다.
다만 병렬 다운로드는 서버에 지나치게 부단을 주기 때문에 별로 권장되진 않는다. 브라우저의 경우는 같읍 웹사이트에 대한 동시 접속 수를 2~6으로 제한하고 있다. 요즘은 정적 파일을 캐시해 서버에 부단을 주지 않고 배포하는 CDN이 보급 돼, 다운로드가 몰려 속도가 떨어지는 일도 줄어들었다. 비교적 파일 크기가 큰 파일로는 동영상이 있지만, 동영상은 일부만 미리 읽어와도 재생이 시작되므로 모든 콘텐츠가 다운로드 될 때까지 기다릴 필요가 없다.
3.0 XMLHttpRequest
지금까지 소개한 다운로드 기능을 자바스크립트로 사용할 수 있게 해주는 기능이 XMLHttpRequest이다. XMLHttpRequest는 HTTP 통신과 마찬가지로 클라이언트가 서버에 요청을 보내고, 응답으로 서버가 클라이언트에 데이터를 보낼 수 있는 것이다. 헤더를 송수신할 수 있고, 캐시 제어나 쿠키 송수신 등 변함이 없다. FormData 클래스를 사용하면, multipart/form-data 형식으로 파일 등을 보낼 수도 있다. 단, HTTP처럼 서버 측에서 클라이언트에 요청을 보낼 수는 없다.
var xhr = new XMLHttpRequest();
xht.open("GET", "/json", true);
xht.onload = function() {
// 응답이 들어왔을 때 호출되는 메서드
if (xhr.status == 200) {
// JSON 파싱해서 표시
console.log(JSON.parse(xhr.responseText));
}
};
xhr.setRequestHeader("MyHeader", "HeaderValue");
xhr.send();
curl -H "MyHeader=HeaderValue" /json
과 같은 의미이다. open에서 세번 째 파라미터를 true로 주면 비동기로 처리가 된다. 동기 실행일 경우 응답이 돌아올 때까지 send() 메서드가 끝나지 않게된다.
3.1 XMLHttpRequest와 브라우저의 HTTP 요청 차이
- 송수신할 때 HTML 화면이 새로고침되지 않는다. (HTTP의 경우 응답이 올 때, 새로운 페이지가 렌더링 됌)
- GET과 POST 이외의 메서드도 전송할 수 있다.
- 폼의 경우 키와 값이 일대일이 되는 형식의 데이터만 전송할 수 있고, 응답은 브라우저로 표시되어 버리지만, 플레인 텍스트, JSON, 바이너리 데이터, XML 등 다양한 형식을 송수신할 수 있다.
- 몇 가지 보안상 제약이 있다. (이후 설명)
4.0 지오로케이션
클라이언트의 물리적 위치에 기반한 서비스를 접해본 경험이 있을 것이다. 물리적 위치를 측정하는 데는 2가지가 있다.
1. 클라이언트 자신이 측정해서 서버에 보냄
2. 서버가 클라이언트 위치를 추측
4.1 클라이언트 자신이 위치를 구하는 방법
최근 모던 브라우저는 지오로케이션 API를 제공한다. 예를들어, 스마트폰이라면 내장된 GPS나 기지국 정보를 활용해 위치 정보를 알려줄 수 있다. GPS가 없는 컴퓨터 경우 와이파이 등을 이용한 위치 측정으로 대략적인 위치를 추측해서, 위도와 경도를 알려줄 수 있다. 하지만, 위치 정보는 사생활과 직결되므로, 사용자가 허락한 경우에만 위치 정보를 사용할 수 있다. (App에서 위치 정보 제공 여부를 묻는 이유)
와이파이 자체에는 GPS가 없으므로 와이파이에서 위치 정보를 알아내는 것은 조금 교묘하고 대규모 방식으로 이루어진다. 우선 와이파이 엑세스 포인트의 고유 ID(BBSID)와 위도 경도 정보를 데이터베이스로 사전에 구축해둔다. 클라이언트는 OS의 API를 이용해 현재 자신이 액세스할 수 있는 액세스 포인트의 BSSID를 가져와 서버에 문의해 위도와 경도를 조회한다.
BSSID는 와이파이 기기 식별자의 48비트 수치로, 기기마다 독특한 수치로 되고 있다. 맥 주소와 같은 것이라고 한다.
스마트폰은 스스로 위도와 경도를 측정하고 와이파이에 접속할 수 있으므로, 차로 이동하지 않아도 스마트폰을 이용해 위치 정보를 수집할 수 있다. 스마트폰을 이용하여 위치 정보를 수집하는 방법은 클라우드소싱이라고 불리며, 2008년경 W3C 지오로케이션 API로서 규격화 됐다.
4.2 서버가 클라이언트 위치를 추측하는 방법
지오 IP라고 불리는 IP 주소로 추측하는 방법이다. IP 주소는 지역마다 등록 관리 기관이 있어, 기업이나 프로바이더 등에 IP 주소를 할당한다. 그렇다고 등록 기관에 문의하면 되는가? 라고 묻는다면 등록 기관은 정확한 장소까지는 관리하지 않는다. 이 방식도 지오로케이션처럼 꾸준히 모은 데이터를 바탕으로 위치 정보를 알려주는 서비스로 맥스마인드, ip2location, ipligence, 도코도코JP가 있다.
6.0 원격 프로시저 호출
프로시저는 각 언어가 제공하는 함수, 클래스 메서드와 같은 것 을 뜻한다.
RPC (원격 프로시저 호출)이란 것은 다른 컴퓨터에 있는 기능을 마치 자신의 컴퓨터 안에 있는 것처럼 호출하고, 필요에 따라 반환값을 받는 구조이다. 원격 메서드 호출(RMI)이라고 불리는 경우도 있다.
RPC의 역사는 1980년대까지 거슬러 올라간다. RPC에는 다양한 방식이 있다. 인터넷의 확산과 함께 HTTP를 기반으로 하는 RPC가 몇 종류 등장했다.
6.1 XML-RPC
최초로 규격화된 RPC는 XML-RPC이다. 유저랜드 소프트웨어와 마이크로소프트가 1998년에 개발했다. XML-RPC의 규격은 xmlrpc.com에 있다. XML-RPC 자체는 RFC화되지 않았지만, XML-RPC를 바탕으로 한 RFC가 있다.(RFC 3529)
XML-RPC가 만들어진 무렵 HTTP/1.1이 있었지만, 사양 요청 예제에는 HTTP/1.0으로 적혀있었다. Content-Length를 명시해야 했기때문에, HTTP/1.1의 청크 방식은 지원하지 않고 단순한 프로토콜 위에 구축됐다. 전송에 사용하는 method는 POST이고 인수, 반환값 모두 XML으로 표현하기 때문에, Content-Type은 항상 text/xml 이다. GET은 캐시될 가능성이 있기때문에 RPC 통신에는 적합하지 않다.
6.2 SOAP
SOAP는 XML-RPC를 확장해서 만들어진 규격이다. 2016년 웹의 아키텍처로서 마이크로 서비스가 자주 화제로 오르지만, SOAP는 10년 전쯤 자주 화제가 됐고, 서비스 지향 아키텍처 안에서 큰 역할을 했었다.
SOAP는 W3C에서 규격화됐다. W3C 사이트에 1.1 사양이 게재된 것은 XML-RPC에서 불과 2년 만인 2000년 이다. 이때 Java의 J2EE가 발흥하기도 했고, 업계 전체가 웹 애플리케이션으로 방향을 바꾼 시기이다.
SOAP는 단순한 RPC였던 XML-RPC보다는 복잡하게 되어있다. SOAP 자체는 데이터 표현 형식으로, SOAP 규격 안에 SOAP를 사용한 RPC인 SOAP-RPC도 정의되어 있다. 메시지 구조를 보면 HTTP와 같다. HTTP 안에 미니 HTTP와 같은 구조로 되어있다. 이로써 HTTP 이외에도 메일 전송 프로토콜(SMTP)를 써서 SOAP 메시지를 주고받을 수도 있다. 헤더에는 요청의 메서드나 트랜잭션 정보를 기술하고, 엔벨로프에는 데이터가 들어간다.
POST /Reservations HTTP/1.1
Host: travelcompany.example.org
Content-Type: application/soap+xml; charset="utf-8"
Content-Length: nnnn
<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" >
<env:Header>
<t:transaction
xmlns:t="http://thirdparty.example.org/transaction"
env:encodingStyle="http://example.com/encoding"
env:mustUnderstand="true" >5</t:transaction>
</env:Header>
<env:Body>
<m:chargeReservation
env:encodingStyle="http://www.w3.org/2003/05/soap-encoding"
xmlns:m="http://travelcompany.example.org/">
<m:reservation xmlns:m="http://travelcompany.example.org/reservation">
<m:code>FT35ZBQ</m:code>
</m:reservation>
<o:creditCard xmlns:o="http://mycompany.example.com/financial">
<n:name xmlns:n="http://mycompany.example.com/employees">
Åke Jógvan Øyvind
</n:name>
<o:number>123456789099999</o:number>
<o:expiration>2005-02</o:expiration>
</o:creditCard>
</m:chargeReservation
</env:Body>
</env:Envelope>
샘플은 W3C 사이트에서 인용했다. 딱 봐도 복잡해 보인다... 이후에 공부할 일이 있다면 읽어보면 좋을 듯 하다.
6.3 JSON-RPC
JSON-RPC는 XML-RPC의 XML 대신 JSON을 이용한 RPC이다. 복잡한 SOAP와 대조적으로 단순하게라는 방침으로 내세웠다. XML-RPC처럼 IETF와 W3C가 아닌 jsonrpc.org라는 자체 사이트에 사양을 게재하고 있다. JSON-RPC는 HTTP 이외에 TCP/IP 소켓 등을 사용하는 것도 가정하고 있어, 사양이 최대공약수적으로 기술되어 있다. 단순함을 추구하면서 몇 가지 XML-RPC와 다른 기능도 갖추고 있다.
기본 응답은 아래와 같다. --> 부분이 request, <--부분이 response이다. 링크를 참고했다.
--> {"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 3}
<-- {"jsonrpc": "2.0", "result": 19, "id": 3}
request와 response를 보면, json형식으로 간결하게 표현하는 것을 볼 수 있다. 책에서는 request 시 필요한 헤더로 Content-Type, Content-Length, Accept 헤더가 필요하다고 명시되어 있지만, jsonrpc.org에는 명시되어 있지 않다. 다만, json obejct의 경우는 지켜야 한다.
---gRPC는 소개하지 않는듯하다.
7.0 WebDAV
WebDav는 HTTP/1.1에 포함되진 않지만, 이 시기에 만들어졌고 수많은 환경에서 지원되고 있다.
WebDAV는 HTTP를 확장해 분산 파일 시스템으로 사용할 수 있게 한 기술로, 마이크로소프트가 개발해 1999년에 RFC 2518로 책정됐다. 현재는 RFC 4918로 갱신돼, 관련된 RFC 3253(버전 관리), RFC 3744(액세스 제어)도 추가로 정의됐다.
WebDAV의 용어는 아래와 같다.
- 리소스
- 일반 파일 시스템에서는 데이터를 저장하는 아토믹 요소를 '파일'로 부르지만, WebDAV에서는 HTTP 용어를 이어받아 리소스로 부른다.
- 컬렉션
- 폴더와 디렉터리에 해당하는 요소
- 프로퍼티
- 리소스와 컬렉션이 가질 수 있는 추가 속성이다. 작성일시, 갱신일시, 최종 갱신자와 같은 정보가 해당한다.
- 락
- 분산 파일 시스템은 같은 폴더를 여러 사람이 동시에 보고 데이터를 공유할 수 있지만, 같은 파일을 동시에 편집하게 될 경우가 있다. 같은 파일에 여러 사람이 동시에 기록하면 마지막에 전송된 내용 이외에는 지워져버린다. 이를 피하기 위해 먼저 선언한 사람 이외의 변경을 거절하는 시스템이다.
HTTP/1.1의 POST, GET, PUT, DELETE 메서드를 그대로 이용하지만 파일 시스템으로서는 기능이 부족해 몇 가지 메서드가 추가됐다.
기본 조작으로서 COPY와 MOVE가 추가됐다. 모두 GET하고나서 POST(MOVE의 경우 그다음에 DELETE)하면 에뮬레이션할 수 있지만, 동영상 소재로 10GB 분량의 콘텐츠가 있을 때 전체를 일단 로컬에 저장했다가 다시 업로드하는 것은 비효율적이다. 그런 경우 처음부터 원격 웹 서버에서만 이동하거나 삭제한 후 결과만 알려주는게 효율적이다. POST 메서드는 리소스만 작성할 수 있으므로, 컬렉션을 작성하는 MKCOL 메서드가 추가됐다. 콜렉션 내의 요소 목록은 프로퍼티로 취득하므로 PROPFIND 메서드를 사용한다. LOCK/UNLOCK 메서드로 파일 잠금 여부를 제어한다.
현재 오픈 소스 개발에서 가장 많이 사용되는 버전 관리 시스템인 Git에서는 전송용 프로토콜로 SSH와 HTTPS 두 가지를 지원한다. 사실은 이 HTTPS 안에서는 WebDAV를 사용한다. SSH는 암호화된 통신 경로를 사용하지만, 그 안의 통신은 오리지널 Git 프로토콜을 사용한다. HTTPS라면 어느 WebDAV 서버를 사용해도 호스트할 수 있으므로 설정이 간단하다는 장점이 있지만, 차분한 전송할 수 있는 Git 프로토콜 쪽이 통신 속도는 뛰어나다.
8.0 웹 사이트 간 공통 인증 및 허가 플랫폼
인터넷 보급과 함께 여러 웹 서비스가 등장했다. 사용자는 웹 서비스마다 메일 주소, 사용자 아이디, 패스워드를 입력해서 계정을 만들어야 했다. 인간의 기억력에는 한계가 있기때문에 모든 사이트에 같은 아이디와 패스워드를 이용하는 사람이 많을 것이다. 만약, 어떤 웹 서비스가 해킹됐다면 유출된 패스워드를 바탕으로 다른 사이트로 침입이 가능하게 된다. 이런 공격을 리스트형 공격이라고 부른다.
외부 서비스가 제공하는 인증 기반에 합승하는 기술도 몇 가지 개발됐다. 직접 사용자 ID와 패스워드를 관리하는 기반 시스템을 구축하려면 많은 시간과 노력이 필요하다. 흔히 있는 '비밀 질문'과 같은 것을 구현하거나, 2단계 인증을 구현해야 하기도 하다. 공격에 노출되기도 하고, 정보 유출 시엔 보상하는 경우도 있다. 플랫폼을 운영 관리하는 게 아니라면, 자사 내에서 사용자 ID와 패스워드 관리를 그만 둘 경우 방대한 작업으로부터 해방돼 서비스 개발에 집중할 수 있고 보안 리스크도 줄어든다.
해당 책에서는 아래와 같은 항목을 설명한다.
- 싱글 사이온
- 커베로스 인증
- SAML
- 오픈아이디
- 오픈소셜
- OAuth (이 중 현재 가장 많이 사용되는 듯?하다)
- 오픈아이디 커넥트
우선 인증(Authentication)과 권한 부여(Authorization)의 차이를 확인하자.
Authentication(인증)
로그인하려는 사용자가 누구인지 확인한다. 브라우저를 조작하는 사람이 서비스에 등록된 어느 사용자 ID의 소유자인지 확인
Autorization(권한 부여)
인증된 사용자가 누구인지 파악한 후, 그 사용자에게 어디까지 권한을 부여할지 결정한다.
실제로 이와 같은 기술을 사용할 때는 OAuth 2.0 혹은 오픈아이디 커넥트 중 하나가 될 것이라고 생각하지만, 구현할 때는 각각의 기술을 제대로 배워둘 필요가 있다고 한다. 다행히 OpenID 파운데이션 제팬 홈페이지에 일본어로 된 RFC와 사양서가 있다. 사용할 언어에 라이브러리와 샘플 코드가 있어 구현하기 쉽다고 해도 관련 문서를 훑어보길 추천한다고 한다.
8.1 싱글 사인온
기업 내에서 사용하는 웹 서비스나 시스템이 많아지면, 싱글 사인온(SSO)이 검토된다. 싱글 사인온은 시스템 간 계정 관리를 따로따로 하지 않고, 한 번의 로그인을 전 시스템에 유효하게 하는 기술이다. 현재도 많이 사용되는 듯 하다. (네이버, 쿠팡 등)
싱글 사인온은 다른 기술과 달리 프로토콜이나 정해진 규칙이 아니고, 이런 용도로 사용되는 시스템을 가리키는 명칭이다. 구현 방식도 웹에만 한정되지 않다. 싱글 사인온을 실현하는 데는 몇 가지 방법이 있다.
각 서비스가 인증 서버에 직접 액세스하러 가는 방법이 가장 이해하기 쉬울 것이다. 사용자 ID와 패스워드를 서비스마다 입력해야 하므로 싱글 사인온은 아니지만, 사용자 ID를 일원화해 관리할 수 있게 된다. 각 애플리케이션은 인증 시스템에 로그인하는 과정을 대행한다. 그 밖에도 다음에 소개하는 티켓을 이용한 방법이 있다.
웹 서비스로 한정되지만, 각 서비스의 앞단에 HTTP 프로시 서버를 두고 인증을 대행하는 방법과 각 서비스에 인증을 대행하는 에이전트를 넣고 로그인 시 중앙 서버에 액세스해 로그인 됐는지 확인하는 방법도 있다.
8.2 커베로스 인증
인터넷보다도 이전 시대부터 내려온 방법으로는 본래의 사용자 관리 구조를 하나로 정리해, 모든 시스템에서 이용하는 방법이 있다. 공통 규격으로서 기업 내에서도 많이 이용되는 것이 RFC 2251에 정의된 LDAP이다. OpenLDAP, 액티브 디렉터리(AD) 같은 구현이 있다. LDAP는 원래 싱글 사인온을 위한 시스템이 아니라 이용자, 조직, 서버 등 기업 내 정보를 일원화해 관리하는 데이터베이스이다. v3에서 추가된 SASL(Simple Authentication and Security Layer)이라는 인증 기능과 세트로 기업 내 마스터 인증 시스템으로 사용된다. RFC 1510(최신은 RFC 4120)에 정의된 커베로스 인증이 널리 사용된다.
커베로스 인증을 하면, 티켓 보증 서버로의 액세스 토큰인 티켓 보증 티켓(TGT)과 세션 키를 얻을 수 있다. 서비스와 시스템을 사용할 때는 TGT와 세션 키를 티켓 보증 서버(TGSA)에 보내고, 클라이언트에서 서버로 액세스하기 위한 티켓과 세션 키를 받는다. 이들은 서비스가 가진 비밀 키로 암호화 되어있다. 사용자가 서비스를 사용할 때는 이 티켓과 세션 키를 서비스에 보냄으로서 싱글 사인온이 실현된다.
8.3 SAML
최근에는 많은 사내 시스템이 웹 서비스로서 구현됐다. SAML(Security Assertion Markup Language)은 웹 계통의 기술(HTTP/SOAP)을 전제로 한 싱글 사인온 구조이다. 옥타, 원로그인 같은 SaaS(Software as a service)형태로 제공되는 서비스도 있다. SAML은 XML 기반의 표준을 많이 다루는 OASIS(Organization for the Advancement of Structured Information Standards)에서 책정된 규격이다.
SAML은 쿠키로 세션을 관리하는 웹의 구조를 따르고, 도메인을 넘어선 서비스 간 통합 인증을 할 수 있다. SAML을 지원하는 싱글 사인온을 조사해보면 수많은 SaaS의 웹 서비스가 나온다. 이 서비스들은 외부 도메인에 있는 구글의 G 스위트, 사이보우즈의 킨톤, 마이크로소프트의 오피스 365, 온라인 스토리지인 드롭박스 등과 연계할 수도 있다.
서비스 간 정보 교환 메타데이터도 공통화됐다. 우선은 아래 용어를 기억하자.
사용자
브라우저를 조작하는 사람
인증 프로바이더
ID를 관리하는 서비스
서비스 프로바이더
로그인이 필요한 서비스
구현 방법은 아래 여섯가지가 있다. 해당 책에서는 HTTP POST 바인딩 구현을 소개한다.
- SAML SOAP 바인딩
- 리소스 SOAP(PAOS) 바인딩
- HTTP 리디렉트 바인딩
- HTTP POST 바인딩
- HTTP 아티팩트 바인딩
- SAML URI 바인딩
우선은 사전 준비로 인증 프로바이더에 서비스 정보를 등록한다. 등록할 때는 메타데이터로 불리는 XML 파일을 사용한다. 여기에 XML 파일을 인용하진 않지만 아래와 같은 항목이 포함된다.
- 서비스 ID (인증 프로바이더가 서비스를 식별하기 위한 것)
- 인증 프로바이더가 HTTP-POST할 엔드포인트 URL
- 바인딩(urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST 등)
- 경우에 따라서는 X.509 형식의 공개 키
서비스 프로바이더 쪽에도 인증 프로바이더 정보를 등록한다. 등록할 정보는 인증 프로바이더가 XML 파일로 제공한다. 여기에도 일련의 통신에서 사용할 엔드포인트 URL 목록과 인증서가 포함된다.
실제 통신은 단순하다. 사용자가 서비스를 이용하려고 접속했다고 가정해보자. 아직 로그인하지 않았다면, 서비스 프로바이더는 인증 프로바이더로 리디렉트 한다. 폼을 이용한 리디렉트로 HTTP의 POST를 사용해 리디렉트 한다. HTTP 리디렉트 바인딩에는 일반적인 302 스테이터스 코드를 사용한다.
브라우저는 인증 프로바이더 화면을 표시한다. 그 화면에서 로그인에 성공하면 인증 프로바이더는 로그인 정보를 서비스 프로바이더로 POST한다. 이때도 자동으로 POST하는 HTML을 반환한다. 이제 서비스 프로바이더는 사용자가 로그인에 성공한 것을 알 수 있으므로, 처음에 사용자가 요청한 페이지의 콘텐츠를 보여준다.
8.4 오픈아이디
오픈아이디는 중앙형 ID 관리를 하지 않고, 이미 등록된 웹 서비스의 사용자 정보로 다른 서비스에 로그인할 수 있는 시스템입니다. (OAuth와 같은 것 같은데..?) 2005년에는 1.0 사양이, 2007년에 2.0 사양이 정해졌지만, 현재는 후속 기술인 오픈아이디 커넥트를 이용하는 웹사이트는 늘고 있고, 오픈 아이디로 이용할 수 있는 서비스는 줄고 있다. 오픈아이디의 고유 용어로는 몇가지가 있는데 아래와 같다.
오픈아이디 프로바이더
사용자 정보를 가진 웹 서비스, 사용자는 이미 이 서비스의 ID가 있다. 2017년 현재 야후!재팬과 하테나가 지원한다.
릴레잉 파티
사용자가 새로 이용하고 싶은 웹 서비스, 2017년 이용할 수 있는 서비스는 이벤트 등록과 참가를 관리하는 웹 서비스인 ATND가 있다.
사용자 입력 식별자
사용자가 입력할 URL 형식으로 된 문자열, 오픈아이디 프로바이더가 제공하며, 오픈아이디 프로바이더의 사용자 프로필 화면 등에 표시된다. 사용자를 판별하는 ID가 아니라 서비스명 수준의 식별자가 사용되기도 한다.
야후!재팬과 하테나 계정이 있는 사용자라면 인증 정보로 ATND를 이용할 수 있다.
8.5 오픈소셜
오픈소셜은 플랫폼으로서 승승장구를 이어가고 있는 페이스북에 맞서고자 SNS 분야에서 늦게 출발한 구글과 페이스북에게 바짝 추격당한 마이스페이스가 손잡고 소셜 네트워크 공통 API로서 개발됐다. 2007년 공개 이후, 현재는 W3C에 이관돼 소셜 웹 프로토콜로서 사양이 책정되고 있다.
오픈소셜은 회원 정보나 친구 관계를 가져오는 Person&Friend API, 액티비티를 작성하는 Activities API, 정보를 저장하거나 공유하는 Persistence API, 다른 멤버에게 메시지를 보내는 requestSendMessage 등 다양한 기능을 지원하며 인증에만 머무르지 않았고 플랫폼을 지향했다.
오픈아이디의 릴레잉 파티의 경우, 서비스 쪽 제약은 거의 없고 오픈아이디 프로바이더와 릴레잉 파티를 사전에 등록할 필요도 없다. 오픈소셜의 경우, 오픈아이디 프로바이더에 해당하는 부분이 소셜 네트워크 서비스 제공자이다. 인증과 권한 부여를 모두 이쪽에서 한다. 릴레잉 파티인 서드 파티 애플리케이션은 서버를 SNS 외부에 준비하지만, UI 부분은 가젯으로 불리며 미리 정해진 규칙에 따라 XML 파일을 만들고, 친구 목록을 가져오거나 하는 각종 API를 이용해 자바스크립트와 AJAX로 애플리케이션을 개발한다. 오픈아이디와 비교하면 여러가지가 밀겹화돼 있다.
브라우저의 관점, 즉 HTTP 차원에서 보면 오픈소셜을 사용하는 소셜 인터넷 워크 서비스에 평소처럼 로그인할 뿐 특별한 기능은 사용하지 않는다. HTML의 IFRAME을 사용해 가젯이라는 서드파티 앱에 액세스하지만, 브라우저에서 사용하는 자바스크립트의 AJAX API나 도메인 외부 서버에 대한 요청 등은 이 가젯 서버에서 대응한다.
가젯 제공자와 플랫폼 제공자 사이의 통신은 책에서 설명해온 HTTP 그 자체이다. 가젯 설정 파일인 XML은 외부 서버에 두고, 가젯 서버가 HTTP로 가져온다. 클라이언트 브라우저의 요청도 가젯 서버가 중계해서 HTTP로 외부 서버에 도달한다. 외부 서버가 사용자 정보를 취득하려면 RESTful API에 HTTP로 액세스 한다.
8.6 OAuth
OAuth는 인증이 아니라 권한을 부여하는 시스템으로서 개발됐고, 현재도 활발하게 이용되고 있다. OAuth는 2006년 말부터 검토되기 시작됐다. 그러다가 2008년에 RFC 5849로 OAuth 1.0이 공개됐고, 2012년에 최신인 2.0이 RFC 6749, RFC 6750으로 공개됐다. 해당 책에서는 2.0에 관해서 소개한다.
OAuth 이전까지는 사용자가 누구인가를 판정하는 인증 프로세스였다. OAuth는 인증이 아니라 권한 부여에 특화된 시스템이다.
권한 부여 서버
오픈아이디에서 말하는 오픈아이디 프로바이더, 사용자는 이 권한 부여 서버의 계정이 있다.
리소스 서버
사용자가 허가한 권한으로 자유롭게 액세스할 수 있는 대상, 트위터나 페이스북의 경우는 권한 부여 서버와 같다.
클라이언트
오픈아이디에서 말하는 릴레잉 파티, 사용자가 이제부터 사용할 서비스나 애플리케이션, 오픈아이디와 달리 권한 부여 서버에 애플리케이션 정보를 등록하고, 전용 ID를 가져와야 한다. 이 ID를 credential이라고 부르기도 한다.
오픈 아이디와 OAuth 모두 비슷하게 화면을 전환한다. 사용자가 새로운 웹 서비스를 이용하려고 할 때, 이미 계정이 있는 서비스의 웹사이트로 전환된다. 바뀐 화면에서 사용자가 승인 버튼을 누르면, 처음에 연 웹 서비스 화면으로 다시 돌아와 정상적으로 서비스를 이용할 수 있게 된다. (단, 예외도 존재한다.)
화면 전환이 비슷하다고 해도, 인증보다 권한 부여는 큰 영향을 미친다. OAuth로 하는 일은 비유하면 전기요금이나 가스요금을 내기 위해 신용카드 번호를 맡기는 것과 같다. 오픈 아이디는 사용자가 이용하려는 웹 서비스가 신용카드 회사에 "이 사람이 회원이 맞나요?"라고 묻고, YES/NO로 응답받는 정도에 불과하지만, OAuth는 최종적으로 회원의 신용카드 번호를 받아온다. 웹 서비스는 그 카드번호가 유효하면 요금을 청구할 수 있다.
위 사례는 조금 극단적이긴 하지만, 실제로 클라이언트=외부 웹 서비스에 무엇을 허가할지는 '범위'로 결정된다. OAuth도 오픈아이디처럼 나중에 권한 부여 서버의 설정 화면에서 허가를 취소하거나 허가 범위를 변겨할 수 있다.
OAuth 2.0은 4가지 플로를 제공하며, 웹 서비스 이외에도 사용할 수 있다.
Authorization Code
자주 사용되는 일반적인 방법이다. 웹 서비스의 서버 내에 client_secret을 감출 수 있고, 외부에서 볼 가능성이 없는 경우에 사용한다.
Implicit Grant
client_secret없이 액세스할 수 있는 패턴, client_secret이 없으므로 클라이언트의 신원을 보증하지 않는다. 안전하게 client_secret을 유지할 수 없는 자바스크립트, 사용자 단말에 애플리케이션 코드를 다운로드하는 스마트폰 앱 등을 위한 방법이다.
Resource Owner Password Credentials Grant
이제까지는 클라이언트 자신이 사용자의 ID와 패스워드에 접근하지 않았지만, 이 방법은 예외다. 허가 서버가 신뢰하는 클라이언트에서 사용한다. iOS에 내장된 페이스북, 트위터 연계 등 특수한 경우다.
Client Credentials Grant
사용자 동의 없이 client_id와 client_secret만으로 액세스하는 방법이다. client_secret을 이용한 클라이언트 인증만 하므로, 서버 사이드 등 client_secret을 외부로부터 감출 수 있는 환경에서 이용할 수 있다.
8.7 오픈아이디 커넥트
오픈아이디 커넥트는 OAuth 2.0을 기반으로 한 권한 부여뿐만 아니라 인증으로 사용해도 문제가 없게 확장한 규격이다. 2014년 2월에 출시됐다. 인증과 허가에 모두 사용할 수 있고, 서비스 제공자(릴레잉 파티, 클라이언트)에서 구현하기도 어렵지 않아 앞으로 사실상의 표준이 될 것이다. 현 시점에서는 구글, 야후!재팬, 믹시 등이 오픈아이디 커넥트 프로바이더로서 서비스를 제공하고 있다.
클라이언트 입장에서 볼 때 OAuth 2.0과의 차이는 사용자 프로필에 액세스하는 방법을 규격화한 점이다. 일반 액세스 토클과 별개로 ID 토큰이 발행되는데, 이 토큰을 사용해 액세스할 수 있다. 사용자 관점에서의 절차는 오픈아이디, OAuth와 같다.
오픈아이디 커넥트에서는 액세스 토큰과 ID 토큰을 가져오기 위해 두 개의 엔드포인트와 세가지 플로를 정의했다.
권한 부여 엔드포인트
클라이언트가 권한 부여 요청을 보낼 서비스 창구, 클라이언트 인증을 하는 플로에서는 토큰 엔드포인트에 액세스하기 위한 키(권한 부여 코드)를 반환한다. 인증하지 않는 플로에서는 액세스 토큰과 ID 토큰은 이 엔드포인트가 반환한다.
토큰 엔드포인트
액세스 토큰과 ID 토큰을 반환하는 창구. 클라이언트를 인증해 강한 권한을 가진 토큰을 반환한다.
세 개의 플로는 아래와 같다.
Authorization Code Flow
OAuth의 Authorization Code와 같다. Client_secret을 은닉할 수 있는 서버 환경용, 권한 부여 엔드 포인트에 액세스해서 권한 부여 코드를 가져온 후, 토큰 엔드포인트에 액세스해서 토큰을 얻는다. 클라이언트 인증을 사용할 수 있으므로 가장 강한 권한을 허용할 수도 있다.
Implicit Flow
OAuth의 Implicit Grant와 같다. HTML 상의 자바스크립트 등 client_secret을 은닉할 수 없는 클라이언트 환경용. 권한 부여 엔드포인트에 액세스해 코드와 토큰을 한 번에 가져온다.
Hybrid Flow
Implicit Flow와 비슷하지만 권한 부여 엔드포인트에서 통신에 필요한 토큰과 추가 정보를 얻기 위한 권한 부여 코드를 얻을 수 있다. 이 권한 부여 코드를 사용해 토큰 엔드포인트에 액세스한다.
OAuth 2.0에서는 Implicit Grant로 사용자 인증을 하려고 하면, 차가 지나갈 정도로 보안에 큰 구멍이 뚫린다고 한다. 이에 대해서도 오픈아이디 커넥트에서는 해시 코드를 사용해 각종 토큰을 검증할 수 있게 개량했다.
OAuth 2.0과의 차이는 Hybrid Flow가 가장 크다. OAuth가 목표로 한 것은 다음 3자간의 워크 플로이다.
- 사용자
- 클라이언트
- 권한 부여 서버
그러나 모바일 애플리케이션이 보급되면서 클라이언트가 다시 둘로 나뉘어 4자간의 워크 플로가 필요해졌다.
- 사용자
- 클라이언트 1 : 스마트폰 단말의 애플리케이션
- 클라이언트 2: 백엔드 웹 서비스
- 권한 부여 서버
Hybrid Flow는 클라이언트 1에서 client_secret을 은닉할 수 없는 환경을 위한 Implicit Flow로 권한을 부여한 후에, 클라이언트 2가 클라이언트 인증을 하면서 토큰의 엔드포인트에 요청을 보내 더 강력한 권한을 가진 토큰을 얻을 수 있게 됐다.
Reference
- 리얼월드 HTTP : 역사와 코드로 배우는 인터넷과 웹 기술
'개발 서적 > 리얼월드 HTTP' 카테고리의 다른 글
HTTP/2의 신택스 : 프로토콜 재정의 (0) | 2024.03.29 |
---|---|
Go 언어를 이용한 HTTP/1.1 클라이언트 구현 (0) | 2024.03.08 |
HTTP/1.1 고속화와 안정성을 추구한 확장 (0) | 2024.02.14 |
검색 엔진용 콘텐츠 접근 제어 (0) | 2024.02.14 |
리퍼러 (0) | 2024.02.14 |