(PyCon KR 2024) 해커의 관점으로 바라본 Django Framework

(PyCon KR 2024) 해커의 관점으로 바라본 Django Framework
PyCon KR 2024, Suwon

이번 파이콘 한국 2024에서 ‘해커의 관점으로 바라본 Django Framework’ 를 주제로 발표한 윤석찬이라고 합니다. 중학생 때부터 컴퓨터를 잘하시는 분들이 발표하러 가셨던, 그래서 열망의 대상이었던 '파이콘'에서, 그것도 10주년 기념 행사에서 발표를 하게 되어 정말 영광스러웠습니다. 네트워킹을 하면서 발표자 / 참가자 / 기획 및 자원봉사자 분들의 열정을 얻을 수 있었기에 의미있는 자리였습니다. 특히 짧은 시간이었지만 열린 공간에서 존경하는 나동희 개발자님과 CPython에 대해 이야기를 나눌 수 있어 영광이었습니다.

파이콘처럼 큰 규모의 컨퍼런스에서 발표하는 것이 익숙지 않아 많이 긴장한 상태였기에, 준비한 내용을 제대로 발표하지 못해서 아쉬움이 크게 남습니다. 그래서 제 블로그에 제가 발표하고자 했던 내용을 정리해서 공유드리고자 합니다.

저는 현재 경희대학교 컴퓨터공학과에 재학 중이고 GitHub, Python, Django, Airflow, Ruby, Ruby-on-Rails, Java Spring 등 여러 대형 OSS 프로젝트에 지속적으로 취약점을 찾아 제보하여 Security Contribute를 하고 있습니다. 제 깃허브는 아래에 링크해두었으니 관심 있으신 분들은 제 프로필을 한번 봐주시길 바랍니다.

ch4n3-yoon - Overview
Web Security Researcher / Majoring CSE @ KyungHee University / Security Contributor of GitHub, Python, Django, Airflow, Ruby, Ruby-on-Rails, Java Spring - ch4n3-yoon
[PyConKR2024] 해커의 관점에서 바라본 Django.pdf

Django의 기본적인 보안 기능들

첫번째로 전통적인 공격 기법인 XSS, SQL Injection, CSRF, Clickjacking, Password Cracking 에 대해 Django에서는 어떤 방어 전략을 쓰고 있는지 알아보았습니다.

XSS (Cross Site Scripting)

XSS는 'Cross Site Scripting'의 줄임말로, 지금까지도 웹 환경에서 가장 빈번하게 발생되는 취약점입니다. HTML을 통해 특정 값을 출력할 때 주로 발생합니다. 약 10년 전 중학생이었던 제가 처음 웹 해킹을 접했을 시절, 우리나라 웹 환경은 주로 PHP와 JSP를 기반으로 운영되었는데 XSS 취약점이 없던 서비스가 없었을 정도로 개발자들이 신경쓰지 않으면 발생하기 쉬운 취약점이었습니다.

우선 PHP 같은 고전적인 웹 스택에서는 해당 취약점이 어떤 경우에 발생하는지부터 확인해보겠습니다.

<p>Hello <?php echo $_GET['nickname']; ?></p>

PHP에서는 $_GET['파라미터 이름'] 형식으로 사용자로부터 전달받은 파라미터에 접근할 수 있습니다. 따라서 위 코드에서 사용자가 <script>prompt(document.domain)</script> 같이 HTML 코드를 포함한 문자열을 입력한다면 어떻게 될까요

서버는 아래와 같은 HTML 코드를 반환하게 됩니다.

<p>Hello <script>prompt(document.domain)</script></p>

이 HTML을 브라우저에서 렌더링하게 되면서 개발자가 의도하지 않았던 javascript 코드가 실행됩니다. nickname 값을 GET으로 받았기 때문에 URL 상으로는 http://example.com/?nickname=<script>prompt(document.domain)</script> 같이 표현되겠습니다. 공격자는 이 취약한 링크를 타 사용자로 하여금 링크 클릭을 유도하여, 타 사용자의 환경에서 임의의 자바스크립트 코드를 실행시킬 수 있습니다.

