[시큐어코딩 가이드] 경로 조작 및 자원 삽입

 

 

 

■ 입력데이터 검증 및 표현

 

프로그램 입력 값에 대한 검증 누락 또는 부적절한 검증, 데이터의 잘못된 형식지정, 일관되지 않은 언어셋 사용 등으로 인해 발생되는 보안약점으로 SQL 삽입, 크로스사이트 스크립트(XSS) 등의 공격을 유발할 수 있다.

 

 

경로 조작 및 자원 삽입

 

■ 개요

 

검증되지 않은 외부 입력값을 통해 파일 및 서버 등 시스템 자원(파일, 소켓의 포트 등)에 대한 접근 혹은 식별을 허용할 경우, 입력 값 조작을 통해 시스템이 보호하는 자원에 임의로 접근할 수 있는 보안약점이다. 경로조작 및 자원삽입 약점을 이용하여 공격자는 자원의 수정·삭제, 시스템 정보누출, 시스템 자원 간 충돌로 인한 서비스 장애 등을 유발시킬 수 있다.


즉, 경로 조작 및 자원 삽입을 통해서 공격자가 허용되지 않은 권한을 획득하여, 설정에 관계된 파일을 변경하거나 실행시킬 수 있다.


Python에서는 subprocess.popen()과 같이 프로세스를 여는 함수, os.pipe()처럼 파이프를 여는 함수 , socket 연결 등에서 외부 입력값을 검증 없이 사용할 경우 경로 조작 및 자원 삽입의 취약점이 발생할 수 있다.

 

 

■ 안전한 코딩 기법

 

외부의 입력 값을 자원(파일, 소켓의 포트 등)의 식별자로 사용하는 경우, 적절한 검증을 거치도록 하거나, 사전에 정의된 리스트에서 선택되도록 한다. 특히, 외부의 입력이 파일명인 경우에는 경로순회 directory traversal) 공격의 위험이 있는 문자 ( /, \, .. 등)를 제거할 수 있는 필터를 이용한다.

 

 

코드예제

 

■ 경로 조작 예제

 

6라인에서 외부 입력값으로 파일 경로 등을 입력받아 14라인에서 파일을 오픈하고 있다. 만약 공격자가 ../../../../etc/passwd와 같은 값을 전달하면 의도하지 않았던 파일의 내용이 클라이언트 측에 표시되어 시스템 정보노출 문제가 발생한다.

 

안전하지 않은 코드의 예
import os
from django.shortcuts import render

def get_info(request):
# 외부로부터 파일명을 입력받고 있다.
request_file = request.POST.get('request_file')
filename, file_ext = os.path.splitext(request_file)
file_ext = file_ext.lower()

if file_ext not in ['.txt', '.csv']:
return render(request, '/error.html', {'error':'파일을 열수 없습니다.'})

# 외부로부터 입력받은 값을 검증 없이 파일 처리에 사용하였다.
with open(request_file) as f:
data = f.read()

return render(request, '/success.html', {'data':data})

 

외부 입력값에서 경로 조작 문자열 ( /, \, .. 등)을 제거한 후 파일의 경로설정에 사용한다.

 

안전한 코드의 예
import os
from django.shortcuts import render

def get_info(request):
# 외부 입력 값으로 받은 파일 이름은 검증하여 사용한다.
request_file = request.POST.get('request_file')

filename, file_ext = os.path.splitext(request_file)
file_ext = file_ext.lower()

if file_ext not in ['.txt', '.csv']:
return render(request, '/error.html', {'error':'파일을 열수 없습니다.'})

# 파일 명에서 경로 조작 문자열을 필터링 한다.
filename = filename.replace('.', '')
filename = filename.replace('/', '')
filename = filename.replace('\\', '')

with open(filename + file_ext) as f:
data = f.read()

return render(request, '/success.html', {'data':data})

 

relplace 함수 외에도 re.sub, filter 함수를 사용하여 특수문자를 필터링 하는 것도 가능하다.

 

 

■ 자원 삽입 예제

 

다음의 예제는 안전하지 않은 코드의 예로, 외부의 입력을 소켓 포트번호로 그대로 사용하고 있다. 외부 입력값을 검증없이 사용할 경우 기존 자원과 충돌로 에러가 발생할 수 있다.

 

안전하지 않은 코드의 예
import socket
from django.shortcuts import render

def get_info(request):
port = request.POST.get('port')

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# 외부로부터 입력받은 검증되지 않은 포트번호를 이용하여
# 소켓을 바인딩 하여 사용하고 있어 안전하지 않다.
s.bind(('127.0.0.1', port))
...
return render(request, '/success')

return render(request, '/error', {'error':'소켓연결 실패'})

 

다음은 안전한 예제를 나타낸 것이다. 내부자원에 접근할 때 외부에서 입력 받은 값을 포트번호와 같은 식별자로 그대로 사용하는 것은 바람직하지 않으며, 꼭 필요한 경우엔 가능한 리스트를 설정하고, 해당 범위 내에서 할당되도록 작성한다.

 

안전한 코드의 예
import socket
from django.shortcuts import render

ALLOW_PORT = [4000, 6000, 9000]

def get_info(request):
port = int(request.POST.get('port'))

# 사용 가능한 포트 번호를 화이트리스트로 제한
if port not in ALLOW_PORT:
return render(request, '/error', {'error':'소켓연결 실패'})

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('127.0.0.1', port))
......
return render(request, '/success')

 

 

댓글

Designed by JB FACTORY