개요
정보 수집이나 파일 다운로드 자동화를 위해 크롤링을 사용한다. 주로 파이썬의 requests 모듈을 이용하여 수집했는데, 대상 사이트에서 응답코드 403(Forbidden)을 반환하면서 데이터를 가져올 수 없었다. 문제를 발견하고 해결하는 과정이다.
문제 식별
cloudflare에서 차단 정책을 도입하여 봇의 접근을 차단하고 있었다. 영문으로는 'Cloudflare challenge page'로 언급하는데 자동화된 접근이나 스크래핑 봇을 차단하기 위해 사용한다. 작동 방식은 브라우저에서만 실행할 수 있는 자바스크립트 구문을 사이트 본문에 삽입하여 실행 여부를 검사하는 방식을 포함하여 사용자의 상호작용이 필요한 버튼을 추가(캡차 등)하는 다양한 방법이 존재한다. 대상 페이지의 경우 명령줄(cli)로 경로에 접근할 때에는 자바스크립트를 실행하지 않기 때문에 봇으로 인식하여 차단하는 원리였다. 파이썬의 requests 모듈 또한 명령행을 통한 접근을 수행하기 때문에 동일하게 차단된다. 이렇게 접근이 차단되었을 때 403 오류코드를 반환한다.
# curl https://(차단 정책이 적용된 URL)?version\=latest
<!DOCTYPE html><html lang="en-US"><head><title>Just a moment...</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=Edge"><meta name="robots" content="noindex,nofollow"><meta name="viewport" content="width=device-width,initial-scale=1"><link href="/cdn-cgi/styles/challenges.css" rel="stylesheet"><meta http-equiv="refresh" content="375"></head><body class="no-js"><div class="main-wrapper" role="main"><div class="main-content"><noscript><div id="challenge-error-title"><div class="h2"><span class="icon-wrapper"><div class="heading-icon warning-icon"></div></span><span id="challenge-error-text">Enable JavaScript and cookies to continue</span>... 생략
이 문제를 해결하기 위해서 브라우저 엔진으로 동작하는 라이브러리(Selenium, Puppeteer)를 이용하여 사이트에 접근해야 한다. 하지만 requests를 이용하는 방식보다 복잡하다.
방법 1. - cfscrape 모듈(우회불가)
cloudflare 차단을 우회하기 위해서는 자바스크립트를 실행할 수 있는 실제 브라우저가 필요하다. 사람이 직접 접근하는 것처럼 봇을 가장하는 것이다. cfscrape 모듈은 복잡한 추가 바이너리 설치 없이 모듈 설치만으로 이러한 동작을 수행한다. 설치 명령과 코드 예제는 다음과 같다.
pip install cfscrape
import cfscrape
scraper = cfscrape.create_scraper() # returns a requests.Session object
url = "https://[접근 차단 URL]"
response = scraper.get(url).content
방법 2. - selenium + chrome(우회가능)
셀레니움은 웹 애플리케이션 테스팅을 위한 프레임워크이며, 여러 프로그래밍 언어를 통해 제어할 수 있다. 셀레니움 단독으로는 웹 브라우저를 실행할 수 없기 때문에 크롬드라이버(chromedriver)를 이용하여 셀레니움 코드를 실행한다. 크롬 인스턴스를 띄워서 실제 브라우저가 동작하는 것처럼 모든 동작을 수행한다. 앞서 언급한 자바스크립트 실행 또한 수행하기 때문에 크롤링 차단 우회를 위해 사용된다.
셀레니움 설치
다음 명령어 실행으로 셀레니움 모듈을 다운받는다.
pip install selenium
크롬 드라이버 설치 방법 1(권장)
두 가지 방법이 있는데 파이썬 모듈을 통해 설치된 크롬 브라우저에 맞는 드라이버를 설치할 수 있다.
크롬 브라우저를 먼저 설치한 후, 파이썬 webdriver_manager 모듈을 통해 버전에 맞는 크롬드라이버를 자동으로 다운로드할 수 있다.
# 크롬 설치
sudo dnf install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
# 설치확인
google-chrome --version
# webdriver_manager 설치
pip install webdriver_manager
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
def main():
url = "http://www.google.com"
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get(url)
크롬 드라이버 설치 방법 2
실행 환경에 맞는 크롬드라이버를 선택하여 다운받는다.
링크: https://chromedriver.chromium.org/downloads
필요 시 실행 권한을 부여해야 한다.
chmod +x chromedriver
파이썬 프로젝트 경로 내 크롬드라이버를 위치시키고 코드에서 사용한다.
from selenium import webdriver
def main():
url = "http://www.google.com"
driver = webdriver.Chrome()
driver.get(url)
전체 코드
cli 환경에서는 브라우저 창(gui)을 띄울 수 없기 때문에 창이 없는 헤드리스(headless) 모드로 실행해야 한다. 앞서 소개한 '크롬 드라이버 설치 방법 1'을 이용하여 설치하였고, 단순히 '--headless' 옵션만 추가하여 실행해도 되지만 해당 옵션 사용 시 http 요청 헤더에 관련 정보가 추가되므로 cloudflare 우회가 되지 않을 수 있다. 우회를 위해 selenium_stealth 모듈을 사용하였다.
from selenium import webdriver
from selenium_stealth import stealth
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
options = webdriver.ChromeOptions()
options.add_argument("--headless")
service = ChromeService(executable_path=ChromeDriverManager().install())
driver = webdriver.Chrome(options=options, service=service)
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
url = f"https://[URL]"
driver.get(url)
마치며
requests 모듈에 비해 크롬 드라이버 설정 등 코드가 추가되었다. 차단 옵션에는 요청자의 IP 차단, 인증되지 않은 사용자 차단 등 다양한 방법을 사용할 수 있다. 무분별한 크롤링은 페이지 가용성 저하와 트래픽 낭비로 사이트 운영 중단을 초래할 수 있음에 주의한다.
'Security > WEB' 카테고리의 다른 글
[웹해킹] OAST / OOB(Out-of-Band) Testing (0) | 2023.05.27 |
---|---|
[PKI] 인증서 관리 규격 (1) | 2022.12.03 |
제 3자 쿠키 허용, 왜 위험할까? (0) | 2022.10.30 |
[WEB] Client Side Storage (0) | 2022.10.09 |
2FA 인증 및 우회 방법 정리 (0) | 2022.08.31 |