클라이언트 화면에서 자바스크립트가 실행되는 것이 왜 위험한지 의아해하시는 분들도 계실 것 같습니다. 클라이언트 화면에서 공격자가 작성한 임의의 자바스크립트 코드가 실행되는 것이 위험한 이유는, 해당 화면에 접근한 사용자로 하여금 의도하지 않은 행위를 유도할 수 있기 때문입니다. 예를 들어 해커가 <script> 태그 내에 alert() 함수 대신 fetch() 같은 함수를 통해 결제 API를 호출하거나, 회원 정보를 수정하는 API를 호출하는 코드를 넣어두었다면 일반사용자들에게 피해가 될 수 있었겠죠.

Django에서는 해당 취약점을 Template 기능을 통해 방어하고 있습니다.

<p>Hello {{ nickname }}</p>

해당 템플릿에 사용자로부터 전달받은 nickname 값을 넣어 Django의  render() 함수를 수행하면, views.py에서 아무런 값 검증 없이도 아래처럼 XSS 공격 코드가 HTML Entity로 인코딩되어 아무런 영향을 미치지 못하도록 합니다.

<p>Hello &lt;script&gt;prompt(document.domain)&lt;/script&gt;</p>

SQL Injection

두번째로 알아볼 취약점은 SQL Injection입니다. 취약점 자체의 이름에서도 알 수 있다시피, SQL을 사용하는 환경에서 발생하는 코드 주입(Code Injection)류의 공격입니다.

SQL은 데이터베이스에서 값을 가져오는데 사용되는 일종의 프로그래밍 언어이며, 웹 서비스에서 데이터베이스와의 통신을 통해 CRUD 기능을 구현 서비스라면 높은 확률로 SQL이 사용됩니다. 이 글에서는 SQL 코드를 ‘쿼리(Query)’라고 부르도록 하겠습니다.

SQL Injection은 쿼리에 공격자의 코드를 삽입해서 데이터를 조작하거나 탈취하는 공격입니다. 보통 SQL 쿼리를 직접 작성하는 환경에서, 사용자의 입력이 포함되는 경우 많이 발생하는 취약점입니다.

그럼 아래 Python 코드에서 Django ORM의 사용없이 SQLite3를 통해 사용자 정보를 불러오는 예시를 보겠습니다.

import sqlite3

def search_user(username):
    query = f"SELECT * FROM user WHERE username = '{username}'"
    Connection = sqlite3.connect("db.sqlite3")
    cursor = connection.cursor()
    cursor.execute(query)
    user = cursor.fetch()

    connection.close()
    return users

user = search_user("' OR 1=1– -")

해당 코드는 사용자로부터 `username` 값을 받아 SQL 쿼리를 만들고, 이를 execute 합니다.

위 코드는 ORM 없이 connection 부터 처음부터 작성해보았습니다. 아시다시피 데이터베이스 통신을 위해서는 아래 단계의 코드가 순차적으로 수행되어야 합니다.

  1. Database Connection 생성
  2. Cursor 생성
  3. Query 실행
  4. 결과 fetch

위 단계의 코드를 적재적소에 배치하여 사용해야 했으나, Django ORM은 이 과정을 좀더 쉽게 만들어주어 생산성을 높였습니다.

또, 만약 Django ORM이 등장하지 않았다면 각 기능마다 수백가지의 쿼리를 작성해야 할 수도 있었습니다. 수백 수천 가지의 쿼리가 필요한 대규모 개발 환경에서 이런 Static하게 만들어지는 쿼리가 많아질 경우 SQL Injection 공격의 타겟이 되기 쉽습니다. 이를 감안하면 Django ORM의 등장은 얼마나 큰 혜택이 되었는지 모르겠습니다.

Django ORM 기능을 활용하면 데이터베이스 쿼리를 반복적으로 작성할 필요 또한 없어지고, ORM 자체에서 사용자의 입력값을 안전하게 처리하기 때문에 SQL Injection을 방지할 수 있습니다.

from .models import User
from django.shortcuts import render


def search_user(request):
    username = request.GET.get("username", "")
    users = User.objects.filter(username=username)
    return render(request, "user_list.html", {"users": users})

