Token导航 LogoToken导航TokenDH.com
待分类操作浏览器github未标认证来源可访问许可证需确认审计通过

spam-prevention垃圾邮件预防

Agent Skill

spam-prevention 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

705

周安装

12

GitHub Stars

公开资料未说明

下载量

97
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:spam-prevention(垃圾邮件预防)
来源仓库:https://github.com/hwatkins/my-skills
仓库路径:skills/spam-prevention
安装命令:
npx skills add https://github.com/hwatkins/my-skills --skill spam-prevention
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/hwatkins/my-skills --skill spam-prevention

简介

spam-prevention 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中围绕仓库状态、代码变更或协作事项进行整理。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装,需结合原始 README 核验具体用法。
  • 安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写操作。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Spam & Bot Prevention for SaaS

Expert guidance for preventing spam signups, bot accounts, and abuse of registration and trial flows in SaaS applications.

Core Principles

  • Layer defenses — no single technique stops all spam; combine multiple signals
  • Minimize friction for real users — invisible protections first, visible challenges only when needed
  • Fail closed on high-risk signals — block or queue for review rather than letting spam through
  • Monitor and adapt — spammers evolve; your defenses must too

Defense Layers (Priority Order)

Layer 1: Invisible Protections (Zero Friction)

These run silently — real users never notice them.

Honeypot Fields

Add hidden form fields that real users won't fill in but bots will.

<!-- Hidden from real users via CSS -->
<div style="position: absolute; left: -9999px;" aria-hidden="true">
  <label for="website">Website</label>
  <input type="text" name="website" id="website" tabindex="-1" autocomplete="off" />
</div>
# Server-side: reject if honeypot field has a value
def create(conn, %{"user" => user_params}) do
  if user_params["website"] && user_params["website"] != "" do
    # Bot detected — return fake success (don't reveal detection)
    conn
    |> put_flash(:info, "Check your email to confirm your account.")
    |> redirect(to: ~p"/")
  else
    # Real signup flow
    Accounts.register_user(user_params)
  end
end

Time-Based Detection

Bots submit forms instantly. Real users take time.

# Add a hidden timestamp field
def new(conn, _params) do
  token = Phoenix.Token.sign(conn, "form_time", System.system_time(:second))
  render(conn, :new, form_token: token)
end

def create(conn, %{"form_token" => token} = params) do
  case Phoenix.Token.verify(conn, "form_time", token, max_age: 3600) do
    {:ok, timestamp} ->
      elapsed = System.system_time(:second) - timestamp
      if elapsed < 3 do
        # Submitted in under 3 seconds — likely bot
        fake_success_response(conn)
      else
        real_signup(conn, params)
      end
    {:error, _} ->
      fake_success_response(conn)
  end
end

JavaScript Challenge

Many bots don't execute JavaScript. Require a JS-generated token.

// On page load, generate a token after a delay
setTimeout(() => {
  document.getElementById('js_token').value = btoa(Date.now().toString());
}, 1000);
# Server: reject if js_token is missing or invalid
if is_nil(params["js_token"]) or params["js_token"] == "" do
  fake_success_response(conn)
end

Layer 2: Email Verification

Disposable Email Blocking

Block known throwaway email domains.

# Use a library like `disposable_email_domains` or maintain your own list
@disposable_domains File.read!("priv/disposable_domains.txt")
  |> String.split("\n", trim: true)
  |> MapSet.new()

def validate_email_domain(changeset) do
  validate_change(changeset, :email, fn :email, email ->
    domain = email |> String.split("@") |> List.last() |> String.downcase()
    if MapSet.member?(@disposable_domains, domain) do
      [email: "please use a permanent email address"]
    else
      []
    end
  end)
end

Sources for disposable domain lists:

  • disposable-email-domains (community-maintained, 100k+ domains)
  • Services: Kickbox, ZeroBounce, NeverBounce (API-based, real-time)

Email Confirmation Flow

Require email verification before activating accounts.

def register_user(attrs) do
  %User{}
  |> User.registration_changeset(attrs)
  |> Ecto.Changeset.put_change(:confirmed_at, nil)
  |> Repo.insert()
  |> case do
    {:ok, user} ->
      deliver_confirmation_email(user)
      {:ok, user}
    error -> error
  end
end

# Don't allow login until confirmed
def get_user_by_email_and_password(email, password) do
  user = Repo.get_by(User, email: email)
  if user && user.confirmed_at && User.valid_password?(user, password) do
    user
  end
end

MX Record Validation

Verify the email domain actually has mail servers.

def valid_mx_record?(email) do
  domain = email |> String.split("@") |> List.last()
  case :inet_res.lookup(to_charlist(domain), :in, :mx) do
    [] -> false
    _records -> true
  end
end

Layer 3: Rate Limiting

IP-Based Rate Limiting

# Using Hammer (Elixir rate limiter)
def create(conn, params) do
  ip = conn.remote_ip |> :inet.ntoa() |> to_string()

  case Hammer.check_rate("signup:#{ip}", 60_000, 5) do
    {:allow, _count} ->
      real_signup(conn, params)
    {:deny, _limit} ->
      conn
      |> put_status(429)
      |> put_flash(:error, "Too many signup attempts. Please try again later.")
      |> render(:new)
  end
end

