Skip to content

AP 인바운드: 존재하지 않는 로컬 post URL로 최상위 포스트를 주입할 수 있다 (H2) #936

Description

@stadia

PR #926 리뷰에서 나온 항목. #926 이 도입한 Post.find_by(id:) 가드의 부작용이다.

문제

reply_target_attributes 의 로컬 /posts/N 분기에 존재 가드가 붙으면서, 대상이 없을 때 조용히 다음 분기로 흘러 최종적으로 {} 를 반환한다. 그 결과 저장이 거부되던 것이 최상위 포스트 저장으로 바뀌었다.

app/models/post.rb:49:241-247 에 검증이 있다:

def validate_parent_post
  return unless parent_id.present?
  errors.add(:parent_id, "원본 포스트를 찾을 수 없습니다.") if parent.nil?
end
inReplyTo = https://<로컬>/posts/<없는id> 결과
#926 이전 parent_id 설정 → validate_parent_post 실패 → RecordInvalid저장 안 됨
#926 이후 {} → parent/article 없는 최상위 :short 포스트로 저장

handle_federated_object? 는 URL 에 /posts/ 문자열이 들어있는지만 본다:

if reply_target_host_kind(in_reply_to) == :local
  return true if in_reply_to.include?("/posts/") || in_reply_to.include?("/articles/")
end

따라서 원격 액터가 존재하지 않는 로컬 post URL 을 inReplyTo 에 넣으면 인박스를 통과하고, 우리 타임라인에 최상위 글로 저장된다. 이전에는 검증이 이를 막았다.

참고로 이슈 #871 이 전제한 "dangling parent_id 는 FK 위반" 도 부정확하다. FK 이전에 검증이 먼저 잡는다.

관측 가능성 문제

{} 로 떨어질 때 남는 warn 이 두 가지 다른 사건을 한 문장으로 뭉갠다:

  • 정말 모르는 원격 URL — 일상적, 무해
  • 로컬 /posts/N 패턴에 매치됐는데 그 post 가 없음 — 데이터 무결성 신호 또는 공격

운영자가 로그만 보고 둘을 분리할 수 없다.

제안

handle_federated_object? 가 로컬 URL 을 수락하기 전에 대상의 실제 존재를 확인한다. 지금은 "URL 에 문자열이 들어있는가"만 본다.

if reply_target_host_kind(in_reply_to) == :local
  return true if (id = in_reply_to[%r{/posts/(\d+)}, 1]) && Post.exists?(id: id)
  return true if (id = in_reply_to[%r{/articles/(\d+)}, 1]) && Article.exists?(id: id)
end

그리고 reply_target_attributes 의 폴백에서 "로컬 패턴 매치 + 대상 없음" 을 별도 메시지로 로깅해 위 두 사건을 분리한다.

주의: 이건 인박스 수용 정책 변경이다. 정상 경로(로컬 post 에 대한 원격 답글)는 발행된 inReplyTo/federation/published/posts/<숫자id> 형태라 숫자 정규식에 매칭되므로 영향받지 않는다(실측 확인). 다만 슬러그 URL(/posts/<slug>, should_federate? 가 false 라 federated_url 이 nil 인 경우의 폴백)은 정규식에 안 걸리므로, 위 변경을 그대로 넣으면 그 경로가 거부된다. 슬러그 해석을 함께 넣을지 결정 필요.

테스트

test/models/concerns/posts/federation_ingest_test.rb"local /posts/ URL for a missing post yields no parent_id" 는 현재 이 동작을 핀 고정할 뿐 타당성은 묻지 않는다. 수용 정책을 정한 뒤 이 테스트의 기대를 함께 갱신해야 한다.

관련: #871, #926

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrubyPull requests that update ruby code

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions