Computer >> 컴퓨터 >  >> 네트워킹 >> 네트워크 보안

PHP SQL 인젝션(SQL Injection): 반드시 알아야 할 모든 것

다양한 인젝션(Injection) 공격 중에서 PHP SQL 인젝션 공격이 가장 두드러지게 나타나고 있습니다. 실제로 상당수의 웹사이트가 이 공격에 취약하며, 일부 조사에 따르면 웹에서 발생하는 공격의 절반 이상이 SQL 인젝션을 통해 이루어진다고 합니다. 이 글에서는 SQL 인젝션이 어떻게 수행되는지, 그리고 PHP 애플리케이션에서 이를 어떻게 방지할 수 있는지 자세히 살펴보겠습니다.

SQL 인젝션(SQLi)이란 무엇인가?

먼저 SQL 인젝션의 개념부터 이해해 보겠습니다. SQL 인젝션(SQLi)은 해커가 실행되는 SQL 명령문을 변조하여 백엔드에서 구동 중인 SQL 문장을 조작하는 공격 기법입니다. 이러한 인젝션은 주로 폼(form)의 입력 필드를 통해 이루어지며, 데이터베이스에 심각한 피해를 초래합니다. 그 결과 데이터베이스에 저장된 매우 민감한 정보가 외부로 노출될 수 있습니다.

해커는 SQL 명령을 주입함으로써 데이터베이스에 대한 제어권을 획득하게 되며, 이후 데이터를 삽입하거나 수정하고 심지어 삭제할 권한까지 갖게 됩니다.

SQL 인젝션이 발생하는 원인

SQL 인젝션 공격은 코드가 안전하지 않게 작성되었을 때 발생합니다. 겉보기에는 안전하게 작성된 것처럼 보여도 여전히 SQLi에 노출되는 대표적인 사례는 다음과 같습니다.

  • 공백 문자 및 타입 처리에 대한 부적절한 필터링
  • 검증되지 않았거나 잘못 인코딩된 데이터를 데이터베이스에 전달하는 경우
  • 코드와 데이터가 뒤섞이는 경우
  • 따옴표를 사용해 문자열을 구분하는 방식

요컨대, 사용자 입력이 적절히 검증되지 않으면 해당 데이터가 악의적인 의도로 해석되어 데이터베이스에 전달될 수 있습니다. 특히 프론트엔드에서 받은 입력값을 별도의 검증 없이 쿼리에 직접 삽입하면 시스템이 손상될 위험이 큽니다.

또 다른 문제는 오류 메시지가 사용자에게 그대로 노출되는 경우입니다. 악의적인 사용자는 이러한 오류 정보를 통해 데이터베이스의 종류나 버전 등을 파악할 수 있으며, 이를 바탕으로 SQL 인젝션 공격에 활용되는 익스플로잇(exploit)을 배포할 수 있습니다.

PHP SQL 인젝션이란?

취약한 코드 예시

다음의 취약한 PHP 코드를 통해 PHP SQL 인젝션 공격의 개념을 이해해 보겠습니다.

<?php
$username = $_POST['uname'];
$pass = $_POST['password'];
$sql = "SELECT * FROM user_table WHERE username='".$username."' AND userpwd='".$pass."'";
?>

위 코드는 사용자 입력을 아무런 검증 없이 그대로 받아들여 SQL 쿼리에 직접 삽입한 후 데이터베이스에서 실행합니다. 해커는 논리적으로 항상 참(true)이 되는 쿼리를 삽입할 수 있습니다. 예를 들어 $password 변수 값이 ' OR '1'='1로 전달되면 조건식이 1=1, 즉 항상 참이 되어 인증 없이 로그인에 성공하게 되고, 이는 심각한 보안 사고로 이어질 수 있습니다.

오류가 사용자 화면에 그대로 출력되는 경우도 문제입니다.

<?php
...
...
elseif($mysqli->error)
{
    print($mysqli->error);
}
?>

이러한 경우 오류 로그를 사용자 화면에 출력하기보다는, 별도의 로그 파일이나 데이터베이스에 기록해 두고 나중에 웹사이트가 SQLi 공격을 받은 횟수 등을 분석하는 데 활용하는 것이 훨씬 안전한 방법입니다.

안전한 코드 예시

PHP SQL 인젝션을 방지하기 위한 안전한 코딩 방법으로, 다음과 같이 사용자 이름과 비밀번호에 포함된 논리적 쿼리 문자열을 검증할 수 있습니다.

<?php
function checkUserInput($str)
{
    return str_replace(array("'", '"', "\\", "`"), array("''", '&quot;', '&#92;', '&#96;'), $str);
}

