UK

Тема

Колір акценту

Тема й колір збережуться на satelit.md і в застосунку.

Панель Обрати тариф

Satelit Shield, від скрипта до сервера.

Базова адреса: https://api.satelit.md/shield/v1. Сторінка використовує публічний ключ сайту; ваш сервер надсилає секрет в Authorization: Bearer shs_….

  1. Додайте сайт і отримайте ключі

    У панелі ви додаєте сайт і його домени. Ви отримуєте ключ сайту, публічний (shp_ і 24 символи), і секрет (shs_ і 40), який бачите один раз.

    Захистити сайтЗахистити сайт

  2. Поставте сенсор на сторінки

    Напряму або через first-party проксі. Новий сайт починає з усіма правилами в спостереженні (shadow).

    <!-- in every page, direct -->
    <script src="https://api.satelit.md/shield/v1/sensor.js"
      data-site="shp_XXXXXXXXXXXXXXXXXXXXXXXX" async></script>
    # Caddyfile: /_sh/* goes to Shield, with your secret and the visitor's address
    example.md {
    	handle_path /_sh/* {
    		rewrite * /shield/v1{uri}
    		reverse_proxy https://api.satelit.md {
    			header_up Host {upstream_hostport}
    			header_up X-Shield-Secret {env.SHIELD_SECRET}
    			header_up X-Shield-Client-IP {client_ip}
    			header_up -Cookie
    			header_up -Authorization
    		}
    	}
    	# ... the rest of your site
    }
    
    # in every page:
    # <script src="/_sh/sensor.js" data-site="shp_XXXXXXXXXXXXXXXXXXXXXXXX" data-base="/_sh" async></script>
    # nginx: /_sh/ goes to Shield, with your secret and the visitor's address
    location /_sh/ {
        proxy_pass https://api.satelit.md/shield/v1/;
        proxy_ssl_server_name on;
        proxy_set_header Host api.satelit.md;
        proxy_set_header X-Shield-Secret "shs_...";        # keep this file out of your repository
        proxy_set_header X-Shield-Client-IP $remote_addr;  # behind Cloudflare: $http_cf_connecting_ip
        proxy_set_header Cookie "";
        proxy_set_header Authorization "";
    }
    
    # in every page:
    # <script src="/_sh/sensor.js" data-site="shp_XXXXXXXXXXXXXXXXXXXXXXXX" data-base="/_sh" async></script>
  3. Прочитайте події, потім увімкніть захист

    У панелі видно, що зупинило б кожне правило. Переведіть у enforce правила, яким довіряєте.

Атрибути скрипта

АтрибутЩо робить
data-siteОбов'язковий. Публічний ключ сайту.
data-baseКуди сенсор надсилає звіти. Типово: тека, з якої завантажено скрипт; через first-party проксі: /_sh.
data-overlay="0"0: сенсор ніколи не малює перевірку на сторінці; сторінку перевірки віддає ваш сервер.
HTML
<!-- in every page, direct -->
<script src="https://api.satelit.md/shield/v1/sensor.js"
  data-site="shp_XXXXXXXXXXXXXXXXXXXXXXXX" async></script>

First-party проксі

Ваш сервер передає /_sh/* на https://api.satelit.md/shield/v1/*, додає X-Shield-Secret і X-Shield-Client-IP (і, за бажанням, X-Shield-Client-Country) та не змінює User-Agent, Accept-Language, Sec-Fetch-* і Sec-CH-UA*. Адресі клієнта вірять лише разом із правильним секретом. Cookie ваших відвідувачів не мають іти до Shield.

# Caddyfile: /_sh/* goes to Shield, with your secret and the visitor's address
example.md {
	handle_path /_sh/* {
		rewrite * /shield/v1{uri}
		reverse_proxy https://api.satelit.md {
			header_up Host {upstream_hostport}
			header_up X-Shield-Secret {env.SHIELD_SECRET}
			header_up X-Shield-Client-IP {client_ip}
			header_up -Cookie
			header_up -Authorization
		}
	}
	# ... the rest of your site
}

# in every page:
# <script src="/_sh/sensor.js" data-site="shp_XXXXXXXXXXXXXXXXXXXXXXXX" data-base="/_sh" async></script>
# nginx: /_sh/ goes to Shield, with your secret and the visitor's address
location /_sh/ {
    proxy_pass https://api.satelit.md/shield/v1/;
    proxy_ssl_server_name on;
    proxy_set_header Host api.satelit.md;
    proxy_set_header X-Shield-Secret "shs_...";        # keep this file out of your repository
    proxy_set_header X-Shield-Client-IP $remote_addr;  # behind Cloudflare: $http_cf_connecting_ip
    proxy_set_header Cookie "";
    proxy_set_header Authorization "";
}

# in every page:
# <script src="/_sh/sensor.js" data-site="shp_XXXXXXXXXXXXXXXXXXXXXXXX" data-base="/_sh" async></script>

JavaScript API

  • SatelitShield.ready(fn): fn({a, t, explain}) після першого звіту сторінки. a: '' нічого, 'g' перевірка, 'b' зупинено.
  • SatelitShield.token(fn): fn(token) зі свіжим токеном вердикту, для форми чи виклику API. Його ви перевіряєте на сервері.
  • SatelitShield.version: версія сенсора.
  • Подія satelit-shield на document, з тим самим detail, що й у ready.
JavaScript
// after the page's first report
SatelitShield.ready(function (r) {
  // r.a: "" nothing, "g" a check, "b" blocked; r.t: a verdict token
});
document.addEventListener("satelit-shield", function (e) {
  console.log(e.detail.a, e.detail.t);
});

// a fresh token goes with the form, in a hidden field
form.addEventListener("submit", function (e) {
  e.preventDefault();
  SatelitShield.token(function (token) {
    form.elements.shield_token.value = token;
    form.submit();
  });
});

Сторінка перевірки й сторінка зупинки

Режим сторінки задає атрибут data-shield елемента html. Без атрибута: звичайна сторінка. check: ваша сторінка перевірки зі статусом 429; сенсор розв'язує її, зберігає перепустку й перезавантажує ту саму адресу. ban: сторінка зі статусом 403; сенсор лише перекладає її.

429
<!doctype html>
<html lang="ro" data-shield="check">
<head>
  <meta charset="utf-8">
  <meta content="noindex" name="robots">
  <script src="/_sh/sensor.js" data-site="shp_XXXXXXXXXXXXXXXXXXXXXXXX" data-base="/_sh" async></script>
</head>
<body><p>Se verifică browserul.</p></body>
</html>

Cookie

  • __shpПерепустка після розв'язаної перевірки: p1.{exp}.{token}.{sig}.
  • __shs1: браузер виконав сенсор (400 днів), щоб читач, який повертається, не вважався клієнтом без доказу.

Обидві first-party, на вашому домені: SameSite=Lax, Secure на https, шлях /.

Перепустка, перевірена у вас

Перепустка перевіряється на вашому сервері, без жодного виклику Shield.

  • Ключ перепустки: HMAC-SHA256(key = sha256_hex(secret), "satelit-shield-pass-v1"), у hex. Обчисліть його один раз.
  • sig: перші 22 символи з base64url(HMAC-SHA256(ключ перепустки, "p1|{exp}|{token}|{client key}|{User-Agent}")); exp у секундах unix, token: 16 hex-символів.
  • Перепустка дійсна, поки exp не минув і підпис збігається з ключем клієнта та User-Agent цього запиту: перепустка, скопійована на іншу адресу чи в інший браузер, нічого не відкриває.
  • Строк: 12 годин; 2 години для ризикованих клієнтів (Tor, хостингова мережа, пул адрес, жорстке рішення за останні 7 днів). Перепустка відкриває перевірку, але ніколи не зупинку.
// Satelit Shield: check a pass (the __shp cookie) on your own server, with no call to Shield. Node 18+.
// A pass is p1.{exp}.{token}.{sig}; sig is the first 22 characters of
// base64url(HMAC-SHA256(pass key, "p1|{exp}|{token}|{client key}|{User-Agent}")).
import { createHash, createHmac, timingSafeEqual } from "node:crypto";

// The pass key: HMAC-SHA256 with the key sha256_hex(secret) over "satelit-shield-pass-v1", in hex.
export function passKey(secret) {
  const key = createHash("sha256").update(secret).digest("hex");
  return createHmac("sha256", key).update("satelit-shield-pass-v1").digest("hex");
}

// The client key: an IPv4 address as it is, an IPv6 address as its /64 (the first four groups and "::").
export function clientKey(ip) {
  const v4 = /^(?:::ffff:)?(\d{1,3}(?:\.\d{1,3}){3})$/i.exec(ip);
  if (v4) return v4[1];
  const [head, tail = ""] = ip.toLowerCase().split("%")[0].split("::");
  const a = head ? head.split(":") : [];
  const b = tail ? tail.split(":") : [];
  const groups = [...a, ...Array(Math.max(0, 8 - a.length - b.length)).fill("0"), ...b];
  return groups.slice(0, 4).map((g) => parseInt(g, 16).toString(16)).join(":") + "::";
}

// True when the pass has not expired and was signed for this address and this browser.
export function passValid(pass, secret, ip, userAgent, now = Date.now() / 1000) {
  const m = /^p1\.(\d+)\.([0-9a-f]{16})\.([A-Za-z0-9_-]{22})$/.exec(pass ?? "");
  if (!m || Number(m[1]) <= now) return false;
  const message = `p1|${m[1]}|${m[2]}|${clientKey(ip)}|${userAgent}`;
  const sig = createHmac("sha256", passKey(secret)).update(message).digest("base64url").slice(0, 22);
  return timingSafeEqual(Buffer.from(sig), Buffer.from(m[3]));
}
"""Satelit Shield: check a pass (the __shp cookie) on your own server, with no call to Shield. Python 3.8+.

A pass is p1.{exp}.{token}.{sig}; sig is the first 22 characters of
base64url(HMAC-SHA256(pass key, "p1|{exp}|{token}|{client key}|{User-Agent}")).
"""
import base64
import hashlib
import hmac
import ipaddress
import re
import time

PASS = re.compile(r"p1\.(\d+)\.([0-9a-f]{16})\.([A-Za-z0-9_-]{22})")


def pass_key(secret):
    """HMAC-SHA256 with the key sha256_hex(secret) over "satelit-shield-pass-v1", in hex."""
    key = hashlib.sha256(secret.encode()).hexdigest().encode()
    return hmac.new(key, b"satelit-shield-pass-v1", hashlib.sha256).hexdigest()


def client_key(ip):
    """An IPv4 address as it is, an IPv6 address as its /64 (the first four groups and "::")."""
    a = ipaddress.ip_address(ip.split("%")[0])
    if a.version == 6 and a.ipv4_mapped:
        a = a.ipv4_mapped
    if a.version == 4:
        return str(a)
    return ":".join(format(int(g, 16), "x") for g in a.exploded.split(":")[:4]) + "::"


def pass_valid(value, secret, ip, user_agent, now=None):
    """True when the pass has not expired and was signed for this address and this browser."""
    m = PASS.fullmatch(value or "")
    if not m or int(m[1]) <= (time.time() if now is None else now):
        return False
    message = f"p1|{m[1]}|{m[2]}|{client_key(ip)}|{user_agent}".encode()
    mac = hmac.new(pass_key(secret).encode(), message, hashlib.sha256).digest()
    sig = base64.urlsafe_b64encode(mac).decode().rstrip("=")[:22]
    return hmac.compare_digest(sig, m[3])

Ключ клієнта

Адреса IPv4 як є; для IPv6: мережа /64, записана як перші чотири групи та :: (2a01:4f8:221:1f15::). Зупинки, перевірки, перепустки й лічильники прив'язані до ключа клієнта.

Серверні виклики

Усі потребують Authorization: Bearer shs_… і робляться лише з вашого сервера.

POST /verify

Перевіряє токен вердикту. Токен живе 300 секунд і перевіряється один раз; інакше success дорівнює false, з error: used, expired, invalid або other_ip. З ip токен від іншого ключа клієнта відхиляється. score: від 0, людина, до 100, точно бот.

POST /verify
curl https://api.satelit.md/shield/v1/verify \
  -H "Authorization: Bearer $SHIELD_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
  "token": "<the verdict token>",
  "ip": "203.0.113.9",
  "ua": "Mozilla/5.0 ..."
}'
200
{
  "ok": true,
  "success": true,
  "score": 3,
  "verdict": "human",
  "action": "allow",
  "rules": [],
  "signals": {
    "hard": [],
    "soft": [],
    "st": []
  },
  "crawler": null,
  "issued_at": "2026-11-05T21:00:00Z",
  "age_s": 4
}

POST /assess

Один запит, синхронно. challenge: віддайте сторінку перевірки (429); block: 403. Рішення в спостереженні відповідає allow, з would. Запит враховується правилами точно як один рядок /ingest.

POST /assess
curl https://api.satelit.md/shield/v1/assess \
  -H "Authorization: Bearer $SHIELD_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
  "ip": "203.0.113.9",
  "ua": "Mozilla/5.0 ...",
  "method": "GET",
  "path": "/ro/catalog/",
  "host": "example.md",
  "kind": "html",
  "headers": {
    "sec-fetch-site": "none",
    "sec-ch-ua": "...",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "accept-language": "ro-RO,ro;q=0.9",
    "referer": ""
  },
  "pass": "",
  "seen": true
}'
200
{
  "action": "allow",
  "rule": "",
  "mode": "",
  "would": "",
  "crawler": null,
  "key": "203.0.113.9",
  "until": null
}

POST /ingest

Пакети не більше 500 рядків, без очікування відповіді. k: html сторінка, api виклик JSON, asset файл. pass: 0 немає, 1 дійсна, 2 є, але недійсна. seen: cookie __shs. g: 1, коли відповіддю була сторінка перевірки.

POST /ingest
curl https://api.satelit.md/shield/v1/ingest \
  -H "Authorization: Bearer $SHIELD_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
  "lines": [
    {
      "t": 1762369200000,
      "ip": "203.0.113.9",
      "ua": "Mozilla/5.0 ...",
      "m": "GET",
      "p": "/ro/catalog/",
      "s": 200,
      "k": "html",
      "h": {
        "sfs": "none",
        "chua": "...",
        "chm": "?0",
        "chp": "\"Windows\"",
        "al": "ro-RO",
        "ref": ""
      },
      "pass": 1,
      "seen": 1,
      "g": 0
    }
  ]
}'
200
{
  "ok": true,
  "n": 1,
  "decided": 0
}

GET /decisions?since=0

since=0 або без since дає весь активний список; з отриманим курсором: лише зміни. a: b зупинка (403), g перевірка (429 без дійсної перепустки), - знято. Рішення в спостереженні тут не з'являються. Читайте список раз на 15–30 секунд і тримайте його в пам'яті.

GET /decisions?since=0
curl "https://api.satelit.md/shield/v1/decisions?since=0" \
  -H "Authorization: Bearer $SHIELD_SECRET"
200
{
  "cursor": "1234",
  "full": true,
  "items": [
    {
      "key": "203.0.113.9",
      "a": "b",
      "rule": "automation",
      "until": "2026-11-06T21:00:00Z"
    },
    {
      "key": "2a01:4f8:1:2::",
      "a": "g",
      "rule": "no-proof",
      "until": "2026-11-06T21:00:00Z"
    },
    {
      "key": "198.51.100.4",
      "a": "-",
      "rule": "",
      "until": null
    }
  ]
}

GET /config

Налаштування сайту: режими, пороги, шлях-пастка, строк перепустки, тариф і витрата за місяць.

GET /config
curl "https://api.satelit.md/shield/v1/config" \
  -H "Authorization: Bearer $SHIELD_SECRET"
200
{
  "site": "shp_XXXXXXXXXXXXXXXXXXXXXXXX",
  "name": "example.md",
  "modes": {
    "automation": "enforce",
    "no-proof": "shadow"
  },
  "thresholds": {},
  "trap": "/_sh/t/9f3k2",
  "overlay": true,
  "pass_life": 43200,
  "plan": "shield_pro",
  "quota": {
    "month": "2026-11",
    "used": 1234,
    "limit": 2500000
  }
}

POST /trust

Захищена адреса на кілька днів: жорсткі правила лише показують перевірку. Для учасника, що платить, або офісу.

POST /trust
curl https://api.satelit.md/shield/v1/trust \
  -H "Authorization: Bearer $SHIELD_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
  "ip": "203.0.113.9",
  "days": 30
}'
200
{
  "ok": true,
  "key": "203.0.113.9",
  "until": "2026-12-05T21:00:00+00:00"
}

GET /events?limit=50 · GET /stats?days=7

Ті самі рядки, що в панелі: події з фільтрами за правилом і дією та цифри за днями.

GET /events?limit=50
curl "https://api.satelit.md/shield/v1/events?limit=50" \
  -H "Authorization: Bearer $SHIELD_SECRET"
200
{
  "items": [
    {
      "id": 81,
      "at": "2026-11-05T21:04:00Z",
      "key": "198.51.100.7",
      "rule": "automation",
      "action": "ban",
      "mode": "enforce",
      "ua": "Mozilla/5.0 ... HeadlessChrome/141.0.0.0 ...",
      "evidence": {
        "hard": [
          "wd"
        ]
      }
    }
  ]
}

Перевіряйте токен у формах

Під час голосування, коментаря чи реєстрації сторінка бере токен через SatelitShield.token() і надсилає його з формою. Ваш сервер перевіряє його один раз, протягом 300 секунд.

curl https://api.satelit.md/shield/v1/verify \
  -H "Authorization: Bearer $SHIELD_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "<the token from the form>",
    "ip": "203.0.113.9"
  }'
// Node 18+: the token came with the form, ip is the visitor's address
async function fromPerson(token, ip) {
  const res = await fetch("https://api.satelit.md/shield/v1/verify", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.SHIELD_SECRET}`, "Content-Type": "application/json" },
    body: JSON.stringify({ token, ip }),
  });
  const v = await res.json();
  return v.success === true && v.action !== "block";
}
import json, os, urllib.request

def from_person(token, ip):
    req = urllib.request.Request(
        "https://api.satelit.md/shield/v1/verify",
        data=json.dumps({"token": token, "ip": ip}).encode(),
        headers={"Authorization": f"Bearer {os.environ['SHIELD_SECRET']}", "Content-Type": "application/json"},
    )
    with urllib.request.urlopen(req, timeout=5) as res:
        v = json.load(res)
    return v.get("success") is True and v.get("action") != "block"
<?php
function from_person(string $token, string $ip): bool {
    $ch = curl_init("https://api.satelit.md/shield/v1/verify");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_TIMEOUT => 5,
        CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("SHIELD_SECRET"), "Content-Type: application/json"],
        CURLOPT_POSTFIELDS => json_encode(["token" => $token, "ip" => $ip]),
    ]);
    $v = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return ($v["success"] ?? false) === true && ($v["action"] ?? "") !== "block";
}

Middleware на Node

Рішення в пам'яті, запити в /ingest пакетами, перепустка перевіряється локально. Працює як є, з SHIELD_SECRET і SHIELD_SITE у середовищі.

За бажанням, для всього сайту. Middleware тримає рішення в пам'яті, читає їх з /decisions раз на 15–30 секунд і надсилає запити пакетами в /ingest. Жоден запит відвідувача не чекає на мережу.

shield-middleware.mjs
// Satelit Shield in a Node server: Express or any (req, res, next) stack. Node 18+, no packages.
// The decisions stay in memory and are read again every 20 s, so a visitor's request never waits for the network.
// Every request is sent to /ingest in batches. /_sh/ (the sensor) is served by the proxy in front, step 1.
import { clientKey, passValid } from "./shield-pass.mjs";

const API = process.env.SHIELD_API || "https://api.satelit.md/shield/v1";
const SECRET = process.env.SHIELD_SECRET;   // shs_..., never in a page
const SITE = process.env.SHIELD_SITE;       // shp_..., the public site key
const HEADERS = { Authorization: `Bearer ${SECRET}`, "Content-Type": "application/json" };
const decisions = new Map();                // client key -> { a: "b" | "g", rule, until }
const lines = [];
let cursor = "0";

export async function poll() {
  const res = await fetch(`${API}/decisions?since=${cursor}`, { headers: HEADERS });
  if (!res.ok) return;
  const body = await res.json();
  if (body.full) decisions.clear();
  for (const d of body.items) {
    if (d.a === "-") decisions.delete(d.key);
    else decisions.set(d.key, d);
  }
  cursor = body.cursor;
}

export async function flush() {
  while (lines.length) {
    const batch = lines.splice(0, 500);
    await fetch(`${API}/ingest`, { method: "POST", headers: HEADERS, body: JSON.stringify({ lines: batch }) }).catch(() => {});
  }
}

export function start() {
  const quiet = (job) => () => job().catch(() => {});
  quiet(poll)();
  setInterval(quiet(poll), 20_000).unref();
  setInterval(quiet(flush), 5_000).unref();
}

const cookie = (req, name) => new RegExp(`(?:^|;\\s*)${name}=([^;]*)`).exec(req.headers.cookie ?? "")?.[1] ?? "";
const page = (mode, text) => `<!doctype html><html data-shield="${mode}"><head><meta charset="utf-8">`
  + `<meta content="noindex" name="robots"><meta name="viewport" content="width=device-width">`
  + `<script src="/_sh/sensor.js" data-site="${SITE}" data-base="/_sh" async></script></head><body><p>${text}</p></body></html>`;

export function shield(req, res, next) {
  const ip = req.ip ?? req.socket.remoteAddress;   // behind a proxy: Express's "trust proxy" gives the visitor's address
  const ua = req.headers["user-agent"] ?? "";
  const path = (req.originalUrl ?? req.url).split("?")[0];
  const shp = cookie(req, "__shp");
  const pass = !shp ? 0 : passValid(shp, SECRET, ip, ua) ? 1 : 2;
  const d = decisions.get(clientKey(ip));
  const on = d && (!d.until || Date.parse(d.until) > Date.now()) ? d.a : "";
  // a ban is a 403; a check is a 429 unless the browser holds a valid pass (a pass never opens a ban)
  const gate = on === "b" ? 403 : on === "g" && pass !== 1 ? 429 : 0;
  res.on("finish", () => {
    const type = String(res.getHeader("content-type") ?? "");
    const kind = /\.(css|js|mjs|png|jpe?g|gif|svg|webp|avif|ico|woff2?)$/i.test(path) ? "asset" : /json/.test(type) ? "api" : "html";
    const h = (name) => req.headers[name] ?? "";
    lines.push({
      t: Date.now(), ip, ua, m: req.method, p: path, s: res.statusCode, k: kind,
      h: { sfs: h("sec-fetch-site"), chua: h("sec-ch-ua"), chm: h("sec-ch-ua-mobile"), chp: h("sec-ch-ua-platform"), al: h("accept-language"), ref: h("referer") },
      pass, seen: cookie(req, "__shs") === "1" ? 1 : 0, g: gate === 429 ? 1 : 0,
    });
  });
  if (!gate) return next();
  res.statusCode = gate;
  res.setHeader("Content-Type", "text/html; charset=utf-8");
  res.setHeader("Cache-Control", "no-store");
  res.end(gate === 429 ? page("check", "Checking your browser.") : page("ban", "Access blocked."));
}

Помилки

Хибний виклик відповідає з error.code і error.message.

КодКоли
unauthorizedСекрету немає або він хибний.
forbidden_originСторінка не на домені сайту (чи його піддомені).
invalidЗапит не тієї форми, що на цій сторінці.
quotaПеревірки місяця скінчилися. Сенсор не ламає сторінку: відповідає так, ніби робити нічого.
busyShield зайнятий. Повторіть за кілька секунд.
not_foundСайту чи ресурсу немає.
401
{
  "error": {
    "code": "unauthorized",
    "message": "The secret is missing or wrong"
  }
}

Правила

Кожне правило має свій режим: off, shadow або enforce.

Жорсткі правила зупиняють незахищену адресу. М'які лише показують перевірку. Правило, що перевищило 50 зупинок на годину, або сайт, що перевищив 200, саме повертається до спостереження, а ви отримуєте подію.

  • automation жорсткесенсор

    WebDriver, ChromeDriver, Selenium, PhantomJS, Nightmare, Playwright, Puppeteer, Cypress, TestCafe, HeadlessChrome.

  • stealth жорсткесенсор

    Залатана автоматизація: підмінений Function.toString, вікно з від'єднаного iframe, перевизначений webdriver, Chromium під виглядом Chrome, worker, що називає себе HeadlessChrome.

  • antidetect жорстке або м'якесенсор

    Підроблений відбиток: рушій JS старший за заявлений браузер, шум на canvas, екран не збігається з CSS, часовий пояс, мова, немає системних шрифтів, worker або Client Hints суперечать.

  • fake-crawler жорсткеобидва шари

    Ім'я робота, спростоване двома незалежними джерелами: офіційним списком IP і зворотним DNS.

  • robots жорсткезапити

    Заявлений бот, що запитує шляхи, заборонені його групі в robots.txt.

  • trap жорсткезапити

    Прихований шлях-пастка сайту, заборонений у robots.txt.

  • challenge-ignored жорсткезапити

    Сторінку перевірки показано одній адресі 6 і більше разів без відповіді.

  • browser-soft м'якесенсор

    Дві й більше слабких ознак: немає плагінів у десктопному Chrome, немає window.chrome, порожні мови, програмний WebGL, протокол DevTools.

  • no-proof м'якезапити

    12 і більше сторінок за 10 хвилин без звіту сенсора й без cookie __shs.

  • rate м'якезапити

    60 і більше сторінок за хвилину.

  • walk м'якезапити

    40 і більше різних сторінок за 10 хвилин.

  • stale-browser м'якезапити

    Десктопний Chrome, старший на 8 і більше версій, без перепустки й без __shs.

  • ua-claim м'якезапити

    Chrome 101+ з повним номером збірки (справжній Chrome пише N.0.0.0) або без Sec-Fetch-Site у Chrome 80+.

  • client-hints м'якезапити

    Заголовки Sec-CH-UA суперечать User-Agent: мобільний, платформа, версія, або їх немає в Chrome 90+.

  • datacenter м'якеобидва шари

    Браузерний User-Agent з хостингової чи хмарної мережі (ASN), без перепустки.

Ніколи не оцінюються
Перевірені роботи й ваш список дозволених.
Лише перевірка, ніколи не зупинка
Виходи Tor, спільні адреси (3 і більше справжніх браузерів на одній адресі за 24 години) та адреси, яким ви довіряєте.

Що зберігає Shield

  • Лише ID перевірок. Сенсор надсилає ID перевірок, кількість взаємодій (без координат і клавіш) і першу мову браузера. Жодного хешу відбитка.
  • Cookie лише на вашому домені. __shp, перепустка після перевірки, і __shs, знак того, що браузер виконав сенсор. Власних cookie Satelit не ставить.
  • Коротке зберігання. Ключ клієнта (адреса або її мережа /64), User-Agent, правило та його доказ: 30 днів. Погодинні лічильники: 90 днів.
  • Секрет лишається на сервері. Ключ сайту публічний. Секрет ви бачите один раз, а Satelit зберігає лише його хеш.

Про Satelit Shield