이전의 정적 쿼리를 작성해 통신하는 방법을 Django ORM을 통해 개선하면 위 같은 코드가 됩니다. 특히 Django ORM에서는 특수문자를 안전하게 받아 처리하기 때문에, 사용자로부터 받은 username 값을 직접 filter() 함수의 인자로 전달해도 아무런 문제가 없습니다.

username = "' or 1=1-- -"
users = User.objects.filter(username=username)
SELECT * FROM users WHERE username = '\' or 1=1-- -'

실제로 Django ORM이 실질적으로 실행하는 SQL 쿼리를 관찰해보면 아래같이 특수문자 앞에 백슬래시가 붙는 식으로 안전하게 처리가 되는 것을 보실 수 있습니다.

CSRF

최근에는 CORS 설정 및 HTTP 정책 변경으로 브라우저에서 정교하게 XSS를 방어할 수 있게 되었지만, 사실 근 몇 년 전까지만 하더라도 CSRF가 XSS 취약점의 심각도를 저하시킬 수 있는  현실적인 방어 전략이었습니다.

Django로 개발해보신 분들이라면 POST 메소드로 파라미터를 보낼 때 한번쯤은 “CSRF Failed” 메시지를 마주해보신 경험이 있을텐데요, 이는 Django의 django.middleware.csrf.CsrfViewMiddleware 미들웨어가 작동한 결과입니다.

Clickjacking

Clickjacking은 공격자가 사용자의 클릭을 가로채어, 본래 의도와는 다른 행동을 하게 만드는 공격 기법입니다. 주로 <iframe> 태그를 사용하여 공격 대상 사이트를 투명하게 오버레이하고, 사용자가 무언가를 클릭할 때 의도하지 않은 동작이 실행되도록 만듭니다.

예를 들어, 사용자가 웹 페이지에서 아무것도 모른 채 버튼을 클릭했는데, 사실은 공격자가 삽입한 악의적인 사이트 위에서 실행되는 중요한 액션을 클릭하게 되는 것입니다.

Django는 이러한 Clickjacking 공격을 XFrameOptionsMiddleware를 통해 방어합니다. 이 미들웨어는 모든 응답 헤더에 X-Frame-OptionsDENY로 설정하여, 외부 사이트에서 해당 페이지를 <iframe>으로 로드할 수 없도록 합니다.

Password Cracking

웹 서비스를 운영 중이라면 비밀번호를 안전하게 저장하는 것이 매우 중요합니다. Django는 이를 위해 기본적으로 PBKDF2PasswordHasher를 사용합니다. 이 해시 알고리즘은 비밀번호 해시를 단순히 한 번 생성하는 것이 아니라, SHA256 알고리즘을 720,000번 반복하여 계산합니다.

이 방식의 장점은 해커가 비밀번호 해시를 크랙하기 위해 많은 자원을 소모하게 만드는 것입니다. 반복 횟수가 많을수록, 해커가 같은 비밀번호를 여러 번 시도하면서 해시를 크랙하기 어려워집니다. 또한 settings.py에 설정된 SECRET_KEY가 salt 값으로 첨가되기 때문에 데이터베이스에 저장된 패스워드가 유출되더라도 안전하다고 볼 수 있겠습니다.

Why still 1-day Vulnerabilities?

*1-day: 취약점에 대한 정보가 공개되고, 패치 버전이 존재하는 취약점

고전적인 해킹 기법에 대한 방어 전략이 철저한 Django라고 했는데, 왜 Security Releases는 계속 나오는걸까요?

django-annouce 이메일

최근들어 Django 프레임워크에 보고되어 패치된 보안 취약점들을 살펴보도록 하겠습니다.

  1. CVE-2024-45230: django.utils.html.urlize 모듈에서 특정 문자 시퀀스를 포함한 매우 큰 입력을 처리할 때 서비스 거부(DoS) 공격이 발생할 수 있는 취약점.
  2. CVE-2024-45231: django.contrib.auth.forms.PasswordResetForm 클래스에서 이메일 전송 실패를 처리하지 않아, 공격자가 비밀번호 재설정 요청을 통해 사용자 이메일을 열거할 수 있는 취약점.
  3. CVE-2024-42005: JSONField를 포함한 모델의 QuerySet.values()values_list() 메서드에서 전달된 인자를 통해 SQL Injection 공격이 가능한 취약점.
  4. CVE-2024-38875: django.utils.html.urlize 모듈에서 대량의 괄호를 포함한 특정 입력을 처리할 때 DoS 공격이 발생할 수 있는 취약점.
  5. CVE-2024-39329: django.contrib.auth.backends.ModelBackend.authenticate 메서드에서 사용 불가능한 비밀번호를 가진 사용자의 로그인 요청 시 시간 차이를 통해 사용자 이름을 열거할 수 있는 취약점.
  6. CVE-2024-39330: django.core.files.storage.Storage.save 메서드를 재정의한 하위 클래스에서 파일 경로 검증이 누락되어 Path Traversal 공격이 가능한 취약점.
  7. CVE-2024-39614: django.utils.translation.get_supported_language_variant 함수에서 특정 문자를 포함한 매우 긴 문자열을 처리할 때 DoS 공격이 발생할 수 있는 취약점.
  8. CVE-2023-43665: Django의 텍스트 Trim 기능에서 지나치게 긴 HTML 입력을 올바르게 처리하지 못하여 DoS 공격이 가능한 취약점.
  9. CVE-2023-41164: Django에서 광범위한 유니코드 문자가 포함된 특정 URI를 잘못 처리하여 DoS 공격이 가능한 취약점.
  10. CVE-2023-36053: Django가 이메일, 도메인을 문자열을 복잡한 정규식을 사용해 검색하여 대량의 문자열을 처리하는 데 문제가 있어 DoS 공격이 가능한 취약점.

특정 메소드에서 대량의 문자열을 인자로 전달받을 때 발생 가능한 DoS 류의 취약점이 많이 보고되는 것을 보실 수 있습니다. DoS 취약점 외에도 CVE-2024-39329, CVE-2024-45231은 사용자 인증 관련, CVE-2024-42005은 SQL Injection 관련 이슈를 지적하고 있네요.

위 취약점 목록을 보시면 아시겠지만, '프레임워크'이다 보니 특정 메소드를 사용했을 때, 혹은 특정 기능이 활성화 되었을 때 등 코드 환경에 따라 취약점의 발생 가능성이 달라지는 것을 보실 수 있습니다. 예를 들어 사용자 인증과 관련된 2건의 취약점은 이메일 인증 기능이 활성화되어야 하고, 사용자의 비밀번호가 특정 조건에 맞춰져 있도록 설정되어야 하는 전제조건이 있고, SQL Injection과 관련된 취약점은 검색 기능에서 JSONField 가 사용되어야 하고, 사용자의 입력이 kwargskey 값으로 들어가야 하는 전제조건이 있기 때문에 발생할 확률이 낮은 취약점이라고 보실 수 있겠습니다.

그럼 이 예시로 두가지 취약점을 분석해보면서 Django는 어떤 취약점을 ‘보안 위협’으로 간주하고 보안 업데이트를 릴리즈하는지 살펴보도록 하겠습니다.

CVE-2022-34265: SQL Injection

이 취약점은 Django ORM을 이용할 때 Trunc()Extract() 함수 사용 시 발생할 수 있었던 SQL Injection 취약점입니다. 아래 코드와 함께 어떤 취약점인지 살펴보도록 하겠습니다.

lookup_name = request.GET.get("lookup_name", "")
Users.objects.filter(created_at__year=Trunc("created_at", lookup_name))

위와 같은 코드에서 lookup_name 값이 아래와 같을 경우

aa' /* injection */ 'aa

최종적으로는 아래와 같은 SQL이 생성됩니다.

SELECT `id`, … ,
    django_datetime_trunc('aa' /* injection */ 'aa', `create_at` …) AS `created_at`
FROM `users`

이 문제가 발생한 이유는 Django의 쿼리셋 메소드에서 특정 데이터베이스 함수에 대한 인자 검증이 충분히 이루어지지 않았기 때문입니다. 화면의 SQL 쿼리 예제를 보시면 lookup_name 인자가 쿼리 내에서 django_datetime_trunct() 함수의 인자로 들어가고 있고, 이 때 입력받은 인자가 그대로 SQL 쿼리 생성에 사용되면서 SQL Injection이 발생합니다.

CVE-2023-36053: Regular Expression DoS