Fingerprint-Based Rate Limiting

IP alone isn't enough — use browser fingerprinting for additional signal.

// Client-side: generate a fingerprint hash
// Use a library like FingerprintJS (free tier available)
import FingerprintJS from '@fingerprintjs/fingerprintjs';
const fp = await FingerprintJS.load();
const result = await fp.get();
document.getElementById('fp').value = result.visitorId;
# Server: rate limit per fingerprint too
case Hammer.check_rate("signup:fp:#{params["fp"]}", 86_400_000, 3) do
  {:allow, _} -> proceed()
  {:deny, _} -> block()
end

Layer 4: CAPTCHA (Visible Friction)

Use only when invisible layers aren't enough, or as escalation for suspicious behavior.

Progressive CAPTCHA

Don't show CAPTCHA to everyone — only when risk signals are present.

def needs_captcha?(conn, params) do
  ip = conn.remote_ip |> :inet.ntoa() |> to_string()

  cond do
    # High signup rate from this IP
    Hammer.check_rate_inc("signup_check:#{ip}", 3_600_000, 10) == {:deny, 10} -> true
    # Disposable email domain
    disposable_email?(params["email"]) -> true
    # Missing JS token (possible bot)
    is_nil(params["js_token"]) -> true
    # Default: no captcha needed
    true -> false
  end
end

CAPTCHA Options (ranked by UX):

  1. Cloudflare Turnstile — invisible/managed, free, privacy-friendly
  2. hCaptcha — privacy-focused, pays publishers
  3. ALTCHA — self-hosted, GDPR-compliant, proof-of-work based
  4. reCAPTCHA v3 — invisible scoring (but Google tracking concerns)
  5. reCAPTCHA v2 — checkbox/image challenges (most friction)

Layer 5: Post-Signup Detection

Catch spam that gets through initial defenses.

Behavioral Signals

defmodule MyApp.SpamDetection do
  def spam_score(user) do
    score = 0

    # No profile completed within 24 hours
    score = if is_nil(user.name), do: score + 1, else: score

    # No meaningful actions taken
    score = if user.actions_count == 0 and hours_since_signup(user) > 2,
      do: score + 2, else: score

    # Suspicious email patterns (random strings)
    score = if random_looking_email?(user.email), do: score + 2, else: score

    # Signed up from known VPN/proxy
    score = if vpn_ip?(user.signup_ip), do: score + 1, else: score

    score
  end

  defp random_looking_email?(email) do
    local = email |> String.split("@") |> List.first()
    # High ratio of digits to letters, or very long random strings
    digit_ratio = local |> String.graphemes() |> Enum.count(&(&1 =~ ~r/\d/)) |> Kernel./(String.length(local))
    digit_ratio > 0.5 or String.length(local) > 20
  end
end

Automated Cleanup

# Oban job to clean unconfirmed accounts
defmodule MyApp.Workers.CleanupUnconfirmed do
  use Oban.Worker, queue: :maintenance

  @impl Oban.Worker
  def perform(_job) do
    cutoff = DateTime.utc_now() |> DateTime.add(-72, :hour)

    from(u in User,
      where: is_nil(u.confirmed_at),
      where: u.inserted_at < ^cutoff
    )
    |> Repo.delete_all()

    :ok
  end
end

Implementation Checklist

Minimum Viable Protection (Start Here)

  • Honeypot field on signup form
  • Email confirmation required before account activation
  • Disposable email domain blocking
  • IP-based rate limiting (5 signups/hour per IP)
  • Clean up unconfirmed accounts after 72 hours

Enhanced Protection

  • Time-based form submission detection
  • JavaScript challenge token
  • MX record validation
  • Progressive CAPTCHA (Cloudflare Turnstile)
  • Browser fingerprint rate limiting

Advanced Protection

  • Post-signup behavioral scoring
  • VPN/proxy detection
  • Phone number verification for high-value actions
  • Machine learning anomaly detection
  • Manual review queue for borderline cases

Monitoring & Metrics

Track these to know if your defenses are working:

MetricWhat It Tells You
Signup-to-confirmation rateLow = spam getting through signup
Confirmation-to-activation rateLow = real users having trouble
Signups per IP per hourSpikes = bot attack
Disposable email block rateRising = targeted by spam
CAPTCHA trigger rateToo high = too aggressive; too low = too lenient
False positive rateReal users being blocked (monitor support tickets)

Common Mistakes

  • Blocking too aggressively — false positives lose real customers. Start lenient, tighten based on data.
  • Relying on CAPTCHA alone — bots solve CAPTCHAs. Layer defenses.
  • Revealing detection — don't tell bots they were caught. Return fake success responses.
  • Hardcoding disposable domains — the list grows daily. Use an updated source or API.
  • No monitoring — you won't know defenses failed until spam is everywhere.
  • Blocking VPNs entirely — many legitimate users use VPNs. Use as a signal, not a block.

Related Skills

  • saas-security: Broader security (auth, API protection, account takeover)
  • stripe-integration: Payment verification as anti-spam signal
  • email-sequence: Confirmation and verification email flows

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

35.44%
按下载量换算34

Claude

31.08%
按下载量换算30

Cursor

19.6%
按下载量换算19

Gemini CLI

10.1%
按下载量换算10

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills