기본 콘텐츠로 건너뛰기

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/)



댓글

이 블로그의 인기 게시물

안드로이드 웨어의 오레오 업데이트

제가 사용하고 있는 엘지 워치 스타일에 대한 안드로이드 오레오 업데이트가 배포되었습니다. 작년 12월초에 엘지 워치 스포츠를 시작으로 발표된 업데이트이니 2달이 걸렸네요. 안드로이드 웨어 오레오 업데이트는 OS적으로 크게 변한 것은 없고 몇가지 기능들이 추가된 업데이트입니다. 이번 업데이트에서 추가된 기능들에 대해 간단히 알아보겠습니다.

파일 관리자 (Clean File Manager) : 깔끔한 안드로이드 파일 관리 앱

안드로이드의 파일 관리 앱으로는 '아스트로 파일 관리자'나 'ES 파일 탐색기'가 유명한데 이 앱들은 저에게는 너무 번잡한 앱들입니다. 파일 관리라는 단순한 기능이 필요한 저로서는 너무 많은 기능을 가지고 있어 무겁고 인터페이스도 어색한 앱들이어서 대안을 찾다가 괜찮은 앱이 있어 소개합니다.

'파일 관리자 (Clean File Manager)'는 위 두 앱들에 비해 기본에 충실한 앱입니다. 클라우드 서비스를 지원하지도 않고 PC 접속 기능도 없습니다. 파일 관리를 제외한 기능으로는 설치된 앱 목록을 표시해주는 기능이 거의 유일한 부가 기능입니다. 그러나, 저처럼 단순한 파일 관리 앱을 원하는 사람에게는 딱인 앱이죠.