이 취약점은 Django의 EmailValidatorURLValidator 컴포넌트에서 발견된 정규표현식 기반 서비스 거부(ReDoS) 취약점입니다. 공격자는 이메일 주소나 URL의 도메인 부분에 매우 많은 수의 레이블을 포함시켜, 정규 표현식 검증 과정에서 과도한 리소스 소비를 유발할 수 있습니다.

아래는 해당 취약점의 영향을 받을 수 있었던 코드의 예시입니다.

from django.core.validators import EmailValidator

def validate_email(request):
    email = request.POST.get('email')
    email_validator = EmailValidator()
    try:
        email_validator(email)
        return HttpResponse("Valid email")
    except ValidationError:
        return HttpResponse("Invalid email")

사용자로부터 입력받은 이메일 문자열을 EmailValidator를 통해 유효 여부를 검증할 경우 Regular Expression Denial of Service에 취약했습니다.

해당 취약점으로 영향을 받는 컴포넌트는 EmailValidatorURLValidator로, 이 클래스들은 이메일 주소와 URL의 도메인을 검증하기 위해 복잡한 정규표현식을 사용합니다.

import re

domain_regex = re.compile(
    r"((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)(?:[A-Z0-9-]{2,63}(?<!-))\Z",
    re.IGNORECASE
)

이 정규표현식은 위와 같이 매우 복잡하게 구성되어 있으며, 입력 길이에 제한이 없고 반복적인 패턴이 포함되어 있었다는 점입니다. 이러한 특성 때문에 공격자가 대량의 반복적인 도메인을 포함한 문자열을 입력할 경우, 정규표현식 매칭 과정에서 과도한 시간이 소요되어 CPU 자원을 고갈시키고, 결국 서비스 거부 상태를 유발할 수 있습니다.

1-day와 결론

지금까지 Django의 몇 가지 보안 취약점에 대해 살펴보았습니다. 그러나 이러한 취약점들이 공개되었음에도 불구하고, 저는 Django가 여전히 안전한 프레임워크라고 믿습니다.

먼저, 최근 공개된 보안 이슈들은 실제로 발생할 가능성이 매우 낮습니다. 예를 들어, SQL Injection 취약점의 경우, 일반적으로 잘 사용되지 않는 특정 기능을 안전하지 않은 방식으로 사용할 때 트리거됩니다. (e.g. CVE-2022-34265의 경우 Trunc() 를 사용하는 코드여야 한다는 점, lookup_name 에 사용자의 입력이 직접 들어가야 했습니다) 대부분의 개발자들은 이러한 기능을 일상적으로 사용하지 않기 때문에, 실제 위험은 상당히 제한적입니다.

DoS 취약점의 경우도 마찬가지입니다. 이 취약점을 악용하기 위해서는 비정상적으로 큰 입력값이 필요합니다. 대부분의 애플리케이션에서는 이러한 극단적인 입력을 허용하지 않으므로, 실제 위험은 크지 않을 것으로 생각됩니다. 더불어 Django의 취약점들은 대부분 특정 코드 패턴을 사용했을 때만 발생합니다. (e.g. CVE-2023-36053의 경우엔 EmailValidator로 사용자의 입력값이 그대로 인자로 전달되어야 했습니다) 또한 공격자가 웹 서비스의 내부 소스코드를 정확히 알지 못한다면, 이러한 취약점을 트리거하기는 매우 어렵습니다.

결론적으로, 이러한 이유들로 인해 저는 'Django는 여전히 안전하다'고 확신합니다. Django 개발팀은 지속적으로 보안 이슈를 모니터링하고 신속하게 대응하고 있으며, Django Security 팀에 제보된 보안 이슈를 책임감있게 공개하고 있기 때문입니다. 이에 저는 Django는 여전히 웹 개발에 있어 가장 안전하고 신뢰할 수 있는 프레임워크 중 하나라고 생각합니다.

다만 개발자가 놓치기 쉬운 부분


‘Django가 안전하다’는 말에는 의심의 여지가 없습니다. 다만, Django 기반의 웹 서비스에서도 쉽게 발견될 수 있는 취약점들은 있습니다. 마지막으로, 개발자 분들이 간과할 수 있는 코드 패턴을 소개하고 글을 줄이고자 합니다.

