[t:/]$ 지식_

광고 어뷰징 차단 단상

2019/05/21

ML에 의한 지능화된 fraud 체크는 일단 열외로 하고, 틀린말 맞는말 섞여있는데 항상 그렇듯이 그정도 퀄의 노트임.

가장 기본적인 중복 접속의 체크는 클라이언트 IP로 할 수 있다. 그런데 http 프로토콜에 붙어오는 IP는 브라우저가 찍은 IP다. IP 레이어의 진퉁 IP가 아니다. 즉, NAT 밑에 있다면 사설 IP가 된다. 같은 IP를 갖는 사람이 많으므로 이 정보로는 걸러낼 수 없다.

그렇다면 IP 레이어의 IP와 포트를 보는 방법이 있다. NAT에 의한 라우터 IP가 찍힌다. 이 때는 같은 라우터를 쓰는 모든 사람이 한 통속;;이 되므로 문제가 된다. NAT의 구조를 공부하면 알겠지만 src 포트까지 보면 된다. 아파치 로그 문서를 보니까 %{format}p 로 로그도 찍을 수 있다고 써 있다. 여기까지 왔다면 몇 초내에 같은 소스 ip와 포트로 뚜르륵 들어오면 반칙이네 라고 판정할 수 있다.

그런데 또 문제가 있다. 서비스를 서빙하는 서버와 같은 라우터 안의 사용자이다. 얘네들은 사설 IP를 들고 80으로 들고온다. NAT3라면 어떨까. 잘 모르겠다. 드물지만 쫑은 발생할 것 같다.

개발단계에서도 문제다. 개발자의 요청이 fraud로 걸릴 수 있다. 화이트 리스트로 관리해야 할까? 조금 지저분한 문제가 된다.

어쨌든 이런 처리는 레디스 같은 저장소에 의존해야 하는데, 으.. 싫다..


광고 요청을 보면 응답 단계에서 리퀘스트 ID를 발급한다. 이제 광고 노출이나 클릭이 발생하면 리퀘스트 ID에 이벤트를 붙여서 로그를 받는다. 노출과 클릭을 1회만 인정하고 싶다면 리퀘스트 ID로 짝을 맞추면 된다. 아마도 빠른 응답을 위해 레디스 같은 것을 생각하기 쉽다. expire 처리도 가능하니 제한 시간내의 이벤트만 처리할 수 있다. 다른 대안으로는 일단 로그로 다 때려넣고 백엔드 데이터 처리에서 발라내는 것이다. 이 때 문제는 제한 시간내의 이벤트를 처리하기 위해 group by 연산을 하고, 사용자별로 광고 요청 - 노출 - 클릭 사이의 시간 갭을 계산해서, 이 이하에 속하는 애들만 발라내야 한다는 것이다. 느리다. 로그 데이터는 하둡과 같은 굼벵이 디비에서 처리를 해야 하므로 문제가 된다.

그렇다면 이벤트 로그에 당초 광고 요청을 했던 시간을 같이 찍으면 어떨까? 그렇다면 이벤트 로그에서 요청 시간까지 관측이 가능하므로 이벤트 로그만 보면 된다. 물론 이때도 group by에 의해 uniq만 구해야 한다. 어쨌든 시간갭 계산은 필요없다는 것은 장점이다. 이때 문제는 광고 요청 시간을 조작 할 수 있다는 것이다.

그렇다면 광고 요청 시각을 응답에 암호화해서 넣는 방법이 있다. 다시 풀어내야 하므로 aes와 같은 양방향 암호화를 써야 한다. 암호가 털리면 요청 시각 조작이 가능하지만 뭐 이런 것 까지라고 생각할 순 있다. 이렇게 암호화된 요청 시각을 응답으로 받아서 노출/클릭에 같이 넣어서 로그 서버로 전송하면 백단에서 데이터 처리만으로 단일 레코드에서 유효시간내 이벤트인지 판정할 수 있다. 솔트는 필수.

