기본 콘텐츠로 건너뛰기

PHP에서 보안 문제를 해결하는 법



한빛미디어 홈페이지에 'PHP에서 보안 문제를 해결하는 법' 이라는 기사가 있습니다. PHP로 개발시 주의해야하는 3가지 보안 문제와 이를 막는 방법에 대한 외국 기사를 번역한 기사인데 관련 사항을 잘모르시는 분들에게 도움이 될 것 같아 기사에 소개된 내용들에 제 생각을 추가하여 정리해보겠습니다.


1. SQL 인젝션 공격

보통 게시판같은 웹 프로그램에서 DB를 사용할때 브라우저를 통해 넘어오는 값들을 SQL 문의 WHERE 절에 사용하게 되는데 이 과정에서 주의를 하지 않으면 심각한 문제가 발생하게됩니다. 아래와 같이 쿼리를 만들다고 가정하면,

$sql = "SELECT * FROM bbs WHERE idx = " . $_GET['idx'];

$_GET['idx']에 각 게시물의 idx 값이 주어진다고 생각해 코딩을 한 것이나 이 $_GET['idx']에 "10; DELETE FROM bbs"와 같은 데이터가 넘겨지면 어떻게 될까요? 위 문구를 통해 만들어진 쿼리문은 아래와 같게 됩니다.

SELECT * FROM bbs WHERE idx = 10; DELETE FROM bbs;

즉, 이 쿼리문의 최종 결과는 bbs 테이블에 있는 모든 데이터를 삭제하게되는 것이죠. 이러한 공격을 SQL 인젝션 공격이라고 하며 웹 개발에서 가장 흔히 생기는 보안문제 중 하나입니다.

이 문제를 해결하기 위해서 기사에서 소개한 PDO 또는 Zend Framework와 같은 프레임워크에서 제공하는 Prepared statements 기능을 사용합니다. PDO에서 제공하는 기능은 PDO 레퍼런스 문서의 'Prepared statements and stored procedures' 항목에서 확인하실 수 있습니다. 한가지 주의하실 것은 전통적인(?) 방법으로만 개발을 하셨던 분들에게 이 기능에 대해 설명을 해드리면 아래와 같이 사용을 하는 경우가 있습니다.

$sql = "SELECT * FROM bbs WHERE idx = " . $_GET['idx'];
$stmt = $db->prepare($sql);
$stmt->execute();

위 처럼 사용하면 SQL 인젝션 문제를 해결하는데 아무런 도움이 안됩니다. 레퍼런스 문서의 예제에도 있듯이 아래와 같이 사용해야 합니다.

$sql = "SELECT * FROM bbs WHERE idx = :idx";
$stmt = $db->prepare($sql);
$stmt->bindValue(':idx', $_GET['idx']);
$stmt->execute();


2. 사이트간 스크립팅 (XSS)

게시판 등에 글을 작성할때 제목이나 본문에 사용자를 공격하는 스크립트를 삽입하는 방식의 공격을 말합니다. 별도 필터링이 없다면 DB에 공격 스크립트가 들어간 게시물이 생성되고 그 게시물을 보게되는 사용자는 현재 접속해 있는 사이트를 신뢰하기 때문에 이렇게 삽입된 악성 코드를 통해 이루어지는 액션도 신뢰를 하는 것을 이용한 공격이죠.

한빛미디어 기사처럼 사용자의 입력을 통해 들어오는 값에 대해서는 각 필드에 맞는 형태인지 타입 체크를 하고 출력에 대해서는 htmlspecialchars() 함수를 사용해 삽입된 스크립트가 실행되지 못하게 하는 것으로 막을 수 있습니다.

다만, 게시판 본문에 HTML 태그 사용을 허용한 경우가 문제가 되는데 이런 경우 특정 사람들만 사용하는 시스템이 아니라면 이 문제를 일으킬 수 있는 코드들은 strip_tags() 함수나 아래와 같은 정규식을 이용해 입력 값에서 무조건 삭제하는 로직을 추가하는 것이 좋다고 생각합니다.

$html = preg_replace('/<\s*\bscript\b[^>]*>(.*?)<\s*\/\s*script\s*>/is', '', $html);


3. 사이트간 요청 위조 (CSRF)

이 공격은 XSS와는 반대로 시스템이 웹브라우저(사용자)를 신뢰하는 것을 이용한 공격입니다. 예를 들면 특정 시스템의 어드민이 있고 이 어드민이 시스템에 로그인된 상태에서 공격 코드가 포함된 이메일이나 게시물을 열어보게되는 상황을 가정해보겠습니다.

그 시스템은 쿠키로 정상 로그인을 확인하고 있고 특정 데이터를 삭제하는 링크가 아래와 같은 상황에서,

http://특정시스템/admin/delete.php?idx=1

어드민이 열어본 이메일에 아래와 같은 이미지가 숨겨져 있다면 어떻게 될까요?

<img src="http://특정시스템/admin/delete.php?idx=1">