XSS

이제 Django에서 XSS 취약점이 발생할 수 있는 4가지의 주요 경우들에 대해 살펴보겠습니다. 이러한 상황들은 개발자가 주의하지 않으면 충분히 발생할 수 있는 부분이라고 생각합니다.

응답 시, HttpResponse()를 사용하는 경우

return HttpResponse(f"Hello {user_input}")

Django Template 엔진을 거치지 않기 때문에 별도로 HTML entity로 인코딩되지 않으니 주의해야 합니다.

템플릿 필터 safe를 사용하는 경우, 템플릿 내에서 {% autoescape off %}를 사용한 경우

<div>{{ user_input|safe }}</div>
<!-- or --> 
{% autoescape off %}

이는 최근 파이썬으로 리펙토링된 그누보드 6에서도 발견될 정도로 발생하기 쉬운 취약점이기 때문에 주의해야 합니다.

[fix] safe 필터 사용으로 인한 Reflected XSS 발생 가능성 제거 by ch4n3-yoon · Pull Request #638 · gnuboard/g6
변경 사항 basic, bootstrap 테마의 alert.html 파일에서 에러메시지를 출력할 때 사용되는 safe 템플릿 필터를 제거했습니다. 관련 이슈 없습니다. 기타 정보 https://g6.demo.sir.kr/board/%22%5Eprompt(document.domain)%5E%22/1 PR ChecklistPR이 다음 요구 ...

href 속성에 URI 형식 검증이 없는 경우

<a href="javascript:alert(1)">click me</a>

SQL Injection

사용자로부터 전달받은 파라미터를 String Concatenation, Format String 등 안전하지 않은 방법으로 SQL 쿼리를 작성할 때 주의가 필요합니다.

특히 ORDER BY 구문의 경우 유의해서 코드를 작성할 필요가 있습니다. ORDER BY 구문에 들어가는 컬럼 이름이나 차순(ASC, DESC)을 설정할 때 똑같이 %s로 바인딩 하게된다면 쿼터로 감싸진 string으로 인식하기 때문에 SQL Syntax Error가 발생하게 됩니다. 이런 기능에서 안전하지 않은 문자열 concatenation으로 쿼리를 작성하는 경우가 꽤 발생하기 때문에 주의가 필요해보입니다.

Third Party Library

이 취약점은 특정 버전의 Django REST Framework에서 발생하는 크로스 사이트 스크립팅 취약점입니다. 문제는 break_long_headers 템플릿 필터에서 입력값을 충분히 검증하지 않아 발생합니다. 이 필터는 헤더를 <br> 태그로 나누고 합치는 과정에서 사용자 입력을 제대로 이스케이프하지 않아 악성 코드가 실행될 수 있었던 취약점입니다. 이와 관련된 더 자세한 정보는 아래 제가 작성한 글에서 확인해주시면 감사하겠습니다.

Potential XSS bug found in Django Rest Framework
안녕하세요 웹 해킹을 하고 있는 윤석찬입니다. 오랜만에 인사드립니다. 이번에는 한국 분들에게도 제 블로그를 노출시키고자, 한국어로 글을 쓰게 되었습니다. 최근 Django Rest Framework 소스코드를 살펴보다가, 부주의하게 사용했을 경우 XSS 공격으로부터 취약할 수 있는 기능을 발견해서 블로그에 정리해봅니다. 이미 Django Security 팀에는 report를 되어 인지된 이슈이기 때문에 블로그에 게시해도 되겠다 싶었고, 무엇보다

다만 Django 팀에서 보안 취약점을 관리하는 rest framework 라이브러리에서 조차 해당 취약점을 널리 알리지 않을 정도로, 서드파티 개발팀에서 적극적으로 공지하지 않는 경우도 있기 때문에 팀에서 적절한 업데이트 전략을 세우는 것이 중요하다고 생각합니다.

정기적으로 업데이트하고, 사용 중인 라이브러리의 보안 공지를 모니터링하며, 라이브러리를 사용하시기 전에 코드 리뷰 및 테스트를 해보시는 걸 강력하게 권장드립니다.

SSRF

이번에는 SSRF(Server-Side Request Forgery) 취약점에 대해 알아보겠습니다.