레디스나 멤캐시를 사용할 때의 문제는 또 있다. 예상 외 트래픽 폭증시 캐시 이빅션이 발생한다는 것. 캐시 이빅션은 메모리 용량만으로만 발생하는 것은 아니다. 멤캐시의 경우 데이터 크기의 빈도에 의해 일정 구획을 사전에 잡아두는데, 메모리가 여유있어도 그 구획을 초과하는 데이터가 들어올 때 이빅션이 발생하는 것을 경험적으로 관측한 바 있다.

트래픽을 받는 단에서 최대한 로직을 제거하는 것을 선호하는 측면에서는 그냥 일단 다 때려받고 백단에서 데이터 처리로 퉁치는 이런 방식이 나을 것 같다. 로직이 없고, 연동이 없으니 그만큼 트래픽 더 받아도 된다. 아무리 레디스라도 연동은 연동이다.

쿠키에 정보를 남기는 것은 어뷰징 가능성이 높고 원래 쿠키라는것이 돗때기 시장이라 제외한다.

여기까지 왔을 때에도 데이터 처리에서 리퀘스트 ID에 대해 group by 연산이 필요하다. 레디스 같은 연동 없이 group by도 없이 처리할 방법은 없을까.


옵트 아웃은 어찌 처리해야 할까. 옵트 아웃을 쿠키로 처리하면 지저분한다. 레디스 등으로 처리하면 레디스 연동 구간이 생긴다. 또한 레디스에서 expire 되기 전에는 옵트 아웃을 해제할 방법이 없다. 사용자가 애드네트워크 서버의 서비스 페이지에 직접 접속하여 옵트아웃을 푸는 액션을 취하도록 유도하는 수 밖에 없다. 만약 광고/캠페인 단위의 옵트아웃이라면 다른 광고에서 이 액션을 유도할 수 있지만, 애트네트워크 서비스 단위의 옵트 아웃이라면 이 액션을 유도하기 어렵다. 쿠키라면 어떨까. 사용자가 쿠키를 삭제하면 그만이지만 쿠키를 지우는 방법을 알기 어렵다. 쿠키 삭제를 앞에서 언급한 서비스 페이지에서 처리할 순 있다. 성능과 구조 상으로는 옵트아웃에 있어서는 아무래도 쿠키가 나을 것 같다.


나야 이래저래 하여 아파치 모듈만으로 웹-와스를 다루고 있지만 기왕 웹-와스로 나눈 마당에 조금 더 머리쓴 구조를 차려볼 수 있다. 아파치 모듈만으로 웹-와스를 나눈 광고 서버에 있어서는 1:1 매핑이 필요없다. 우리 회사의 일반적인 웹애플리케이션에서는 세션 처리 문제 때문에 (세션 클러스터링 제외) 1:1 매핑을 하는 듯 하다. 광고 서버에서는 킵얼라이브 마저 끈 세션없는 서비스를 하므로 mod_proxy에 L4 묶어서 매시로 묶어도 상관없을 것이다. 이것은 아파치-아파치가 아니라 아파치-톰캣이라도 마찬가지다.

기왕 나눴으니 앞에서 받는 웹단의 아파치에서 간단한 필터링 처리를 해주면 좋을 것이다. mod_proxy로 토스하기 전에 옵트아웃을 처리하거나, 블랙리스트 관리를 하는 방법이 있다. 약간의 트래픽은 제거하고 와스단으로 넘길 수 있다. 국가 컨트롤 등을 한다면 왕창 날릴 수 도 있다. 매체, 사용자 블랙리스트 컨트롤을 하거나, 고트래픽, 저트래픽 분할하여 서로 다른 서버로 넘기는 L7식의 구성도 모색해 볼 수 있겄다. 어쨌건 나눈다면 이런 이유도 있지만 할 일 별로 쪼갰다는 데에 의미를 부여할 수 있다. 내가 일하는 방식상 그걸 선호한다. 1인 과제라면 아마도 프로토버프로 컨버전 해서 udp로 날리겠지만...









[t:/] is not "technology - root". dawnsea, rss