4Chan CAPTCHA 해독하기
(nullpt.rs)- 브라우저 안에서 4Chan CAPTCHA를 자동으로 풀기 위해 TensorFlow.js 모델을 만든 프로젝트로, 목표였던 최소 80%와 선호 목표인 90% 이상 정확도를 달성함
- CAPTCHA 수집은 요청 API의
ttl,cd, Cloudflare Turnstile, 점증하는 대기 시간 때문에 단순 대량 스크래핑으로 처리하기 어려웠음 - 상용 사람 기반 풀이와 수동 라벨링은 오류와 스로틀링에 막혔고, 실제 배경 약 2,500개와 문자별 50~150개 이미지로 합성 데이터 약 50,000장을 만들어 학습 규모를 확보함
- 모델은 CNN+LSTM 구조와 CTC 인코딩을 사용했고, Keras/TensorFlow 학습 후 Python 3.10, Keras 2,
.h5포맷을 거쳐 TensorFlow.js로 변환함 - 실제 브라우저에서는 첫 로딩이 약 1초, 이후 실행은 체감상 즉시 처리됐으며, 수백 개의 실제 CAPTCHA에서 90% 초과 성공률을 보임
목표와 공개 코드
- 목표는 머신러닝과 TensorFlow 학습을 겸해 4Chan CAPTCHA를 브라우저에서 안정적으로 푸는 모델을 만드는 것이었음
- 기준은 최소 80% 정확도, 가능하면 90% 이상이었고 최종적으로 달성함
- 관련 코드는 GitHub의 4chan-captcha-playground에 공개됨
4Chan CAPTCHA의 동작 방식
- 4Chan은 게시물이나 답글을 작성하기 전에 CAPTCHA 입력을 요구함
- 일반 CAPTCHA는 5~6개 영숫자가 들어 있는 이미지이며, 사용자가 모든 문자를 정확히 입력해야 함
- 슬라이더 CAPTCHA는 무작위 문자 조각처럼 보이는 배경 이미지와 투명한 구멍이 있는 전경 이미지를 맞춰 CAPTCHA 텍스트를 보이게 하는 방식임
CAPTCHA 수집에서 만난 제한
- 새 CAPTCHA 요청을 관찰하면 브라우저는
https://sys.4chan.org/captcha?framed=1&board={board}로 요청을 보냄 framed=1을 제거하면 HTML 안의postMessage()대신 원시 JSON이 반환됨- JSON에는
challenge,ttl,cd,img,img_width,img_height,bg,bg_width등이 포함됨 ttl은 약 2분 후 CAPTCHA가 만료되는 시간으로 보임cd는 다음 CAPTCHA 요청까지 기다려야 하는 쿨다운 값으로 해석됨
- JSON에는
- 연속 요청을 보내면
cd가 점점 커짐- 초기 몇 번은 5초마다 요청 가능함
- 이후 8초로 늘고, 계속 대략 두 배씩 증가함
- 최종적으로 280초에서 상한에 도달함
- 280초 타이머에 도달한 뒤에는 CAPTCHA가 더 어려워짐
- 여러 가로선과 타원형 방해 요소가 추가된 이미지가 나타남
- 데이터 품질은 낮아지지만 여전히 사용할 수는 있었음
- CAPTCHA 요청 전에는 Cloudflare Turnstile 통과가 필요함
- 많은 프록시와 단순 스크립트를 쓰는 방식은 현실적이지 않았음
- 수집 스크립트는 브라우저에서 Cloudflare 쿠키를 복사해 사용하고, 만료되면 수동으로 교체함
- 이 방식으로 수백 개의 CAPTCHA를 수집했지만, 학습에 충분한 양은 아니었고 정답 라벨도 없었음
사람 기반 라벨링의 한계
- 슬라이더 CAPTCHA 정렬은
trainer/captcha_aligner.py의 휴리스틱 스크립트로 100% 성공률을 보였음 - 상용 CAPTCHA 풀이 서비스에 CAPTCHA를 보내 실제 사람이 정답을 입력하게 하는
trainer/labeler.py를 작성함 - 처음 보낸 수십 개 CAPTCHA는 대부분 하나 이상의 문자가 잘못 풀이됨
- 서비스의 “100% Recognition” 기능을 사용해 여러 작업자의 답이 일치할 때만 결과를 받도록 설정함
- 설정값은
n = 2,x = 2,y = 3 - 먼저 2명에게 보내고, 둘이 일치하지 않으면 최대 3명을 추가로 보내 두 명의 답이 일치할 때까지 시도함
- 설정값은
- 이 설정으로 약 80% CAPTCHA가 풀이됐고, 그중 약 90%는 정확했지만 약 10%는 오류가 있었음
- 여러 작업자가 같은 실수를 하는 경우가 있었음
- 직접 또는 지인의 도움으로 CAPTCHA를 풀어 이미지와 정답을 저장하는 사용자 스크립트도 사용함
- 수백 장의 이미지를 추가로 얻어 학습 세트에 넣음
- 반복 요청 스로틀링과 CAPTCHA 난이도 상승 때문에 이 접근은 중단됨
합성 데이터 생성
- 4Chan과 해당 CAPTCHA 코드는 오픈소스가 아니어서 동일한 코드를 로컬에서 실행할 수는 없었음
- 대신 실제 CAPTCHA 구조를 근사해 합성 CAPTCHA를 생성함
- CAPTCHA는 배경과 문자 두 부분으로 나눠 다룸
- 배경은 실제 이미지에서 큰 contour를 찾아 문자 영역을 제거해 얻음
- 문자 제거 후에는 노이즈 배경만 남음
- 개별 문자는 수동 라벨링으로 확보함
- VoTT로 문자를 태깅함
- 간단한 스크립트로 문자를 추출하고 후처리함
- 문자별로 50~150개의 고립 이미지를 확보함
- 4Chan CAPTCHA에는
0, 2, 4, A, D, G, H, J, K, M, N, P, R, S, T, W, X, Y만 포함됨- 모호함을 피하기 위한 선택일 가능성이 있음
- 추출한 문자와 배경을 조합하고, 관찰한 문자 배치 패턴에 맞춰 합성 이미지를 생성함
- 입력 문자가 이미 라벨링되어 있어 합성 CAPTCHA의 정답도 자동으로 만들 수 있었음
모델 구조와 전처리
- 학습 데이터는 사전 정렬된 슬라이더 CAPTCHA, 일반 CAPTCHA, 합성 CAPTCHA를 섞어 사용함
- 학습 스크립트는 모든 이미지를 300x80 픽셀로 맞추고 순수 흑백으로 변환함
- 모델은 CAPTCHA 풀이 관련 여러 글을 참고해 구성한 LSTM CNN 구조임
- 3개 convolution/max-pooling 레이어 사용
- 2개 LSTM 레이어 사용
- 4번째 convolution 레이어도 시험했지만 성능이 개선되지 않음
- 출력 길이가 5자 또는 6자로 가변적이어서 CTC 인코딩을 사용함
- 구현에는 Keras와 TensorFlow를 사용함
tf.image.resize() 인자 순서 문제
- 일부 오래된 정렬 슬라이더 CAPTCHA는 300x80 해상도나 종횡비에 맞지 않았음
- 학습 스크립트가 다양한 입력을 처리하도록
tf.image.resize()를 사용함 - 처음에는 크기 인자를
(width, height)튜플로 가정했지만, 실제tf.image.resize()는(height, width)순서를 요구함 - 이 실수로 이미지가 80x300처럼 세로로 늘어나 읽을 수 없는 형태가 됨
- 32 epoch 이상 학습해도 본 이미지에서 성능이 거의 나오지 않음
- 새 CAPTCHA에는 무작위에 가까운 예측을 냄
- 처리된 입력 이미지를 시각화하면서 문제를 확인했고, 수정 후 학습 성능이 크게 좋아짐
학습 규모와 결과
- 최종 데이터셋은 약 500장 수동 풀이 이미지와 약 50,000장 합성 이미지로 구성됨
- 합성 이미지는 약 2,500개 배경 이미지와 문자별 50~150개 이미지에서 무작위 샘플링해 생성함
- 데이터셋은 무작위로 섞은 뒤 90/10 비율로 학습 세트와 평가 세트로 나눔
- NVIDIA RTX A4000 Laptop GPU에서 epoch당 학습 시간은 약 45초였음
- 첫 epoch 종료 시 loss는 19 수준이었고, 예측은 거의 맞지 않았음
- 4번째 epoch 종료 시 loss는 0.55까지 떨어졌고, 무작위 테스트 예측 5개 중 5개가 맞음
- 8~16 epoch가 시간과 최종 성능 사이의 좋은 절충점이었음
- 8번째 epoch쯤 loss가 안정화됨
- 16 epoch를 넘기면 개선 폭이 크게 줄어듦
trainer/infer.py로 Python에서 추론을 테스트했고, 보지 않은 이미지에서도 결과가 유망했음
TensorFlow.js 변환과 브라우저 실행
- 사용자 스크립트는 TensorFlow.js와 TypeScript로 작성함
- Python 코드의 CAPTCHA 정렬 알고리듬과 이미지 전처리 코드를 다시 구현함
- 관련 코드는 저장소의
user-scripts/디렉터리에 있음 - Python TensorFlow/Keras 모델 포맷은 TensorFlow.js가 기대하는 포맷과 호환되지 않음
- 공식 변환 스크립트를 사용해야 했지만, 두 가지 문제가 있었음
- 공식 TensorFlow-to-TFJS 변환기는 Python 3.12에서 동작하지 않았고 오류 메시지도 명확하지 않았음
- PyEnv로 Python 3.10을 사용하자 변환이 성공함
- 변환 스크립트는 Keras 3 모델을 TensorFlow.js 포맷으로 변환할 수 있었지만, TensorFlow.js는 그 변환된 모델을 실제로 읽지 못했음
- 관련 문제는 forum post를 통해 확인함
- 해결책은 Keras 2 사용이었음
tf_keras레거시 패키지를 설치함TF_USE_LEGACY_KERAS=1환경 변수를 설정해 학습함- 레거시
.h5모델 포맷으로 내보내고 변환 스크립트에 입력 포맷을 지정함 - 코드 변경은 한 줄의 단순 수정만 필요했음
실제 4Chan CAPTCHA 성능
- 실제 4Chan CAPTCHA에서도 모델은 잘 동작함
- 첫 모델 로딩은 약 1초가 걸림
- 이후 실행은 체감상 즉시 처리됨
- 브라우저에서 수백 개의 실제 CAPTCHA를 푼 경험상 성공률은 90% 초과였음
- 문자 자체를 틀리는 경우는 드물었고, 부정확할 때는 보통 문자 하나를 통째로 누락함
- 실제 데이터 학습을 더 늘리거나 합성 데이터 생성기의 CAPTCHA 레이아웃을 조정하면 개선 여지가 있음
- 상용 사람 기반 CAPTCHA 풀이 서비스보다 이 모델의 정확도가 훨씬 높았음
4글자 CAPTCHA와 마무리
- 프로젝트 완료 후 글을 작성하고 편집하는 동안 4Chan이 때때로 4글자 CAPTCHA를 제공하기 시작함
- 모델은 5글자와 6글자 CAPTCHA로만 학습됐지만, 4글자 CAPTCHA에서도 동일한 수준의 성능을 보임
- 프로젝트 과정에서 머신러닝과 컴퓨터 비전을 많이 학습했고, 시작 목표였던 브라우저 기반 CAPTCHA 풀이 모델을 완성함
댓글과 토론
Hacker News 의견들
-
Keras와 TensorFlow.js 연동이 엉망인 부분은 전형적인 TensorFlow 같음
TensorFlow를 쓰면 항상 통합되고 매끈한 제품이라기보다, 대충 관련 있어 보이는 도구들을 한 우산 아래 모아둔 느낌이었음
사실 Google의 오픈소스 라이브러리나 도구는 전부 이런 느낌이라고까지 말할 수 있음- 관련해서 15일 전 François Chollet이 Google을 떠난다는 글에서도 비슷한 맥락이 있었음: https://news.ycombinator.com/item?id=42130881
“왜 2019년에 Keras를 TensorFlow에 합치기로 했나?”에 대한 답은 “내가 결정한 게 아니다. 2018년에 TF 리더들이 내린 결정이었고, 당시 나는 L5 개인 기여자였으며 그건 L8 결정이었다”였음 - 콘웨이의 법칙이 떠오름
- 관련해서 15일 전 François Chollet이 Google을 떠난다는 글에서도 비슷한 맥락이 있었음: https://news.ycombinator.com/item?id=42130881
-
내 사이트[0]에서 댓글 폼 스팸을 막으려고 CAPTCHA가 필요했는데, 예전에 본 재미있는 방식을 재활용해 봤음
절대 완벽하지도 않고 어렵지도 않지만, 만드는 과정이 정말 마음에 들었음
[0] https://www.hybridlogic.co.uk/contact- Doom CAPTCHA가 떠오름
https://vivirenremoto.github.io/doomcaptcha/ - 보려고 하니 차단됐다고 나옴. VPN도 쓰지 않았음
- Doom CAPTCHA가 떠오름
-
왜 사람들이 왜곡된 텍스트 기반 CAPTCHA에서 멀어졌는지 이유가 있음
이제는 컴퓨터가 사람보다 더 잘 푸는 지점에 거의 와 있음
https://www.usenix.org/system/files/conference/woot14/woot14...는 이 주제에 관한 논문인데 꽤 흥미롭다고 봄
그래도 놀라울 만큼 많은 텍스트 기반 CAPTCHA는 ImageMagick으로 회색조 변환, 팽창과 침식 처리를 한 뒤 Tesseract에 넘기는 몇 줄짜리 셸 스크립트로 풀 수 있음
하지만 https://2captcha.net 같은 사이트도 있으니, 결국 CAPTCHA는 최소한의 작은 수고를 요구하는 장치에 가까움- 기술적으로 뚫을 수 있다고 해서 쓸모없는 건 아님
이 글의 해법에도 상당한 시간, 기술, 노력이 들어갔고, 결과물도 일반화가 잘 되지 않아서 다른 종류의 CAPTCHA라면 처음부터 다시 해야 함
대부분의 스패머는 이걸 재현하지 못하고, 재현할 수 있는 사람은 합법적으로 돈을 벌거나 더 돈 되는 표적을 노릴 가능성이 큼
이런 CAPTCHA는 성공적인 스팸의 비용을 예상 수익보다 높이는 데 여전히 잘 작동함 - 다음에는 뭐가 올지 궁금함
모든 회원이 운영자와 15분 화상 인터뷰를 해야 하는 포럼을 만들 수 있을까? “확장성이 없다”는 건 알지만, 웃긴 장난 같은 장치로는 가능해 보임 - CAPTCHA는 시스템을 악용하는 행위자의 난도를 올리는 또 하나의 방어선일 뿐이라고 봄
해결책은 아니고, 조금씩 낡아가는 작은 요새 같은 것임 - 작다고 보기 어려움
링크에 따르면 reCAPTCHA v3는 10~15초가 걸리고 CAPTCHA 1000개당 1.3달러가 듦
큰 웹사이트를 대규모로 긁어오는 것처럼 CAPTCHA를 우회하고 싶은 많은 작업에서는 이 비용이 실제로 상당히 크고 감당하기 어려워짐 - 그 정도라면 작업 증명 CAPTCHA가 아마 최선의 선택지일 수 있음
mCaptcha.org도 그중 하나고 다른 구현도 있음
전통적인 CAPTCHA는 조금이라도 효과적이면 접근성 측면에서 악몽이 되기 쉬움
- 기술적으로 뚫을 수 있다고 해서 쓸모없는 건 아님
-
이런 주제에 관심 있다면, 2014년에 내가 정리한 Silk Road CAPTCHA 분석도 있음: https://github.com/mieko/sr-captcha
-
4chan의 대응은 적절해 보임
어차피 신경망으로 쉽게 풀 수 있으니 사람에게 주는 일을 단순화하는 쪽임
이제는 아주 어려운 CAPTCHA를 설계해도 기계가 풀기 어려워질 가능성은 낮고, 사람만 더 짜증나게 만들 확률이 큼- 그렇다면 무료 사용자의 글쓰기를 아예 막고, 글을 쓰려면 모두 연 20달러짜리 4chan Pass를 사게 할 수도 있음
https://4chan.org/pass
이미 CAPTCHA 없이 쓰기 위한 옵션으로 제공되고 있음
CAPTCHA가 완전히 무효라면 CAPTCHA와 무료 글쓰기를 없애고, 글을 쓰려는 사람은 모두 4chan Pass를 사야 한다는 결론이 따라옴 - 그 지점에 갇힌 지 최소 5년, 길게는 10년은 된 것 같음
- 다음에는 그냥 Worldcoin 망막 스캔을 쓰면 됨
- 4chan은 사람이 짜증나는지 별로 신경 쓰지 않음
최근에는 15분 글쓰기 지연을 도입했는데 정말 화남
Cookie AutoDelete에서 4chan을 허용 목록에 넣어야 했음
- 그렇다면 무료 사용자의 글쓰기를 아예 막고, 글을 쓰려면 모두 연 20달러짜리 4chan Pass를 사게 할 수도 있음
-
CAPTCHA가 있는 척하면서 실제로는 사용자의 타이밍과 행동을 분석하는 편이 낫지 않을까 싶음
솔직히 이미 그런 일이 벌어지고 있을 것 같기도 함
완전히 메타적으로 가자면, 상대편 행위자가 사람인지 아닌지 판단하도록 AI를 훈련할 수도 있음
즉 역 튜링 테스트를 발명하는 셈이고, AI가 정상적인 사람의 응답과 구별하지 못하면 사람으로 보는 방식임
마케팅용 사람 응답과 구별하는 게 아니라는 점이 다름
이제 이 생각만으로도 좀 속이 안 좋아서 누워야겠음- 대형 CAPTCHA 제공업체들은 이미 대체로 그렇게 하고 있음
CAPTCHA를 내보내기 전부터 TLS 지문, IP, HTTP/2, 요청, JavaScript 환경, 글꼴과 이미지 렌더링 능력, 브라우저 자체를 먼저 식별함
이 정보들로 신뢰 점수를 계산하고, 애초에 CAPTCHA를 보여줄지 결정함
그다음에야 CAPTCHA 입력을 분석할 의미가 생기지만, 그 시점이면 이미 봇의 90%는 잡힌 셈임
브라우저가 아무 인식 없이 서버에 알려줄 수 있는 정보량은 말도 안 될 정도라서, 우리 각자의 디지털 지문이 실제 지문보다 더 고유할 가능성이 큼 - reCAPTCHA가 하는 일이 그거임
- 대형 CAPTCHA 제공업체들은 이미 대체로 그렇게 하고 있음
-
4chan CAPTCHA 돌파의 원조급 사례는 여전히 Yannick Kilcher가 “Raiders of the Lost Kek” 데이터셋으로 GPT-J를 미세조정한 것이라고 봄
영상으로 나온 대규모 언어 모델 활용 중 가장 멋진 사례일지도 모름: https://youtu.be/efPrtcLdcdM?si=errY0PrEhnX9ylDw- 4chan에 대한 면책 고지와 경고만 거의 1분 가까이 나옴
기록감임
- 4chan에 대한 면책 고지와 경고만 거의 1분 가까이 나옴
-
“공식 TensorFlow-to-TFJS 모델 변환기가 Python 3.12에서 작동하지 않고, 문서화도 제대로 되어 있지 않다”, “TensorFlow.js는 Keras 3를 지원하지 않는다” 같은 것 때문에 몇 년 전 가볍게 기계 학습을 해보려다 거의 포기했음
최신 튜토리얼이 이미 낡아 있는 경우가 너무 많았고, 무작위 함정이 많았으며, “시작하기” 가이드들이 이미 전문가라고 가정하는 경우가 충격적일 정도였음- 기계 학습을 몇 년 해온 입장에서, 최신 유행은 피하라고 권하고 싶음
오래된 베이지안 통계학 교과서로 기초를 배우고, 그다음 PyTorch 같은 주요 프레임워크로 넘어가는 편이 좋음
처음에는 CNN, RNN, Transformer 아키텍처와 학습 파이프라인의 모든 부분을 직접 작성해 보는 게 좋음
데이터 로더까지 포함하되 CUDA 행렬 커널은 빼도 됨
LangChain처럼 남의 래퍼를 다시 감싼 래퍼는 멀리하는 게 좋음
문서가 낡은 정도를 넘어 기초에 대해 아예 틀린 경우도 많음
Hugging Face는 기본기를 알고, 표준 래퍼가 깨졌을 때 고칠 수 있다면 훌륭함
- 기계 학습을 몇 년 해온 입장에서, 최신 유행은 피하라고 권하고 싶음
-
이건 몇 시간 들여 정화조 뚜껑 여는 법을 배우는 것과 비슷함
- 이상하게도 4chan 대부분은 Musk 이전의 Twitter보다도 덜 뇌가 썩는 느낌임
- 정화조 시스템을 공부하면서 배울 수 있는 것들을 과소평가하면 안 됨
-
CAPTCHA 풀이 서비스 링크를 따라가 보면, 그 일을 하는 사람들의 프로필을 읽을 수 있음
위험한 공장에서 일하는 것보다 더 윤리적이라는 식으로 홍보되고 있음