SSRF는 서버 사이드 요청 위조로, 공격자가 서버로 하여금 원래 의도하지 않은 목적지로 요청을 보내도록 유도하는 취약점입니다. 이를 통해 공격자는 서버를 프록시처럼 이용하여 내부 네트워크나 보호된 리소스에 접근할 수 있습니다. 이러한 접근은 내부 시스템 정보 유출, 데이터베이스 침해, 심각한 경우 시스템 전체의 보안 위협으로까지 이어질 수 있겠습니다.

Django에서 SSRF는 주로 사용자가 제공한 URL을 서버 측 요청에 사용할 때 발생합니다. 예를 들어, 아래처럼 requests 라이브러리를 사용하여 사용자 입력을 그대로 URL로 사용하는 경우가 있겠습니다.

import requests

def fetch_url(request):
    url = request.GET.get('url', '')
    response = requests.get(url)
    return HttpResponse(response.text)

위 같은 코드 기반 서비스를 통해 file:///etc/passwd 로 내부 리소스를 유출하거나, http://localhost/admin 등 internal function 으로 접근하거나, http://localhost:22/ 등 포트스캔을 수행할 수 있습니다.

Leaked SECRET_KEY

취약점을 검색해보면 꽤 발생되는 버그 유형임을 알 수 있습니다.

Django 블로그에서도 권한 상승 (Privilege Escalation)과 원격 코드 실행 (Remote Code Execution) 공격으로 이어질 수 있음을 명시해두었습니다. 특히 Django 4점대 버전까지 사용자 세션 관리에 사용 가능했던 PickleSerializer를 사용한다면 SECRET_KEY 유출 만으로 원격 코드실행이 가능했습니다.

아래 자세한 내용은 레포지토리를 참고하시길 바랍니다.

GitHub - ch4n3-yoon/django-pickleserializer-rce-poc: Proof of Concept (PoC) repository demonstrating the Remote Code Execution (RCE) vulnerability in Django’s PickleSerializer (up to version 4.0). This repository provides examples and explanations to understand the exploit and its impact
Proof of Concept (PoC) repository demonstrating the Remote Code Execution (RCE) vulnerability in Django&#39;s PickleSerializer (up to version 4.0). This repository provides examples and explanation...

다행히 현재 Django LTS 버전에서는 이 문제를 인식하고 PickleSerializer를 완전히 제거했습니다. 이로 인해 SECRET_KEY 노출만으로는 직접적인 원격 코드 실행으로 이어지지는 않습니다. 그렇지만 SECRET_KEY 는 암호화 등에 사용되는 중요한 설정이기 때문에 유출되었다면 빠른 대처가 필요합니다.

Denial of Service

아직 Django가 DoS에 영향을 받을 만한 코드는 많습니다. 최근 Django의 Cookie-parsing 로직에서 사용되는 CPython의 특정 메소드에서 Potential Denial of Service 취약점을 찾으면서 HTTP request를 파싱하는 부분을 집중적으로 살펴보았는데, Django 답지 않게 레거시 코드가 많다고 느꼈습니다.

Django 사용자들이 무조건 Python 버전 업데이트를 해야하는 이유
안녕하세요. 윤석찬입니다. 최근 제가 Python의 CPython 라이브러리에 제보한 취약점이 공개되었습니다. CVSS 3.x 기준 Serverity는 10점 만점 중 7.5점으로 심각도는 ‘높음’으로 평가되었고, 해당 취약점을 악용하면 인터넷에 노출된 모든 Django 서버에 DoS를 유발시킬 수 있었습니다. 본 글에서는 파이썬의 표준 라이브러리 중 하나인 http.cookies 모듈의 _unquote() 메소드에서 발견된 서비스 거부(

HTTP 파싱 로직은 인터넷을 통해 접근 가능한 모든 Django 서버가 영향을 받을 수 있습니다. 따라서 대량의 크기의 문자열로 구성된 POST 되도록이면 꼭 nginx, apache 같은 프록시를 사용하시고 Header나 POST data의 사이즈 제한을 변경하지 않도록 주의하시길 바랍니다.

이것으로 PyCon KR 2024에서의 제 발표를 정리하겠습니다. 끝까지 봐주신 분들 모두 감사합니다!