[시큐어코딩 가이드] 서버 사이드 요청 위조
- 정보보안/시큐어코딩 가이드
- 2022. 8. 10.
■ 입력데이터 검증 및 표현
프로그램 입력 값에 대한 검증 누락 또는 부적절한 검증, 데이터의 잘못된 형식지정, 일관되지 않은 언어셋 사용 등으로 인해 발생되는 보안약점으로 SQL 삽입, 크로스사이트 스크립트(XSS) 등의 공격을 유발할 수 있다.
서버사이드 요청 위조
■ 개요
적절한 검증절차를 거치지 않은 사용자 입력값을 서버간의 요청에 사용하여 악의적 인 행위가 발생할 수 있는 보안약점이다.
외부에 노출된 웹 서버에 취약한 애플리케이션이 존재하는 경우 공격자는 URL 또는 요청문을 위조하여 접근
통제를 우회하는 방식으로 비정상적인 동작을 유도하거나 신뢰된 네트워크에 있는 데이터를 획득할 수 있다.
■ 안전한 코딩 기법
식별할 수 있는 범위 내에서 사용자의 입력 값을 다른 시스템의 서비스 호출에 사용하는 경우, 사용자의 입력 값을 화이트리스트 방식으로 필터링한다.
사용자가 지정하는 무작위의 URL을 받아들여야 한다면 내부의 URL을 블랙리스트로 지정하여 필터링 한다. 또한 동일한 내부 네트워크에 있더라도 기기 인증, 접근권한을 확인하여 요청이 이루어질 수 있도록 한다.
코드예제
■ 삽입 코드의 예
설명 | 삽입 코드의 예 |
내부망 중요 정보 획득 | http://sample_site.com/connect?url=http://192.168.0.45/member/list.json |
외부 접근 차단된 admin 페이지 접근 | http://sample_site.com/connect?url=http://192.168.0.45/admin |
도메인 체크를 우회하여 중요 정보 획득 | http://sample_site.com/connect?url=http://sample_site.com:x@192.168.0.45/member/list.json |
단축 URL을 이용한 Filter 우회 | http://sample_site.com/connect?url=http://bit.ly/sdjk3kjhkl3 |
도메인을 사설IP로 설정 해 중요정보 획득 | http://sample_site.com/connect?url=http://192.168.0.45/member/list.json |
서버 내 파일 열람 | http://sample_site.com/connect?url=file:///etc/passwd |
다음 예제는 안전하지 않은 코드 예제이다. 9라인의 사용자로부터 입력된 URL 주소를 검증 없이 사용하여 의도하지 않은 다른 서버의 자원에 접근 가능하게 된다.
안전하지 않은 코드의 예 |
from django.shortcuts import render import requests def call_third_party_api(request): addr = request.POST.get('address', '') # 사용자가 입력한 주소를 검증하지 않고 HTTP 요청을 보내고 응답을 # 사용자에게 반환 result = requests.get(addr).text return render(request, '/result.html', {'result':result}) |
안전한 코드는 다음과 같이 사전에 정의된 서버 목록을 정의하고 매칭되는 URL만 사용할 수 있으므로 URL 값을 임의로 조작할 수 없다.
안전한 코드의 예 |
from django.shortcuts import render import requests # 도메인을 화이트리스트에 정의 할 경우 DNS rebinding 공격 등에 # 노출될 위험이 있어 신뢰 할 수 있는 자원에 대한 IP를 사용하여 # 검증하는 것이 좀더 안전하다. ALLOW_SERVER_LIST = [ 'https://127.0.0.1/latest/', 'https://192.168.0.1/user_data', 'https://192.168.0.100/v1/public' ] def call_third_party_api(request): addr = request.POST.get('address', '') # 사용자가 입력한 URL을 화이트리스트로 검증한 후 그 결과를 반환하여 # 검증되지 않은 주소로 요청을 보내지 않도록 제한한다. if addr not in ALLOW_SERVER_LIST: return render(request, '/error.html', {error = '허용되지 않은 서버입니다.'}) result = requests.get(addr).text return render(request, '/result.html', {'result':result}) |