그 어드민은 이미 정상적으로 로그인된 상황이기 때문에 악성 이메일에 들어가 있는 위 링크도 정상적인 요청으로 시스템에서 처리가 되는 것입니다.

이 공격을 막기 위해서는 일단 GET 방식으로는 사이트의 페이지 요청 등만 처리하고 데이터의 전송 또는 변경에 관련된 작업을 하여서는 안됩니다. POST 방식에 대한 공격은 데이터 변경을 요청하는 페이지와 실제 요청을 처리하는 페이지간에 랜덤한 토큰을 생성하는 형태로 방어를 합니다.

데이터 변경 요청을 하는 페이지에서는 랜덤하게 생성된 토큰을 아래와 같이 세션에 저장을 합니다. 한빛미디어 기사에서는 토큰 생성에 별도 외부 라이브러리를 사용하였는데 아래와 같이 PHP 함수를 조합하여도 일반적인 용도에서는 충분한 수준의 랜덤값을 만들 수 있지 않을까합니다.

$token = bin2hex(openssl_random_pseudo_bytes(16));
$_SESSION['token'] = $token;

그리고, 데이터 변경 요청폼에도 히든 필드로 해당 토큰을 넘겨줍니다. 데이터 변경을 처리하는 페이지에서는 요청시 넘겨받은 데이터에 있는 토큰과 세션에 저장된 토큰을 비교해서 정상적인 요청인지 확인을 하는 것입니다.

if ($_SESSION['token'] != $_POST['token']) {
    die('fail');
}



한빛미디어 기사 처음부분에 이런 글이 있습니다.

"사람들은 PHP가 안전하지 않다고 폄하하지만, 그들은 보안에 약한 코드를 만드는 것은 언어가 아니라 개발자라는 사실을 잊습니다."

위 글은 다른 언어를 사용하는 사람들에게 하는 말이지만 전 일부 PHP 개발자들에게도 비슷한 말을 하고 싶습니다. PHP가 느슨한 구조를 가진 언어라고 해서 개발자들까지 느슨한 - 좀 심하게 이야기하면 엉망인 - 형태로 개발하는 것을 정당화할 수는 없습니다. PHP에 대한 이런저런 안좋은 이야기는 1차적으로는 언어의 문제로 인한 것이지만 그 언어를 통해 개발하는 개발자들에 의한 생긴 부분도 있다라는 것을 생각해야할 것 같습니다.


===


사진 포트폴리오 - City, City People (http://photo.just4fun.kr/)

사진 매거진 앱 - Viewzin (http://viewzin.just4fun.kr/)



이 블로그의 인기 게시물

구글 결제에서 결제 수단 등록 문제

얼마 전 카드를 새로 만들어서 구글 결제의 결제 수단을 교체하려고 하였는데 카드 번호가 잘못되었다라고 하면서 등록이 되지 않았습니다. 관련해서 검색을 해보니 이런 문제가 흔히 발생하고 있더군요. 저처럼 당황하실 분들을 위해 구글 고객센터와 통화해 해결하면서 알아낸 것들을 공유하겠습니다.

IFTTT의 새로운 앱 'Do'

IFTTT 는 간단하게 정의하면 온라인 자동화 서비스라고 할 수 있습니다. 예를 들면 블로그에 포스팅을 하고 이 글을 SNS에 공유하기 위해서는 일일이 각 SNS마다 직접 공유 포스팅을 하거나 Buffer와 같은 서비스를 사용해 공유 포스팅을 하는 과정을 거쳐야 합니다. IFTTT는 이런 과정없이 블로그에 새로운 글이 등록되면 이것을 IFTTT에서 감지해 자동으로 SNS에 공유 포스팅이 등록되게 해줄 수 있습니다. 블로그를 예로 들었지만 IFTTT는 온라인 서비스들을 사용할때 발생할 수 있는 다양한 상황들을 감지해 자동화해줄 수 있는 편리한 서비스입이다. 이런 IFTTT에서 기존 앱의 이름을 IF로 바꾸고 별도의 새로운 앱인 'Do' 시리즈 를 출시하였습니다.

안드로이드 N의 '프리폼 윈도우 모드(freeform window mode)'

안드로이드 N의 개발자 프리뷰에서 가장 큰 변화는 멀티 윈도우 지원이었습니다. 그런데, 프리뷰가 발표된 이후 숨겨진 옵션으로 단순 멀티 윈도우가 아닌 일반 PC OS들처럼 윈도우의 크기와 위치를 자유롭게 조절할 수 있는 '프리폼 윈도우 모드(freeform window mode)'가 존재한다라는 사실이 알려지게 되었습니다. 이 윈도우 모드는 개발자 프리뷰에서는 활성화되어 있지 않았지만 구글의 공식 문서에도 언급이 되어 있는 옵션이었습니다. 어떤 모습인지 궁금하였던 '프리폼 윈도우 모드'의 작동하는 모습이 담긴 영상이 인터넷에 올라왔습니다.