최근들어 여러 사이트들에서 쿠키관련 동의를 얻고 있기에 쿠키란 이름은 우리에게 익숙하다. 이 쿠키가 하는 역할은 간단한데, 클라이언트에 대한 몇가지 정보와, 설정을 브라우저에 저장하고 있는 것이다.
그래서 쿠키가 왜 필요한가를 생각해 본다면 다음과 같은 클라이언트-서버 통신의 특징 하나와 일상 예시 하나를 떠올릴 수 있다.
응용계층의 http는 무상태성(stateless)를 지닌다.
평소 웹 서비스를 사용할 때 우리는 로그인을 한번만 진행하고 원하는 서비스를 이용한 후 용건이 끝나면 로그아웃을 한다.
1번과 2번 사이에 왠지모를 괴리감이 든다.
무상태성은 상태를 가지지 않는다는 의미이다. 즉 클라이언트와 서버가 서로 어떤 상태인지 알지 못한다는 말이된다.
1번의 특징을 따르면 우리는 다음과 같이 웹 서비스를 사용하고 있어야 한다.
쇼핑몰 사이트 접속-로그인=>상품 클릭=>상세 페이지 접속-로그인=>찜하기 클릭=>장바구니 접속-로그인...
이런 1번과 2번 사이의 불가능한 상황을 가능한 상황으로 만들어 주는 것 중 하나가 쿠키다.
클라이언트-서버 통신에 대한 쿠키의 개입을 다음과 같은 단계로 설명해 볼 수 있다.
쿠키를 생성할 때 서버는 자신이 주고받을 쿠키의 조건을 설정할 수 있다.
서버는 http 헤더의 Set-Cookie
속성에 조건을 설정하고, 해당 설정을 통해 특정 조건을 만족하는 쿠키만 주고 받는다.
나름 이해를 위해서 조건을 설정한다 라는 표현을 썼지만 사실상 쿠키의 속성 설정을 의미한다.
서버가 설정할 수 있는 조건은 다음과 같다.
naver.com
과 같이 특정 도메인만 쿠키를 주고받도록 설정할 수 있다.'/user'
와 같이 설정하면 그의 하위 경로까지 포함해 쿠키를 받는다. 기본값은 '/'
이다.ms
를 지나면 쿠키를 파괴한다.true
면 https를 사용하는 경우만 허용한다.true
면 js 등의 스크립트로 접근할 수 없게 된다.(document.cookie 불가능)naver.com
정도의 수준에서 같은지 다른지를 구분한다.포트, 프로토콜, 하위 도메인을 따지지 않으며, 접미사와 접미사 바로 앞을 기준으로 한다.)GET
)true
인 경우)이렇게 설정한 조건들은 서버에서 클라이언트로 전송할때 http 헤더의 Set-Cookie 속성에 담겨져 전달된다.
클라이언트의 요청에서는 Cookie 속성으로 이름이 바뀐다.
set-cookie는 다음과 같이 생겼다.
'Set-Cookie' : [
'cookie=cookie-name',
'Secure=Secure; Secure',
'HttpOnly=HttpOnly; HttpOnly',
'Path=Path; Path=/cookie',
'Doamin=Domain; Domain=anyDomain.com'
]
위의 설정들을 보자니 쿠키는 여러 보안을 통해 상당히 안전한 것 같지만, 사실을 탈취해 이용해 먹기 쉬운 축에 속하니 민감한 정보는 쿠키에 넣어 전달하면 안된다.
express를 예로 서버에 쿠키를 생성하고 설정해 보내는 방법은 다음과 같다.
// server index
const express = require('express');
const cookieParser = require('cookie-parser'); //쿠키 생성을 위해 불러오고
const app = express();
app.use(cookieParser()); // 쿠키 생성
app.listen(3000, () => console.log('Server is starting on port:3000'));
// 요청을 담당할 미들웨어
// 쿠키를 설정한 후
const cookieOption = {
domain: 'anyDomain',
path: '/', // any path
httpOnly: true,
sameSite: 'none',
secure: true
}
module.exports = (req, res) => {
// ...
res.cookie('cookieName',userInfo, cookieOption) // 쿠키이름, 사용자 정보, 쿠키 옵션을 인자로 전달한다.
}
// 쿠키를 조건에 따라 없애야 하는 상황에는 다음과 같이 작성한다.
res.status(205).clearCookie('cookieName', cookiesOption).send('logout')
평소에 쿠키와 같이 언급되는 세션은 또 무엇일까 느낌상 무슨 연관이 되어 있어 보인다.
wikipedia에 찾아보니 다음과 같이 정의한다.
네트워크 분야에서 반영구적이고 상호작용적인 정보 교환을 전제하는 둘 이상의 통신 장치나 컴퓨터와 사용자 간의 대화나 송수신 연결상태를 의미하는 보안적인 다이얼로그(dialogue) 및 시간대를 가리킨다
https://ko.wikipedia.org/wiki/세션_(컴퓨터_과학)
뭔가 이해하기 힘들지만 정리해 보자면 다음과 같다.
즉, 서버와 클라이언트가 연결된 후 해제되기 전까지의 상태를 세션이라는 하나의 단위로 나타낸 것이다.
여기서 더 간단하게 얘기하면 서버와 클라이언트가 연결되면 세션이라는 말이다.
앞서 쿠키는 브라우저에 정보를 저장한다고 설명했다.
그러나 세션은 서버에 정보를 저장한다. 그럼 세션은 쿠키와 반대되는 개념인가 싶으면 또 아니다.
세션은 쿠키를 통해서 클라이언트가 인증된 사용자인지 확인한다.
위에서 설명한 쿠키 방식과 비슷하지만 서버는 세션저장소에 세선을 저장하고, 쿠키에 암호화 된 session id를 저장한다. 사용자 정보가 저장된 장소가 브라우저에서 서버의 세션 저장소로 옮겨진 것과 같다.
session 또한 쿠키를 사용하기에 쿠키가 가지는 취약점을 동일하게 가진다.
express를 예로 서버에 세션을 생성하는 방법은 다음과 같다.(쿠키와 크게 다르지 않다.)
const express = require('express');
const session = require('express-session'); // 세션 생성을 위해 불러온다.
const app = express();
// express-session 라이브러리를 통해 쿠키와 세션을 설정한다.
app.use(
session({
secret: 'secretstring',
resave: false,
saveUninitialized: true,
cookie: {
domain: 'anyDomain',
path: '/',
httpOnly: true,
sameSite: 'none',
secure: true,
},
})
);
app.listen(3000, () => console.log('Server is starting on port:3000'));
// 요청을 담당할 미들웨어
module.exports = (req, res) => {
//...
req.session.sessionId=secretId
};
// session을 삭제할 때는 다음과 같이 작성할 수 있다.
req.session.destroy()
쿠키와 세션은 XXS을 통한 session id, cookie 탈취에 취약하다
XXS(Cross-site Scripting)은 웹 애플리케이션에서 주로 나타나며, 외부에서 악성 스크립트를 삽입하는 방식(게시판에 악성 스크립트를 포함하는 게시글을 작성)으로 이루어진다.
웹 애플리케이션에서 사용자의 입력값을 제대로 검사하지 않는 경우 나타나기 때문에 웹 애플리케이션을 제작할 때는 XXS 관련 보안도 신경써야 한다.