XSS 방어를 위한 JavaScript의 세션 쿠키 접근 제한

사례 : XSS 취약점이 보고된 사이트에서 취약점을 개선하고나서, 추가 취약점 가능성을 대비한 세션 쿠키 접근 제한 사례입니다.

목표 : XSS 공격 성공시에도 Javascript 에서 세션 쿠키에 접근할 수 없도록 하여, 피해를 최소화 합니다.


배경

XSS(cross-site scripting) 취약점은 공격자가 입력한 JavaScript 가 사이트내에서 의도치 않게 실행되는 공격입니다.
이때 공격자 본인이 아닌 다른 사용자가 해당 페이지를 열게 되면, 해당 사용자의 인증 정보가 담긴 세션 쿠키를 탈취하는 등 공격이 가능하게 됩니다.
선제적으로 프로그램 소스 단에서 사용자가 입력한 데이터를 철저하게 필터링해야 하지만, 신규 취약점이 발견될 가능성을 고려하여 방어용 보안 정책을 마련하고자 합니다.

PHP 에서는 사용자 인증 정보를 담은 세션 쿠키를 생성하는데, 이때 HttpOnly 속성을 부여하면 JavaScript 에서 접근할 수 없어 공격자가 쉽게 인증 정보를 얻지 못하게 됩니다.

사례

쿠키를 생성할 때 HttpOnly 속성만 지정하면 되기 때문에 다른 준비 작업은 필요없습니다.

사이트에 사용된 솔루션 : 그누보드4 불당팩 (https://github.com/open2/gnuboard4-buldang-pack)

작업 대상 기능 정리

사용자 인증에 사용되는 쿠키는 보통 다음 2개 위치에서 생성됩니다.

  • 로그인시 생성되는 세션 쿠키
  • 자동 로그인시 추가 생성되는 쿠키

주의) 보안 개선 작업 중 가장 흔한 실수는 로그인시 세션 쿠키만 처리하고, 자동 로그인쿠키에는 적용을 누락하는 경우입니다. 이런 사이트는 자동 로그인된 사용자만 탈탈 털리는 신기한 경험을 하게 됩니다. ㅜㅜ

로그인시 생성되는 세션 쿠키

먼저 크롬 브라우저에서 작업하려는 사이트에 접속하여, [개발자 도구]를 실행합니다.

크롬 개발자 도구에서 [Application – Cookies]를 확인하면, PHP 에서 생성한 세션 쿠키(PHPSESSID)의 HTTP 항목이 비어 있습니다. (쿠키명은 사이트마다 다를 수 있음)

이때 JavaScript 에서 세션 쿠키에 바로 접근할 수 있습니다. 이 경우 XSS 공격을 당하면 해당 사용자인 것처럼 글을 작성하거나 정보를 빼낼 수 있습니다.

이제 JavaScript 에서 세션 쿠키에 접근할 수 없도록 제한해 보겠습니다.
세션 시작(session_start) 루틴 이전에 세션 쿠키 설정(session_set_cookie_params)에서 5번째 입력값인 HttpOnly 속성을 true 로 선언합니다. (미선언시 기본값은 false)

// common.php

//session_set_cookie_params(0, "/");
session_set_cookie_params(0, "/", $g4['cookie_domain'], false, true);

다시 크롬 개발자 도구에서 쿠키 정보를 열어, 테스트를 위해 기존 쿠키를 강제로 삭제하고, 웹페이지를 새로고침합니다.

새로운 세션 쿠키가 만들어지고, HTTP 항목에 체크가 되어 있습니다.

이제 JavasScript 에서 세션 쿠키에 접근이 안됩니다. (물론 HttpOnly 속성이 아닌 쿠키도 함께 있다면, 해당 쿠키는 출력됩니다.)

이상 세션 쿠키에 대한 HttpOnly 속성은 완료되었습니다. 다음은 중요한 자동 로그인쪽입니다.

자동 로그인시 추가 생성되는 쿠키

자동 로그인은 보통 세션 쿠키와 별도로 쿠키를 생성하게 됩니다. 이로 인해 세션 쿠키에만 HttpOnly 속성을 적용하면, 자동 로그인 정보는 XSS 공격에 탈탈 털리게 됩니다.

먼저 프로그램 소스에서 자동 로그인 처리 루틴을 찾습니다. 보통 로그인 처리 소스에서 로그인이 성공한 다음 처리됩니다.

그누보드4 불당팩의 경우 다음 소스에 있는데, 다행이 쿠키 처리를 공통 함수로 감싸고 있습니다.

// bbs/login_check.php

set_cookie('ck_mb_id', $uid, 86400 * 31);

따라서 공통 함수 set_cookie() 안에서, setcookie() 함수의 7번째 입력값인 HttpOnly 속성을 true 로 변경합니다. (미선언시 기본값은 false)

// lib/common.lib.php

function set_cookie($cookie_name, $value, $expire)
{
    global $g4;

    //setcookie(md5($cookie_name), base64_encode($value), $g4[server_time] + $expire, '/', $g4[cookie_domain]);
    setcookie(md5($cookie_name), base64_encode($value), $g4[server_time] + $expire, '/', $g4[cookie_domain], false, true);
}

이제 로그인 작업과 마찬가지로 크롬 개발자 도구에서 기존 쿠키를 모두 삭제하고, 자동 로그인후에 생성되는 쿠키에 HTTP 속성이 있는지 테스트해보면 됩니다.

추가로 소스내에서 setcookie() 함수를 사용하는 소스를 찾아, 인증 등 중요 정보라면 동일하게 HttpOnly 속성을 부여하면 됩니다.

팁) PHP 함수 인자값 쉽게 구분하기

PHP 함수의 5번째, 7번째 입력값을 지정하라고 하니 혼동되시죠?

PHP 개발툴의 대세인 PhpStorm을 사용하시면, 입력값 힌트가 보여 메뉴얼을 뒤지지 않아도 됩니다.

https://www.jetbrains.com/phpstorm/

오류 가능성 및 배포 안내

오류 가능성

Flash 파일 업로드, ActiveX 등 쿠키를 JavaScript 기반으로 사용하는 사이트의 경우, 세션 쿠키에 HttpOnly 속성을 부여하면 Flash, ActiveX 에서 세션에 접근하지 못해 로그인하라는 오류가 발생할 수 있습니다.

배포 안내

신규 세션/자동 로그인 쿠키가 생성되는 시점에서 HttpOnly 속성이 적용됩니다. 따라서 기존 사용자들의 세션/자동 로그인 정보를 강제 초기화해주어야 합니다.

그누보드4 불당팩에서 자동 로그인 정보 삭제하기

delete from g4_cookie;

그누보드4 불당팩에서 redis 세션 정보 삭제하기

redis-cli --scan --pattern 'PHPREDIS_SESSION:*' | xargs redis-cli DEL

참고

  • XSS(cross-site scripting) : https://namu.wiki/w/XSS
  • HTTP Cookie : https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies