왜 로그인 유지를 해야하는가?

간단한 예를 통해 왜 로그인 유지가 필요한지 설명하겠다.

로그인 페이지가 있어, 로그인을 하면 해당 유저의 점수를 출력하는 사이트가 있다. 하지만 정말로 로그인이 된 상태일까? 새로고침을 해도 페이지 내용이 변하지 않는걸 보면 로그인이 된 게 맞지 않을까?

이에 대한 답은 '아니오'다. URL을 살펴보면 id의 값을 받는 것을 확인할 수 있다. 만약 이 id값을 바꿨을 때, 다른 유저의 정보 페이지가 출력된다면 이 클라이언트는 'nciwo'라는 아이디로 로그인됐다고 보긴 어렵다.

Enter id=2 without login

URL의 파라미터 값만 바꿨다고 로그인을 하지 않고도 다른 유저의 정보를 열람할 수 있다. 

이러면 로그인이 의미 없게 된다.

그렇기에 로그인을 한 뒤 해당 유저가 로그인한 ID에 해당함을 서버에서는 알 수 있어야 한다.

그것을 이 글에서는 로그인 유지라고 표현한다.

로그인 유지

그럼 어떤 방식으로 로그인을 유지시킬 수 있는가? 다른 페이지로 접속할 때마다 클라이언트로부터 ID와 PW를 받아, 로그인 인증을 한다면 클라이언트의 신원을 확인할 수 있다. 하지만 이걸 유지라고 하긴 어렵다. 상시 재확인이라는 말이 더 어울린다. 옛날 개발자들은 이러한 고민을 해결하기 위해 쿠키라는 개념을 도입했다. 로그인을 하면 서버가 클라이언트에게 특정한 키워드의 쿠키를 지급하고, 앞으로 클라이언트는 이 쿠키를 인증 도구로써 서버와 통신을 하는 것이다.

Figure 1

Figure 1에서는 로그인 과정을 개괄적으로 보여준다. 단계별로 약간의 부연설명을 붙이자면

  1. `POST id='nciwo', pw='1234'`
    맨 처음 사진에 나왔던 로그인 페이지에서, ID와 PW를 입력하고 LOGIN 버튼을 누른 상태다. 
  2. `set-cookie: login_id='nciwo'`
    해당 ID-PW에 해당하는 유저 정보를 확인한 뒤, 클라이언트에게 login_id의 값이 nciwo인 쿠키값을 전달한다.
  3. `Cookie: login_id='nciwo', GET index.php?id=nciwo`
    서버에게 login_id에 해당하는 쿠키 값으로 nciwo를 보여주며, 유저 정보 페이지를 요청한다.
  4. `Return page about 'nciwo'`
    서버는 클라이언트에게 해당하는 페이지를 제공한다.

cookie를 이용한 로그인

BurpSuite를 통해 실제로 통신이 이뤄지는 과정을 살펴보겠다.

POST id='nciwo', pw='1234'

로그인을 처리하는 login_proc.php로 `id=nciwo&pw=1234`를 `POST`를 통해 전달하는 모습을 확인할 수 있다. 이는 위에서 설명했던 첫번째 과정에 해당한다.

set-cookie: login_id='nciwo'

서버는 Response에서 `Set-Cookie`를 통해 login_id의 쿠키값을 nciwo로 지정해준다.

Location을 통해 클라이언트가 유저 페이지로 리다이렉션할 수 있도록 했기 때문에 클라이언트는 자동으로 유저 페이지로 `GET` 요청을 보내게 될 것이다.

Cookie: login_id='nciwo', GET index.php?id=nciwo

첫번째 줄을 통해 index.php?user=nciwo로 `GET` 요청을 보내는 것을 확인할 수 있다.

그와 동시에 Cookielogin_id=nciwo를 넣어 서버에게 전달한다.

Return page about 'nciwo'

성공적으로 로그인했다.

만약 여기서 아까처럼 URL만 약간 수정하면 어떻게 될까? 'noot'이라는 아이디가 있다고 가정하고, user 파라미터에 nciwo 대신 넣어보자.

GET index.php?user=noot

인증에 실패했다면서 유저 정보가 뜨지 않는 것을 확인할 수 있다. 이는 파라미터로 들어온 noot과 쿠키 값인 nciwo를 일치하는지 비교한 뒤, 일치하는 경우에만 정보를 보여주도록 설계가 돼있기 때문이다.

보안이 성립된 것으로 보인다.

 

 

면 좋겠으나, 잘 생각해보면 문제점이 존재한다.

Cookie값 변조

아까 GET의 파라미터로 들어온 noot과 쿠키값인 nciwo를 비교해서 일치하면 정보를 제공한다고 했다. 여기서 문제는 보내는 쿠키값은 클라이언트가 수정할 수 있다는 점이다. 만약 쿠키값을 noot으로 변조해서 GET 요청을 다시 시도한다면 유저 noot의 정보를 가져올 수 있을지도 모른다..

