Session & HTTP Session hijacking
Session
세션 데이터는 세션 데이터 저장소에 보관된다.
SID라는 고유 식별자로 세션 데이터 저장소에서 특정 레코드를 식별한다.
세션 저장소 경로는 php.ini의 ``session.save_path``에 지정되며 ``php session_save_path()``로 얻거나 설정할 수 있다.
이를 이용해 세션 저장소에 직접 접근해서 다른 세션 데이터를 얻어올 수 있다.
```php
$path = ini_get('session.save_path');
$handle = dir($path);
while ($filename = $handle->read()) {
if (substr($filename, 0, 5) == 'sess_') {
$data = file_get_contents("$path/$filename");
if (!empty($data)) {
session_decode($data);
$session = $_SESSION;
$_SESSION = array();
echo "Session [" . substr($filename, 5) . "]\n";
print_r($session);
echo "\n--\n\n";
}
}
}
```
PHP의 PHPSESSID 처리
- ``php session_start()``를 호출하면 PHP는 현재 요청의 cookie || query string에 `` PHPSESSID``가 있는지 확인한다.
- SID가 있는 경우 : 저장소에서 해당 세션의 데이터를 읽어들이고 ``php $_SESSION``에 저장한다.
SID가 없는 경우 : SID를 생성하고 세션 데이터 저장소에 새로운 레코드를 생성한다. - 쿠키를 설정하고 헤더를 캐싱한다.
HTTP Session hijacking ( L7 )
HTTP 자체는 stateless protocol이라, SID를 이용해 세션을 식별하고, SID는 결국 쿠키이므로, 쿠키를 훔쳐내면 다른 사용자의 세션을 가져올 수 있다.
유효한 SID를 얻어내기 위한 방법으로는 세 가지가 있다.
- 예측
- 캡쳐
- 고정
예측
보통 SID는 충분히 랜덤하게 생성되기 때문에 이 방법으로는 어렵다.
캡쳐
monitor 모드로 패킷 스니핑해서 가져오는 방법을 사용할 수 있다.
고정
PHP는 요청에 포함된 SID가 있으면 새로운 SID를 생성하지 않고 그 SID에 연결된 세션 저장소를 사용한다.
이를 이용해 victim이 공격자가 미리 지정해 놓은 SID를 사용하도록 하는 방법이다.
```html
<a href="http://example.org/index.php?PHPSESSID=1234">example.org</a>
```
query string에 `` PHPSESSID``를 지정하고 redirect시키는 방법도 있다.
```html
<meta http-equiv="refresh" content="0;
url=http://example.org/index.php?PHPSESSID=1234" />
```
```js
<script>location.href='http://example.org/index.php?PHPSESSID=1234'</script>
```
이렇게 해서 만들어진 세션 데이터 저장소 레코드는 `` PHPSESSID=1234``를 지정하면 접근할 수 있어 공격자가 세션 데이터를 얻어낼 수 있다.
세션 고정 공격을 예방하기 위해서는 SID를 발급할 때 flag를 설정해두고, SID를 전송받았을 때 flag가 설정되어 있는지를 체크해 실제로 발급한 SID인지를 확인하는 방법이 있다.
```php
if (!isset($_SESSION['initiated'])){
session_regenerate_id();
$_SESSION['initiated'] = TRUE;
}
```
그러나 이 방법은 공격자가 해당 사이트를 방문해서 SID를 받은 다음, 이 SID를 고정 공격에 사용하면 해당 사이트에서 정상적으로 발급받은 SID가 맞기는 하니까 우회할 수 있다.
그래서 다른 방법을 사용해야 한다. 생각해보면 세션 고정 공격은 공격자가 정상적으로 얻을 수 있는 권한보다 높은 권한을 가진 SID를 획득하기 위해 수행하는 공격이므로, 로그인 하는 등 권한이 변경될 때 마다 SID를 재생성한다면 무력화할 수 있다.
```php
$_SESSION['logged_in'] = FALSE;
if (check_login()){
session_regenerate_id();
$_SESSION['logged_in'] = TRUE;
}
```
단, 모든 페이지에서 SID를 재생성하는 것은 비효율적이며 그렇게 한다고 더 안전한 것도 아니다.
Note ) 뒤로가기 했는데 갑자기 로그인이 풀린다던가 하는 것은 SID가 변경되었고, 뒤로가기 하면서 변경되기 전의 SID를 가리키게 돼서 그렇다.
is_hijacked?
`` User-Agent``의 일관성을 검사하는 건 별로지만, 심층 방어의 관점에서 보면 안하는 것 보다는 낫다.
```php
if (isset($_SESSION['HTTP_USER_AGENT']){
if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT'])){
// password authentication or exit
}
} else {
$_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
}
```
그러나 일반적으로 SID는 쿠키에 넣어서 전송되기 때문에 SID를 알고 있다면 모든 HTTP header 정보를 알고 있을 가능성이 높고, 이 경우 `` User-Agent``를 비롯한 모든 값을 복사해 사용할 수 있다.
따라서 검사에 사용하는 정보들을 salt와 함께 해싱한 다음 이를 토큰으로 사용해서 페이지의 모든 내부 링크가 query string에 토큰을 포함하도록 구성하는 방법으로 보완할 수 있다.
```php
<?php
$url['token'] = rawurlencode($token);
$html['token'] = htmlentities($url['token'], ENT_QUOTES, 'UTF-8');
?>
<form action="index.php?token=<?= $html['token'];?>">
...
```
세션 하이재킹을 막기 위해서는
- SID를 비공개하거나
- 세션이 hijacking되었는지를 식별할 수 있는 방법을 사용하거나
1.
SID 비공개는 SSL로 처리하면 된다. 근데 암복호화를 사용하는건 어쨌든 오버헤드를 동반하는 일이기도 하고, 하나의 페이지라도 HTTP로 처리한다면 거기서 SID가 노출되니까 모든 페이지를 SSL로 처리해야만 한다. 대체로 이 방법을 사용한다. 구글이나 페이스북이나...
그러나 SSL을 이용하는 경우에도 `` ClientHello``에 이전 SID를 적어 보내면 이전 세션 연결이 재개되기 때문에
이를 이용해 hijacking 할 수 있다. 고 하는데 직접 해보지는 않았다.
2.
src IP를 사용해 검증하는 방법이 괜찮기는 한데, 이건 각 웹사이트들이 IP보안이라는 항목으로 제공하고 있는 듯. 그리고 모바일에서는 이동하면서 IP가 변경되거나 와이파이를 잡는 경우 매번 다시 로그인해야 하기 때문에 유저가 불편함.
물론 IP도 spoof 할 수 있지만, 서로 다른 원거리 서브넷에 위치한 victim으로 IP spoof한다면 라우팅이 제대로 되지 않아 response가 돌아오지 않을 듯. ( 실험해보지는 않았다. )
PHP 단에서 L3 real IP addr을 확인하는 방법은 ``php $_SERVER['REMOTE_ADDR']``를 사용하는 것.
'Security > WebHacking' 카테고리의 다른 글
Blind SQL Injection (0) | 2017.08.08 |
---|---|
Basic SQL injection (0) | 2017.08.08 |
인증(Authentication)과 인가(Authorization) (0) | 2017.06.28 |
Brute Force / Replay Attack (0) | 2017.06.28 |
Filtering / Escape (0) | 2017.06.27 |