문의 폼 스팸 막기

문의 폼 스팸 막기4분 분량

봇은 채우고 사람은 채우지 않는 허니팟 필드를 추가하는 방법

스크린 리더, 키보드, 자동 완성에 걸리지 않으면서 봇을 잡는 허니팟을 어떤 폼에든 추가하고, 스택별 서버 검사까지 구현하세요.

Preet SutharIkiform 창업자

텍스트 입력을 하나 더 추가해 화면 밖에 숨기고, 탭 순서에서 빼고, aria-hidden으로 감싼 뒤 어떤 브라우저도 자동 완성하지 않는 이름을 붙이세요. 사람은 절대 채우지 않지만, 모든 입력을 채우는 봇은 채웁니다. 서버에서는 이 필드에 값이 있는 POST를 스팸으로 처리하고, 실제 메시지와 똑같이 응답하세요.

허니팟이 다른 점

허니팟은 두 가지 조건을 동시에 만족해야 합니다. HTML을 읽는 봇에게는 채울 만한 평범한 필드로 보여야 합니다. 모든 사람에게는 존재하지 않아야 하며, 여러분과 다른 방식으로 폼을 쓰는 사람도 포함됩니다. 스크린 리더를 쓰는 사람, 키보드로 탭 이동하는 사람, 비밀번호 관리자가 폼을 대신 채워 주는 사람입니다. 허니팟 버그는 대부분 이 중 누군가를 잊어서 생기며, 증상은 실제 메시지가 조용히 스팸으로 분류되는 것입니다.

사람과 봇이 같은 허니팟 필드를 만나는 방식어떤 브라우저나 보조 기술을 쓰는 사람스크립트 봇폼을 채움화면, 키보드, 스크린 리더함정을 건너뜀화면 밖, tabindex-1,aria-hidden자동 완성이 건너뜀브라우저가 모르는 이름받음함정이 비어 있음HTML을 파싱모든 입력을 찾음모두 채움함정까지 포함바로 POST스팸같은 성공 응답사람과 봇이 같은 허니팟 필드를 만나는 방식어떤 브라우저나 보조 기술을 쓰는 사람스크립트 봇폼을 채움화면, 키보드, 스크린 리더함정을 건너뜀화면 밖, tabindex -1, aria-hidden자동 완성이 건너뜀브라우저가 모르는 이름받음함정이 비어 있음HTML을 파싱모든 입력을 찾음모두 채움함정까지 포함바로 POST스팸같은 성공 응답
  1. 사람은 화면, 키보드, 스크린 리더 등 자신이 쓰는 도구로 폼을 채웁니다.
  2. 함정은 화면 밖에 있고, 탭 순서에서 빠져 있으며, 보조 기술에서 숨겨져 있으므로 사람은 절대 닿지 않습니다.
  3. 이름이 브라우저와 비밀번호 관리자가 자동 완성하지 않는 것이라 비어 있는 채로 남습니다.
  4. 함정이 빈 채로 POST가 도착하고 받음 처리됩니다.
  5. 스크립트 봇은 HTML을 파싱해 함정을 포함한 모든 입력을 찾습니다.
  6. 모두 채워서 바로 POST합니다.
  7. 함정에 값이 있으므로 POST는 스팸입니다. 봇은 그래도 평범한 성공 응답을 받습니다.
윗줄의 각 안전장치는 실제 사람이 필드에 닿을 수 있는 경로를 하나씩 막습니다. 하나라도 빠지면 윗줄도 스팸으로 끝날 수 있습니다.

마크업

함정은 폼 안에 실제 필드와 나란히 일반 텍스트 입력으로 두세요. type="hidden" 입력은 효과가 없습니다. 봇은 사람이 그런 입력을 편집할 수 없다는 것을 알고 건드리지 않습니다.

