본문 바로가기
사이드프로젝트

[오픈소스 기여 도전기 #1] 덫을 놓고 기다리다: ID 충돌 버그 강제 재현 성공 (With. 2PC & DB조작)

by Jaejin Sim 2026. 2. 18.
반응형

지난 포스팅에서 저는 "서로 다른 두 사이트가 Stripe 계정을 공유할 때, 웹훅이 브로드캐스팅(Broadcasting)되는 구조"를 이용해 버그를 잡겠다고 예고했습니다.

그리고 오늘, 컴퓨터 2대와 DB 강제 조작을 통해 그 가설을 완벽하게 증명해 냈습니다. 멀티 사이트 환경에서 우연히 발생할 수 있는 '죽음의 ID 충돌' 시나리오. 그 재현 과정을 공개합니다.

https://simjaejin.tistory.com/103

 

[오픈소스 기여 도전기 #1] 거대한 코드 숲에서 길을 찾다 (구조 분석 & 웹훅 테스트)

지난 포스팅에서 우여곡절 끝에 로컬 개발 환경(wp-env) 구축에 성공했습니다. 하지만 기쁨도 잠시, 막상 IDE를 켜고 코드를 보니 눈앞이 캄캄해졌습니다."환경은 떴는데... 도대체 코드는 어디를

simjaejin.tistory.com

 


🧪 1. 실험 환경: 완벽한 충돌을 위한 무대

이 버그를 재현하려면 "서로 다른 도메인을 가진 두 개의 워드프레스"가 필요합니다. 무료 터널링 툴의 제약을 극복하기 위해 하이브리드 구성을 마쳤습니다.

Stripe 대시보드에는 이 두 도메인의 웹훅 엔드포인트가 모두 등록되어 있습니다. 즉, PC 2에서 결제하면 PC 1에도 알림이 갑니다.


⚙️ 2. 시나리오 설계: 운명을 조작하다 (DB Hacking)

자연 상태에서 두 사이트의 Order ID가 우연히 겹치기를 기다리려면 몇 년이 걸릴지 모릅니다. 그래서 저는 MySQL의 AUTO_INCREMENT 값을 조작하여 강제로 충돌을 유도했습니다.

상황 설정

  • 목표 ID: 1000040
  • PC 1 (Victim): ID 1000040을 '환불(Refund)' 객체로 만듦.
  • PC 2 (Trigger): ID 1000040을 '주문(Order)' 객체로 만듦.

PC 1 작업: '지뢰' 매설

먼저 PC 1에서 주문(1000039)을 생성하고 즉시 환불 처리했습니다. 워드프레스(WooCommerce)는 환불 객체도 wp_posts 테이블에 저장하므로, 이 환불 건이 **ID 1000040**을 차지하게 됩니다.

(여기에 PC 1 DB: 1000039(주문), 1000040(환불) row 데이터 스크린샷)

PC 2 작업: '방아쇠' 당기기 준비

PC 2의 워드프레스 DB에 접속해, 다음 주문이 무조건 1000040번이 되도록 강제 설정했습니다.

ALTER TABLE wp_posts AUTO_INCREMENT = 1000039;

이제 PC 2에서 결제 버튼을 누르면, 생성될 주문 번호는 1000040이 됩니다. 모든 준비는 끝났습니다.


💥 3. 발사 및 피격 (The Impact)

PC 2(Cloudflare) 사이트에서 상품을 장바구니에 담고 결제를 진행했습니다. 예상대로 PC 2에서는 Order #1000040이 정상적으로 생성되고 결제가 완료되었습니다.

바로 그 순간, Stripe는 "Order #1000040 결제 성공"이라는 웹훅을 PC 1에게도 전송했습니다.

PC 1의 ngrok 터미널을 확인해 볼까요?

HTTP Requests
-------------
22:40:46.949 KST POST /  500 Internal Server Error
22:40:30.159 KST POST /  500 Internal Server Error

(경) 500 에러 발생 (축) 성공입니다! PC 1 서버가 비명을 질렀습니다.

 


🕵️‍♂️ 4. 로그 분석: 범인은 '타입 체크 부재'

서버가 뱉어낸 에러 로그를 뜯어보니, 제가 예상했던 바로 그 지점에서 정확하게 터졌습니다.

[15-Feb-2026 13:40:47 UTC] PHP Fatal error:  Uncaught TypeError: WC_Stripe_Order_Helper::get_intent_id_from_order(): Argument #1 ($order) must be of type WC_Order, Automattic\WooCommerce\Admin\Overrides\OrderRefund given...

해석

  1. Stripe 웹훅이 order_id: 1000040을 달고 PC 1에 도착했습니다.
  2. PC 1의 코드는 wc_get_order(1000040)을 호출했습니다.
  3. 하지만 PC 1에서 1000040은 주문이 아니라 '환불(Refund)' 객체였습니다.
  4. 코드는 이걸 확인하지 않고, 주문 객체(WC_Order)만 쓸 수 있는 get_intent_id_from_order() 함수에 환불 객체를 집어넣었습니다.
  5. PHP: "이건 주문이 아니잖아!" -> Fatal Error 💥


📝 5. 결론 및 다음 계획

이로써 "WooCommerce Stripe 플러그인은 웹훅 처리 시 객체 타입(Order vs Refund)을 확인하지 않아, 멀티 사이트 환경에서 치명적인 오류를 일으킬 수 있다"는 사실이 증명되었습니다.

단순히 코드를 읽어서 짐작하는 것과, 이렇게 실제 환경을 구축해서 에러를 터뜨려 보는 것은 하늘과 땅 차이입니다. 이제 저는 확신을 가지고 코드를 수정할 수 있게 되었습니다.

다음 편 예고: 이제 원인을 알았으니 고칠 차례입니다. 단 3줄의 코드 수정으로 이 문제를 우아하게 해결하고, GitHub에 Pull Request(PR)를 보내 오픈소스 컨트리뷰터가 되는 마지막 과정을 공개하겠습니다.

반응형