PHP 보안 이슈 해결방법: SQL 인젝션·XSS 차단과 php.ini 안전 설정 가이드

PHP 웹 애플리케이션 주요 보안 취약점 3가지와 발생 원인

PHP 웹 환경에서 발생하는 보안 이슈는 대부분 검증되지 않은 외부 입력값의 직접 처리와 기본 서버 환경 설정의 미비에서 시작됩니다. 사용자 입력값을 그대로 데이터베이스 질의문이나 HTML 브라우저 화면에 반영하면 심각한 데이터 유출이나 관리자 권한 탈취 사고로 직결됩니다.

실무 운영 환경에서 가장 빈번하게 보고되는 대표적인 취약점은 다음 세 가지 유형으로 분류할 수 있습니다.

  • SQL 인젝션(SQL Injection): 사용자가 입력한 문자열을 SQL 쿼리문으로 직접 결합할 때 발생하며, 공격자가 임의의 SQL 구문을 주입하여 데이터베이스를 무단 조회하거나 변조합니다.
  • 크로스 사이트 스크립팅(XSS): 검증되지 않은 악성 스크립트가 포함된 입력값이 웹 브라우저에 그대로 출력되어, 일반 방문자의 세션 쿠키 탈취나 브라우저 조작을 유발합니다.
  • 교차 사이트 요청 위조(CSRF): 로그인된 사용자의 권한을 도용하여 본인 의도와 무관하게 비밀번호 변경, 결제, 게시물 작성 등의 비정상 요청을 전송하게 만듭니다.





코드 수준의 핵심 보안 조치: 취약점별 방어 로직 구현

보안 취약점 조치의 기본은 소스 코드 수준에서 입력과 실행 로직의 완전한 분리를 달성하는 것입니다. 각 취약점 유형에 맞추어 표준화된 방어 로직을 적용해야 안전성을 확보할 수 있습니다.

코드 수준의 핵심 보안 조치: 취약점별 방어 로직 구현 설명 이미지

1. SQL 인젝션 차단을 위한 PDO Prepared Statement 구현

문자열 결합 방식의 쿼리 작성을 전면 중단하고, PHP 데이터 객체(PDO)의 Prepared Statement를 사용해 쿼리 구조와 입력 데이터를 엄격히 분리해야 합니다.

  1. 데이터베이스 연결 시 PDO 인스턴스를 생성하고 에러 모드를 예외(ERRMODE_EXCEPTION)로 설정합니다.
  2. 쿼리문 작성 시 사용자 입력값이 들어갈 자리에 이름 기반(:username) 또는 물음표(?) 플레이스홀더를 배치합니다.
  3. prepare() 메서드로 쿼리 템플릿을 데이터베이스 엔진에 먼저 컴파일시킵니다.
  4. bindValue() 또는 execute() 메서드에 배열 형태로 입력값을 전달하여 데이터로만 바인딩되도록 처리합니다.

2. XSS 방지를 위한 출력 데이터 정제

외부 입력값을 HTML 문서 내에 출력할 때는 브라우저가 스크립트 태그로 해석하지 못하도록 특수문자를 엔티티 코드로 변환해야 합니다. PHP 내장 함수인 htmlspecialchars()를 필수로 호출합니다.

출력 처리 시 두 번째 인자로 ENT_QUOTES 플래그를 지정하여 작은따옴표와 큰따옴표를 모두 이스케이프하고, 세 번째 인자로 UTF-8 문자 인코딩을 명시해야 안전합니다.

3. CSRF 공격 방어를 위한 일회용 토큰 검증

상태를 변경하는 모든 POST, PUT, DELETE 요청에는 세션에 저장된 비밀 토큰과 폼 전송 토큰을 대조하는 인증 절차가 필요합니다.

  1. 사용자 세션 시작 시 암호학적으로 안전한 난수(bin2hex(random_bytes(32)))로 CSRF 토큰을 생성하여 세션에 보관합니다.
  2. HTML 폼 내부의 숨김(hidden) 필드나 비동기 통신(AJAX)의 HTTP 요청 헤더에 해당 토큰을 삽입합니다.
  3. 서버 수신 컨트롤러에서 요청 값과 세션 값을 hash_equals() 함수로 안전하게 비교하여 불일치 시 처리를 즉시 차단합니다.

운영 서버 보호를 위한 php.ini 필수 보안 설정 점검

안전한 소스 코드를 작성했더라도 php.ini 환경 설정이 취약하면 서버 내부 정보가 공격자에게 노출될 수 있습니다. 운영 환경에서는 불필요한 기능과 상세 에러 출력을 엄격히 통제해야 합니다.

운영 서버 보호를 위한 php.ini 필수 보안 설정 점검 설명 이미지

  • display_errors = Off: 운영 서버에서 오류 메시지와 시스템 경로가 브라우저에 직접 출력되는 현상을 원천 차단합니다.
  • log_errors = On: 브라우저 출력 대신 서버 내부의 지정된 에러 로그 파일에만 기록되도록 설정합니다.
  • allow_url_fopen = Off 및 allow_url_include = Off: 외부 원격 서버의 파일을 include나 require로 불러와 실행하는 RFI(Remote File Inclusion) 공격을 방지합니다.
  • expose_php = Off: HTTP 응답 헤더에 PHP 버전 정보가 노출되지 않도록 숨겨 버전별 기지 취약점 공격 대상에서 제외시킵니다.
  • session.cookie_httponly = 1: 자바스크립트의 document.cookie를 통한 세션 아이디 탈취를 차단합니다.
  • session.cookie_secure = 1: HTTPS 보안 암호화 통신 상태에서만 세션 쿠키가 전송되도록 강제합니다.
  • session.cookie_samesite = “Lax” 또는 “Strict”: 외부 도메인에서 시작된 요청에 쿠키 전송을 제한하여 CSRF 위험을 낮춥니다.

배포 전후 지속 가능한 PHP 보안 점검 체크리스트

보안 조치는 1회성 작업으로 끝나지 않으므로, 정기적인 점검 체계를 운영 절차에 편입해야 합니다. 배포 주기마다 다음 항목을 확인하여 애플리케이션 무결성을 유지합니다.

  • 외부 의존성 패키지 취약점 검증: Composer 환경에서 ‘composer audit’ 명령어를 정기 실행하여 서드파티 라이브러리의 보안 권고안을 확인하고 즉시 업데이트합니다.
  • 파일 업로드 디렉터리 권한 분리: 사용자가 업로드하는 파일 보관 디렉터리에서 PHP 스크립트 실행 권한을 웹 서버(Nginx, Apache) 레벨에서 원천 박탈합니다.
  • 입력 데이터 유효성 검증 체계화: filter_var() 함수를 사용하여 이메일, IP, 정수형 등 데이터 타입의 정합성을 사전 필터링합니다.
  • 정기적인 보안 로그 모니터링: 웹 서버 접근 로그 및 PHP 에러 로그를 분석하여 비정상적인 반복 호출이나 SQL 삽입 시도를 조기 감지합니다.
WordPress Appliance - Powered by TurnKey Linux