contact.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="POST">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />
  <label for="message">Message</label>
  <textarea id="message" name="message" required></textarea>

  <div class="hp" aria-hidden="true">
    <label for="hp-field">Leave this empty</label>
    <input id="hp-field" name="_gotcha" type="text" tabindex="-1" autocomplete="off" />
  </div>

  <button>Send</button>
</form>
스타일
<style>
.hp {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
</style>
  • 래퍼의 aria-hidden="true"는 레이블과 입력을 접근성 트리에서 제거하므로 스크린 리더가 읽지 않습니다.
  • tabindex="-1"은 Tab 키가 아무도 볼 수 없는 입력에 멈추지 않게 합니다.
  • 레이블에는 Leave this empty라고 적습니다. 어떤 도구가 여전히 필드를 노출하더라도 읽는 사람은 어떻게 해야 하는지 알 수 있습니다.
  • required는 추가하지 마세요. 사람이 볼 수 없는 필수 필드는 모든 정상 제출을 막습니다.

숨기는 방법

봇은 대놓고 숨긴 필드를 건너뛰는 법을 배웠습니다. 단순한 스크레이퍼는 여전히 모두 채우지만, 일부는 입력하기 전에 hidden 속성이나 인라인 display: none을 확인합니다. 숨기는 장치가 입력 자체에서 멀수록 더 많은 봇이 속습니다.

기법사람에게 숨겨짐여전히 잡히는 봇
hidden 속성 또는 인라인 display: none예가장 단순한 봇만. 똑똑한 봇이 가장 먼저 확인하는 부분
스타일시트의 화면 밖 클래스예대부분. 봇이 CSS를 불러와 적용해야만 알아챔
opacity: 0 또는 크기 0대체로. 일부 돋보기 도구와 포커스 윤곽선에는 여전히 드러남대부분. 하지만 사람에게는 더 위험
로드 후 JavaScript로 추가예스크립트를 실행하는 봇. 일반 HTML 스크레이퍼는 보지 못하므로 놓침

스타일시트의 화면 밖 클래스를 쓰세요. 가장 많은 봇을 잡고 사람에게도 가장 안전합니다. 허니팟 하나가 효과를 잃으면 더 교묘한 숨김 기법 대신 이름이 다른 허니팟을 하나 더 추가하세요.

자동 완성이 무시하는 이름 고르기

브라우저와 비밀번호 관리자는 필드의 name, id, 레이블, autocomplete 값을 보고 추측해서 채웁니다. autocomplete="off"는 힌트일 뿐이며, 일부 브라우저는 주소나 연락처처럼 보이는 필드에서 이를 무시합니다. 그래서 실제로 일을 하는 것은 이름입니다.

피할 것이유대신 쓸 것
email2, phone, name, address자동 완성이 알아보고 실제 사람의 정보로 채움_gotcha, company_website
honeypot, trap, bot일부 봇은 이름이 뻔한 필드를 건너뜀평범하고 그럴듯한 이름
url, website 단독일부 자동 완성 도구는 저장된 프로필로 웹사이트 필드를 채움company_website 같은 복합 이름

서버에서 확인하기

허니팟은 서버가 읽어야만 작동합니다. 봇은 여러분의 스크립트를 실행하지 않으므로 브라우저 측 검사는 쓸모가 없습니다. 필드에 값이 있으면 실제 메시지와 똑같이 응답하되, POST를 저장하거나 전달하지 마세요. 응답이 다르면 어떤 필드 때문에 들켰는지 봇에게 알려 주게 됩니다. 아래 예제는 여러분의 saveMessage를 호출합니다. 메시지를 저장하거나 이메일로 보내는 코드로 바꾸세요.

일반 HTML 폼에는 자체 서버가 없습니다. 폼을 Ikiform으로 연결하고 함정 이름을 _gotcha로 짓거나, Honeypot field에서 폼마다 이름을 바꾸세요. Ikiform이 서버 검사를 하며, 함정은 응답으로 저장되지 않습니다.

contact.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="POST">
  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />
  <label for="message">Message</label>
  <textarea id="message" name="message" required></textarea>

  <div class="hp" aria-hidden="true">
    <label for="hp-field">Leave this empty</label>
    <input id="hp-field" name="_gotcha" type="text" tabindex="-1" autocomplete="off" />
  </div>

  <button>Send</button>
</form>
함정에 걸린 POST는 저장하지 않더라도 로그를 남기세요. 하루 단위 횟수를 보면 허니팟이 여전히 제 몫을 하는지 알 수 있고, 갑자기 0으로 떨어지면 봇이 건너뛰는 법을 배웠다는 뜻입니다.

Ikiform이 허니팟을 처리하는 방식

모든 Ikiform 폼에는 기본적으로 _gotcha라는 허니팟이 있습니다. 폼 설정의 Honeypot field에서 폼마다 바꿀 수 있으며, 어떤 이름을 고르든 예약된 이름이 되어 응답으로 저장되지 않습니다. 허니팟이 채워지면 스팸 점수에 5가 더해지며, 이것만으로 기준값 5에 도달합니다.

Ikiform은 POST를 버리지 않습니다. 스팸으로 보관하므로, 실제 사람이 함정에 걸리더라도 메시지를 찾아 복원할 수 있습니다. 스팸으로 판정된 POST는 실제 제출과 같은 성공 응답을 받으므로 봇은 아무것도 알 수 없습니다. spam 상태로 저장되고, 알림 이메일과 웹훅을 보내지 않으며, 사용량에 포함되지 않고, 30일 뒤 삭제됩니다. 스팸을 차단한 워크스페이스에는 매주 월요일 합계, 가장 많이 받은 폼, 주요 사유를 담은 요약 이메일이 한 통 갑니다. 스팸 문서와 HTML 폼에 레퍼런스가 있습니다.

작동 확인하기

  1. 스크린 리더를 켜고 폼을 불러와 이동해 보세요. 함정은 한 번도 읽히지 않습니다.
  2. Tab 키로 폼을 이동해 보세요. 포커스가 마지막 실제 필드에서 바로 버튼으로 갑니다.
  3. 브라우저와 비밀번호 관리자가 폼을 자동 완성하게 해 보세요. 함정은 비어 있습니다.
  4. 허니팟을 채워 _gotcha=bot으로 POST하세요. 여전히 성공 응답을 받지만, 제출은 스팸으로 저장되고 이메일이나 웹훅을 보내지 않습니다.
  5. 평범한 메시지를 보내세요. 함정 필드의 흔적 없이 받음 상태로 도착합니다.

주의할 점

  • JSON POST. FormData 대신 본문을 직접 만든다면 함정을 포함하세요. 그렇지 않으면 서버는 봇의 POST에서도 함정을 빈 값으로 읽습니다.
  • 필드 이름을 바꾸는 폼 빌더. 일부 빌더는 입력 이름에 접두사를 붙이거나 이름을 바꿉니다. 함정의 실제 이름은 편집기가 아니라 POST된 본문에서 확인하세요.
  • 캐싱. 이름을 바꾼 뒤에도 캐시된 페이지에 옛 함정 이름이 남아 있으면, POST가 옛 이름으로 도착해 통과합니다. 양쪽 이름을 동시에 바꾸세요.
  • 체크박스 허니팟. 숨겨진 체크박스도 작동하지만, 스크린 리더는 엉뚱한 체크박스를 텍스트 입력보다 더 서툴게 처리합니다. 텍스트 입력을 쓰세요.
  • 하나의 계층일 뿐. 실제 브라우저를 실행하고 보이지 않는 필드를 건너뛰는 봇은 어떤 허니팟이든 그냥 지나칩니다. 레이트 리밋과 함께 쓰고, 필요하면 사람 확인도 더하세요.

질문

막히는 단계가 있거나 내 상황에 맞는 가이드가 필요하다면 이메일을 보내세요: hello@ikiform.com.