일단 탈취하고자 하는 유저의 정보페이지를 URL로 입력한다. 

그럼 브라우저는 서버에게 패킷을 보내려고 할 것이다. 그 패킷을 보내기 전에 변조를 시도해본다.

중요한 점은 login_id의 값을 nciwo가 아닌 noot으로 수정하는 것이다.

이제 보내보면..

로그인을 건너뛰고 다른 사용자의 정보를 가져와버렸다.

결국 쿠키는 로그인 유지를 하는 데 있어선 필요하지만 보안을 그리 강화시키지는 못한다.

이를 해결하기 위해 세션이라는 것을 사용한다.

세션

쿠키값을 id 그대로 사용하는 것 바람에 보안에 취약하다고 잠깐 생각할 수도 있다. 그렇다고 쿠키값을 랜덤하게 지정해버리면 서버 측에서는 클라이언트가 가져온 랜덤한 쿠키값이 어떤 유저에 해당하는 값인지 알 수 없다.

근본적인 원인은 인증에 사용될 값이 클라이언트에 저장돼있음에 있다. 즉, 세션은 이 근본적인 원인을 해결하기 위해 인증값을 서버에 저장하는 방식을 택한다.

Figure 2

Figure 2에서는 세션을 이용한 로그인 및 유저 페이지 요청 과정을 보여준다.

  1. `POST id='nciwo', pw='1234'`
    이 부분은 바뀌지 않는다. 그저 클라이언트가 서버에게 아이디와 비밀번호를 보내는 과정이다.
  2. `session_start -> session_id: 3289fweiaj2, uid: nciwo`
    서버에서는 세션을 시작한다. 랜덤한 이름(여기선 3289fweiaj2)의 세션 파일을 하나 만들어서 그 안에 uid라는 키의 값으로 사용자의 아이디를 적어둔다.
  3. `set-cookie: PHPSESSID=3289fweiaj2`
    세션도 인증 정보를 전달할 때는 쿠키를 사용한다. 세션파일의 이름을, 즉 세션 ID를 클라이언트에게 set-cookie로 전달한다.
  4. `GET index.php?user=nciwo, Cookie: 3289fweiaj2`
    클라이언트는 방금 받은 세션 ID를 이용해 서버에게 자신의 유저 정보 페이지를 요청한다.
  5. `Return user page`
    세션 ID에 해당하는 이름의 세션 파일을 열어보고, 그 안에 있는 uid와 요청받은 URL의 파라미터 값이 일치한지 확인 후, 클라이언트에게 페이지를 반환한다.

POST id='nciwo', pw='1234'

이번에도 똑같이 id=nciwo, pw=1234로 POST 요청을 보내본다.

set-cookie: PHPSESSID=3289fweiaj2

그럼 Response가 오는데, 이번에도 `Set-Cookie`가 있는 것을 확인할 수 있다. 하지만 이번엔 값이 약간 다르다.

PHPSESSID라는 이름을 가진 쿠키의 값으로 이상한 문자열이 들어가 있다. 저 이상한 문자열이 바로 인증에 사용될 세션 ID다.

session ID & value

실제로 세션 ID는 서버 안 특정 경로에 파일로 저장이 되며, 그 안에 사용자의 정보가 들어있다.

특히, 해당 파일의 이름이 `sess_[세션 ID]`인 것 또한 확인할 수 있다.

GET index.php?user=nciwo, Cookie: 3289fweiaj2

앞으로 클라이언트는 세션 ID를 쿠키로 설정해, 서버와 통신을 할 것이다. 서버는 해당 세션 ID에 해당하는 파일을 열어 `GET` 파라미터로 들어온 user의 값 nciwo가 일치하는지 확인한 뒤 응답한다.

Return user page

쿠키값으로 사용자 ID가 들어갈 때와 달리, 세션 ID를 유추하는 것은 사실상 불가능하기 때문에 원하는 사용자로 위장해서 접속하는 것을 막을 수 있다. 물론, 세션 ID를 탈취당한다면 위장 접속이 가능해진다. 

세션 vs 쿠키

세션은 쿠키보다 훨씬 보안적으로 안전하지만, 서버에 파일을 생성하도록 한다. 이는 서버에 무리를 줄 수도 있다. 아무 생각없이 세션을 마구잡이로 생성하는 것은 지양해야 한다.

'Study > with normaltic' 카테고리의 다른 글

SegFault CTF - 5주차 - 1  (0) 2025.05.06
SegFault CTF - 4주차  (0) 2025.04.24
PHP와 MySQL  (0) 2025.04.16
간단한 로그인 페이지  (1) 2025.04.08
Web Server와 Web Application Server  (0) 2025.04.05

+ Recent posts