$username = checkUserInput($_POST['uname']);
$pass = checkUserInput($_POST['password']);
?>

이처럼 사용자 입력을 SQL 쿼리에 추가하기 전에 먼저 검증할 수 있습니다.

PHP SQL 인젝션을 방지하는 또 다른 강력한 방법은 프리페어드 스테이트먼트(Prepared Statements), 즉 PDO(PHP Data Objects)를 사용하는 것입니다. 이는 SQL 문장을 반복적으로 실행할 수 있게 해주는 효율적인 방식으로, PDO가 제공하는 메서드를 활용하면 파라미터화된 쿼리(parameterized query)를 손쉽게 사용할 수 있습니다. 또한 코드의 가독성과 이식성이 향상되며, MySQL뿐 아니라 다른 종류의 데이터베이스와도 함께 사용할 수 있습니다. PDO는 어느 부분을 사용자 입력으로 처리하고 어느 부분을 데이터베이스 명령으로 처리할지 명확히 구분해 줍니다.

MySQL 데이터베이스에 PDO를 적용한 코드 예시는 다음과 같습니다.

// DB 접속 정보
define('DB_HOST', 'localhost');
define('DB_USER', 'John_Reaper');
define('DB_PASS', 'admin@wsxdn.com@$$w0rd$');
define('DB_NAME', 'users');

if(isset($_GET['id']))
{
    $id = $_GET['id'];
    if(is_numeric($id) == true)
    {
        try
        {
            $dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
            $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $q = "SELECT username FROM users WHERE id = :id";
            $sth = $dbh->prepare($q);
            $sth->bindParam(':id', $id);
            $sth->execute();
            $sth->setFetchMode(PDO::FETCH_ASSOC);
            $res = $sth->fetchColumn();
            print(htmlentities($res));
            $dbh = null;
        }
        catch(PDOException $e)
        {
            error_log('PDOException - '.$e->getMessage(), 0);
            http_response_code(500);
            die('Connection to database unsuccessful');
        }
    }
    else
    {
        http_response_code(400);
        die('Error processing malformed request');
    }
}

PHP SQL 인젝션 방지 방법

다음과 같은 모범 사례를 적용하면 웹사이트를 SQL 인젝션으로부터 보호할 수 있습니다.

  1. 사용자 입력 검증: 프론트엔드에서는 JavaScript로, 백엔드에서는 PHP로 사용자 입력을 반드시 검증합니다.
  2. 오류 메시지 숨기기: 오류 내용을 사용자에게 직접 표시하지 말고 파일에 기록하여 공격자가 접근할 수 없도록 관리합니다. 사용자에게 보여지는 오류 메시지는 최대한 일반적으로 작성하여 데이터베이스 관련 정보가 노출되지 않도록 해야 합니다.
  3. 파라미터화된 쿼리 사용: SQL 쿼리 중 어떤 부분을 사용자 입력으로 취급할지 명확히 지정하는 프리페어드 스테이트먼트를 활용합니다.
  4. 불필요한 저장 프로시저 제거: 저장 프로시저는 공격에 쉽게 악용될 수 있으므로, 사용하지 않는 것은 삭제하여 SQL 인젝션 가능성을 줄입니다.
  5. 최소 권한 원칙 적용: 사용자별로 적절한 권한만 부여하여, 설령 공격자가 인증을 우회하더라도 피해를 최소화합니다.

PHP SQL 인젝션으로부터의 완벽한 보호

Astra와 같은 웹 방화벽(WAF)을 도입하면 SQLi에 대한 방어력을 크게 높일 수 있습니다. Astra Security Suite는 SQLi뿐 아니라 XSS, CSRF, 악성 봇(bad bots) 등 100가지 이상의 공격으로부터 웹사이트를 보호합니다.

PHP SQL 인젝션(SQL Injection): 반드시 알아야 할 모든 것

Astra의 악성코드 스캐너는 10분 이내에 웹사이트 전체를 검사하며, 이후 스캔에는 1분 미만의 짧은 시간이 소요됩니다. 또한 보안 감사(Security Audit) 및 모의 해킹(Pentesting) 서비스를 통해 Astra 엔지니어가 코드 한 줄 한 줄을 직접 검토하여 웹사이트의 취약점을 찾아내고 수정함으로써, 뚫을 수 없는 수준의 보안을 완성합니다.

PHP SQL 인젝션(SQL Injection): 반드시 알아야 할 모든 것

지금 바로 Astra 데모를 신청해 보세요!