كابتشا توقف البوتات،
لا البشر.
يتحقق VeilGuard من الزوّار عبر إشارات غير مرئية. لا أحاجيز صور ولا اتصالات بأطراف خارجية. كل شيء يعمل على خادمك أنت.
بلا تسجيل، بلا بطاقة ائتمانية. الإعداد يستغرق نحو دقيقتين.
أنشئ حسابك
جرّبه بنفسك
الأداة أدناه حقيقية: التحدّي وإثبات العمل وتحليل الإشارات والتحقق من جهة الخادم، كلها تعمل في هذه الصفحة.
يرسل رمزًا مزيّفًا يحمل بصمات الأتمتة.
تحقّق كإنسان أولًا، ثم أعد إرسال ذلك الرمز.
تم التحقق من أنك إنسان
هذا ما يراه زوّارك: علامة صح خضراء ولا شيء غيرها.
تم حجب البوت
استجابة الخادم الخام
- أطر الأتمتة (WebDriver وPuppeteer وPlaywright)
- المتصفحات بلا واجهة ومحرّكات العرض البرمجية
- User-Agent للبوتات والزواحف المعروفة
- إنتروبيا السلوك: إشارات الفأرة ولوحة المفاتيح واللمس
- إثبات عمل مرتبط بتحدٍّ أحادي الاستخدام
- تحليل التوقيت وإعادة التشغيل على خادمك
ما الذي يفعله VeilGuard
ستة أشياء يتقنها.
معدل حجب يقارب 100%
محرك متعدد الإشارات يجمع بصمات الأتمتة وإنتروبيا السلوك وإثبات العمل وتحليل التوقيت على الخادم.
يوقف بوتات AI أيضًا
يأخذ البصمات على Chrome بلا واجهة وأتمتة CDP والوكلاء المبرمَجة ويحجبها، وليس الزواحف الساذجة فقط.
تكامل في دقيقتين
مقتطف واحد للأداة، ونداء واحد على خادمك. انسخ والصق لـPHP وNode.js وPython والمواقع الثابتة.
يعمل 100% على خادمك
التحقق مجرد فحص HMAC-SHA256 محلي. حتى لو تعطل veilguard.online، ستبقى حمايتك تعمل.
غير مرئية بالتصميم
لا إشارات مرور ولا حنفيات حريق. يضغط البشر مرة واحدة ويرون «تم التحقق!».
الخصوصية أولًا ومتوافقة مع GDPR
لا ملفات تعريف ارتباط، ولا بكسلات تتبع، ولا طلبات لأطراف خارجية. الإشارات المؤقتة تعيش داخل الرمز فقط.
كيف تعمل
ثلاث خطوات، بلا حاجة إلى حساب.
-
1.
أنشئ مفاتيح مجانية
بلا تسجيل وبلا بريد إلكتروني. تحصل فورًا على مفتاح الموقع ومفتاح سرّي.
-
2.
الصق مقتطفين
سكربت الأداة في نموذجك، ونداء التحقق في خادمك الخلفي.
-
3.
انتهى
تُدقّق الرموز محليًا عبر HMAC-SHA256. البشر يمرون، والبوتات لا.
تكامل في دقيقتين
أنشئ مفاتيحك المجانية، واختر تقنيتك، والصق الكود. لا شيء يغادر خادمك.
مفاتيحك جاهزة
مفتاح الموقع (عام)
المفتاح السرّي (خاص)
تعامل مع سرّك كأنه كلمة مرور: فهو لا يترك خادمك أبدًا.
1 · الأداة: الصقها في نموذجك
/*! VeilGuard v1.0.0 - self-hosted bot protection - https://veilguard.online */
(function () {
'use strict';
var LINK = 'https://veilguard.online';
var STR = {
en: { nb: "I'm not a bot", vd: 'Verified!', ck: 'Verifying…', er: 'Verification failed — click to retry' },
de: { nb: 'Ich bin kein Bot', vd: 'Verifiziert!', ck: 'Prüfung…', er: 'Fehlgeschlagen — zum Wiederholen klicken' },
es: { nb: 'No soy un bot', vd: '¡Verificado!', ck: 'Verificando…', er: 'Error — haz clic para reintentar' },
fr: { nb: "Je ne suis pas un robot", vd: 'Vérifié !', ck: 'Vérification…', er: 'Échec — cliquez pour réessayer' },
pt: { nb: 'Não sou um bot', vd: 'Verificado!', ck: 'Verificando…', er: 'Falhou — clique para tentar de novo' },
ru: { nb: 'Я не робот', vd: 'Проверено!', ck: 'Проверка…', er: 'Ошибка — нажмите для повтора' },
zh: { nb: '我不是机器人', vd: '已验证!', ck: '验证中…', er: '验证失败 — 点击重试' },
ja: { nb: 'ロボットではありません', vd: '認証済み!', ck: '確認中…', er: '失敗 — クリックで再試行' },
ar: { nb: 'لست روبوتًا', vd: 'تم التحقق!', ck: 'جارٍ التحقق…', er: 'فشل — انقر للمحاولة مجددًا' }
};
var BOT_UA = /bot|crawl|spider|slurp|headless|phantom|selenium|puppeteer|playwright|webdriver|curl|wget|python|scrapy|axios|go-http|httpclient|okhttp|libwww|lighthouse|facebookexternalhit|telegram|whatsapp|discord/i;
function sha256(ascii) {
function rightRotate(value, amount) { return (value >>> amount) | (value << (32 - amount)); }
var mathPow = Math.pow, maxWord = mathPow(2, 32), result = '';
var words = [], asciiBitLength = ascii.length * 8;
var hash = sha256.h = sha256.h || [], k = sha256.k = sha256.k || [], primeCounter = k.length;
var isComposite = {};
for (var candidate = 2; primeCounter < 64; candidate++) {
if (!isComposite[candidate]) {
for (var i = 0; i < 313; i += candidate) { isComposite[i] = candidate; }
hash[primeCounter] = (mathPow(candidate, 0.5) * maxWord) | 0;
k[primeCounter++] = (mathPow(candidate, 1 / 3) * maxWord) | 0;
}
}
ascii += '\x80';
while (ascii.length % 64 - 56) ascii += '\x00';
for (i = 0; i < ascii.length; i++) {
var j = ascii.charCodeAt(i);
if (j >> 8) return '';
words[i >> 2] |= j << ((3 - i) % 4) * 8;
}
words[words.length] = ((asciiBitLength / maxWord) | 0);
words[words.length] = (asciiBitLength);
for (j = 0; j < words.length;) {
var w = words.slice(j, j += 16), oldHash = hash;
hash = hash.slice(0, 8);
for (i = 0; i < 64; i++) {
var w15 = w[i - 15], w2 = w[i - 2];
var a = hash[0], e = hash[4];
var temp1 = hash[7]
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25))
+ ((e & hash[5]) ^ ((~e) & hash[6]))
+ k[i]
+ (w[i] = (i < 16) ? w[i] : (
w[i - 16]
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15 >>> 3))
+ w[i - 7]
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2 >>> 10))
) | 0);
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22))
+ ((a & hash[1]) ^ (a & hash[2]) ^ (hash[1] & hash[2]));
hash = [(temp1 + temp2) | 0].concat(hash);
hash[4] = (hash[4] + temp1) | 0;
}
for (i = 0; i < 8; i++) { hash[i] = (hash[i] + oldHash[i]) | 0; }
}
for (i = 0; i < 8; i++) {
for (j = 3; j + 1; j--) {
var b = (hash[i] >> (j * 8)) & 255;
result += ((b < 16) ? 0 : '') + b.toString(16);
}
}
return result;
}
function b64u(s) {
try {
return btoa(unescape(encodeURIComponent(s)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
} catch (e) { return ''; }
}
function unb64u(s) {
try { return decodeURIComponent(escape(atob(s.replace(/-/g, '+').replace(/_/g, '/')))); }
catch (e) { return ''; }
}
function rand(n) {
var s = '', chars = '0123456789abcdef';
try {
var a = new Uint8Array(n);
(window.crypto || window.msCrypto).getRandomValues(a);
for (var i = 0; i < n; i++) s += chars[a[i] & 15];
} catch (e) {
for (var j = 0; j < n; j++) s += chars[Math.floor(Math.random() * 16)];
}
return s;
}
function resolveLang(lang) {
if (lang && lang !== 'auto' && STR[lang]) return lang;
var nav = ((navigator.languages && navigator.languages[0]) || navigator.language || 'en').slice(0, 2).toLowerCase();
return STR[nav] ? nav : 'en';
}
function resolveTheme(theme) {
if (theme === 'light' || theme === 'dark') return theme;
try { return (window.matchMedia && matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; }
catch (e) { return 'light'; }
}
function powBits(challenge) {
try {
var dot = challenge.lastIndexOf('.');
var p = JSON.parse(unb64u(challenge.slice(0, dot)));
var d = p && typeof p.d === 'number' ? p.d : 12;
if (d <= 0 || d > 24) d = 12;
return Math.round(d / 4) * 4;
} catch (e) { return 12; }
}
function solvePow(challenge, bits) {
var zeros = Math.max(1, Math.round(bits / 4));
var prefix = new Array(zeros + 1).join('0');
for (var p = 0; p < 2000000; p++) {
var h = sha256(challenge + '.' + p);
if (h && h.slice(0, zeros) === prefix) return { p: p, h: h };
}
return { p: p, h: '' };
}
var STYLE = '.vg-root{display:inline-block;width:100%;max-width:340px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;direction:ltr}'
+ '.vg-box{border:1px solid #d4d4d8;border-radius:10px;background:#fff;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.07);cursor:pointer;transition:border-color .2s,box-shadow .2s;-webkit-user-select:none;user-select:none}'
+ '.vg-box:focus-visible{outline:2px solid #10b981;outline-offset:2px}'
+ '.vg-dark{background:#18181b;border-color:#3f3f46}'
+ '.vg-main{display:flex;align-items:center;gap:11px;padding:15px 16px;min-height:52px}'
+ '.vg-cb{width:24px;height:24px;border:2px solid #a1a1aa;border-radius:5px;background:#fff;flex:none;display:flex;align-items:center;justify-content:center;position:relative;transition:border-color .15s,background .15s}'
+ '.vg-dark .vg-cb{background:transparent;border-color:#52525b}'
+ '.vg-box:hover .vg-cb{border-color:#10b981}'
+ '.vg-cb svg{width:15px;height:15px;opacity:0;transform:scale(.3);transition:opacity .18s ease,transform .18s ease}'
+ '.vg-ok .vg-cb{background:#10b981;border-color:#10b981}'
+ '.vg-ok .vg-cb svg{opacity:1;transform:scale(1)}'
+ '.vg-label{font-size:14px;color:#3f3f46;font-weight:500;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}'
+ '.vg-dark .vg-label{color:#e4e4e7}'
+ '.vg-ok .vg-label{color:#059669;font-weight:600}'
+ '.vg-err .vg-label{color:#dc2626;font-weight:500}'
+ '.vg-brand{display:flex;align-items:center;gap:5px;color:#71717a;font-size:11.5px;font-weight:700;letter-spacing:.01em;flex:none}'
+ '.vg-brand svg{width:18px;height:18px;display:block}'
+ '.vg-foot{display:flex;justify-content:flex-end;align-items:center;gap:5px;padding:4px 12px 6px;border-top:1px solid #f4f4f5;background:#fafafa}'
+ '.vg-dark .vg-foot{border-top-color:#27272a;background:#131316}'
+ '.vg-foot svg{width:10px;height:10px;display:block;color:#a1a1aa}'
+ '.vg-foot a{font-size:10.5px;color:#a1a1aa;text-decoration:none;font-weight:500}'
+ '.vg-foot a:hover{color:#10b981;text-decoration:underline}'
+ '.vg-spinner{position:absolute;inset:2px;border-radius:50%;border:2px solid #e4e4e7;border-top-color:#10b981;opacity:0;transition:opacity .15s;animation:vg-spin .8s linear infinite}'
+ '.vg-dark .vg-spinner{border-color:#3f3f46}'
+ '.vg-checking .vg-spinner{opacity:1}'
+ '.vg-ok{border-color:#10b981 !important;box-shadow:0 0 0 3px rgba(16,185,129,.14)}'
+ '.vg-err{border-color:#f87171 !important}'
+ '@keyframes vg-spin{to{transform:rotate(360deg)}}'
+ '@media (prefers-reduced-motion:reduce){.vg-spinner{animation-duration:2s}}';
var LOGO = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" fill="#10b981" fill-opacity="0.14" stroke="#10b981" stroke-width="1.5" stroke-linejoin="round"/><path d="m8.6 12 2.4 2.4 4.4-4.8" stroke="#10b981" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var CHECK = '<svg viewBox="0 0 24 24" fill="none"><path d="m5 13 4.2 4.2L19 7" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var MINI = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>';
function ensureStyle() {
if (document.getElementById('vg-style')) return;
var st = document.createElement('style');
st.id = 'vg-style';
st.textContent = STYLE;
document.head.appendChild(st);
}
function render(target, opts) {
opts = opts || {};
var el = typeof target === 'string' ? document.querySelector(target) : target;
if (!el || !el.appendChild) return null;
ensureStyle();
var lang = resolveLang(opts.lang);
var t = STR[lang];
var theme = resolveTheme(opts.theme);
var sitekey = opts.sitekey || '';
var inputName = opts.inputName || 'veilguard-token';
var mode = opts.mode === 'static' ? 'static' : 'server';
var challengeUrl = opts.challengeUrl || ('/veilguard-challenge?sitekey=' + encodeURIComponent(sitekey));
var st = { mm: 0, ds: 0, kd: 0, td: 0, pd: 0, scc: 0, load: Date.now(), token: '', done: false, busy: false };
var lastXY = null;
function track(evt, fn) { document.addEventListener(evt, fn, { passive: true, capture: true }); }
track('mousemove', function (e) {
st.mm++;
if (lastXY) { var dx = e.clientX - lastXY[0], dy = e.clientY - lastXY[1]; st.ds += Math.sqrt(dx * dx + dy * dy); }
lastXY = [e.clientX, e.clientY];
});
track('keydown', function () { st.kd++; });
track('touchstart', function () { st.td++; });
track('pointerdown', function () { st.pd++; });
track('scroll', function () { st.scc++; });
el.innerHTML = '';
el.className += ' vg-root';
var box = document.createElement('div');
box.className = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
box.setAttribute('role', 'checkbox');
box.setAttribute('aria-checked', 'false');
box.setAttribute('aria-label', t.nb);
box.tabIndex = 0;
box.innerHTML = '<div class="vg-main">'
+ '<div class="vg-cb"><span class="vg-spinner"></span>' + CHECK + '</div>'
+ '<span class="vg-label" aria-live="polite">' + t.nb + '</span>'
+ '<div class="vg-brand">' + LOGO + '<span>VeilGuard</span></div>'
+ '</div>'
+ '<div class="vg-foot">' + MINI + '<a href="' + LINK + '" target="_blank" rel="noopener noreferrer">veilguard.online</a></div>';
var input = document.createElement('input');
input.type = 'hidden';
input.name = inputName;
el.appendChild(box);
el.appendChild(input);
var label = box.querySelector('.vg-label');
function setState(s) {
var cls = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
if (s === 'checking') cls += ' vg-checking';
if (s === 'ok') cls += ' vg-ok';
if (s === 'err') cls += ' vg-err';
box.className = cls;
box.setAttribute('aria-checked', s === 'ok' ? 'true' : 'false');
label.textContent = s === 'checking' ? t.ck : s === 'ok' ? t.vd : s === 'err' ? t.er : t.nb;
}
function detect() {
var flags = 0;
try { if (navigator.webdriver === true || !!window.webdriver) flags |= 1; } catch (e) {}
if (BOT_UA.test(navigator.userAgent)) flags |= 2;
try {
if (window._phantom || window.callPhantom || window.__nightmare || window.domAutomation || window.__selenium_unwrapped) flags |= 4;
} catch (e) {}
try { if ((window.outerWidth === 0 || window.outerHeight === 0) && window.innerWidth > 0) flags |= 8; } catch (e) {}
try {
var c = document.createElement('canvas');
var gl = c.getContext && (c.getContext('webgl') || c.getContext('experimental-webgl'));
if (gl) {
var dbg = gl.getExtension('WEBGL_debug_renderer_info');
var r = dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : '';
if (typeof r === 'string' && /swiftshader|llvmpipe|software|basic render/i.test(r)) flags |= 16;
}
} catch (e) {}
try { if (!navigator.languages || !navigator.languages.length) flags |= 32; } catch (e) {}
var isChromeUA = navigator.userAgent.indexOf('Chrome/') !== -1 && !/Edg|OPR|SamsungBrowser/i.test(navigator.userAgent);
if (isChromeUA && !window.chrome) flags |= 64;
if (isChromeUA && (!navigator.plugins || navigator.plugins.length === 0)) flags |= 128;
var score = 100;
if (flags & 31) { score = 0; }
else {
if (flags & 32) score -= 20;
if (flags & 64) score -= 15;
if (flags & 128) score -= 10;
var dt = Date.now() - st.load;
if (dt < 350) score -= 30;
if (st.mm === 0 && st.td === 0 && st.kd === 0 && st.pd <= 1) score -= 25;
else if (st.mm > 0 && st.ds < 40) score -= 10;
if (score < 0) score = 0;
if (score > 100) score = 100;
}
return { flags: flags, score: score };
}
function staticChallenge() {
var now = Date.now();
return b64u(JSON.stringify({ n: rand(16), i: now, e: now + 300000, k: sitekey, d: 12 }));
}
function activate() {
if (st.done || st.busy) return;
st.busy = true;
setState('checking');
var started = Date.now();
var finish = function (challenge) {
if (!challenge) { setState('err'); st.busy = false; return; }
try {
var det = detect();
var pow = solvePow(challenge, powBits(challenge));
var att = {
v: 1, ch: challenge, ts: Date.now(), sc: det.score, hl: det.flags,
lg: lang, ua: navigator.userAgent.slice(0, 180),
ev: { mm: st.mm, ds: Math.round(st.ds), kd: st.kd, td: st.td, pd: st.pd, sc: st.scc, dt: started - st.load },
pw: pow
};
st.token = 'v1.' + b64u(JSON.stringify(att));
input.value = st.token;
} catch (e) { st.token = ''; }
var wait = Math.max(0, 550 + Math.random() * 350 - (Date.now() - started));
setTimeout(function () {
st.busy = false;
if (st.token) {
st.done = true;
setState('ok');
if (typeof opts.callback === 'function') { try { opts.callback(st.token); } catch (e2) {} }
} else { setState('err'); }
}, wait);
};
if (mode === 'static') { finish(staticChallenge()); return; }
try {
fetch(challengeUrl, { credentials: 'omit' })
.then(function (r) { if (!r.ok) throw new Error('http ' + r.status); return r.json(); })
.then(function (d) { finish(d && d.challenge ? String(d.challenge) : ''); })
.catch(function () { setState('err'); st.busy = false; });
} catch (e) { setState('err'); st.busy = false; }
}
box.addEventListener('click', activate);
box.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); activate(); }
});
return {
reset: function () { st.done = false; st.busy = false; st.token = ''; input.value = ''; setState('idle'); },
getResponse: function () { return st.token; },
getSiteKey: function () { return sitekey; }
};
}
function auto() {
var nodes = document.querySelectorAll('.veilguard[data-sitekey]');
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (n.getAttribute('data-vg-init') === '1') continue;
n.setAttribute('data-vg-init', '1');
render(n, {
sitekey: n.getAttribute('data-sitekey') || '',
lang: n.getAttribute('data-lang') || 'auto',
theme: n.getAttribute('data-theme') || 'auto',
mode: n.getAttribute('data-mode') || 'server',
challengeUrl: n.getAttribute('data-challenge-url') || undefined,
inputName: n.getAttribute('data-input-name') || 'veilguard-token'
});
}
}
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', auto); }
else { auto(); }
window.VeilGuard = { render: render, auto: auto, version: '1.0.0' };
})();
<!-- VeilGuard widget — place inside your <form> -->
<div class="veilguard" data-sitekey="vg_YOUR_SITE_KEY" data-mode="static"></div>
<!-- Load the widget once per page (before </body>) -->
<script src="veilguard-widget.js"></script>
المواقع الثابتة بلا خادم خلفي، لذا يجري التحقق داخل متصفح الزائر. هذا يحجب الزواحف الساذجة ومعظم البوتات؛ واربطه بأي خادم خلفي للتحقق التشفيري.
1 · الأداة: الصقها في نموذجك
/*! VeilGuard v1.0.0 - self-hosted bot protection - https://veilguard.online */
(function () {
'use strict';
var LINK = 'https://veilguard.online';
var STR = {
en: { nb: "I'm not a bot", vd: 'Verified!', ck: 'Verifying…', er: 'Verification failed — click to retry' },
de: { nb: 'Ich bin kein Bot', vd: 'Verifiziert!', ck: 'Prüfung…', er: 'Fehlgeschlagen — zum Wiederholen klicken' },
es: { nb: 'No soy un bot', vd: '¡Verificado!', ck: 'Verificando…', er: 'Error — haz clic para reintentar' },
fr: { nb: "Je ne suis pas un robot", vd: 'Vérifié !', ck: 'Vérification…', er: 'Échec — cliquez pour réessayer' },
pt: { nb: 'Não sou um bot', vd: 'Verificado!', ck: 'Verificando…', er: 'Falhou — clique para tentar de novo' },
ru: { nb: 'Я не робот', vd: 'Проверено!', ck: 'Проверка…', er: 'Ошибка — нажмите для повтора' },
zh: { nb: '我不是机器人', vd: '已验证!', ck: '验证中…', er: '验证失败 — 点击重试' },
ja: { nb: 'ロボットではありません', vd: '認証済み!', ck: '確認中…', er: '失敗 — クリックで再試行' },
ar: { nb: 'لست روبوتًا', vd: 'تم التحقق!', ck: 'جارٍ التحقق…', er: 'فشل — انقر للمحاولة مجددًا' }
};
var BOT_UA = /bot|crawl|spider|slurp|headless|phantom|selenium|puppeteer|playwright|webdriver|curl|wget|python|scrapy|axios|go-http|httpclient|okhttp|libwww|lighthouse|facebookexternalhit|telegram|whatsapp|discord/i;
function sha256(ascii) {
function rightRotate(value, amount) { return (value >>> amount) | (value << (32 - amount)); }
var mathPow = Math.pow, maxWord = mathPow(2, 32), result = '';
var words = [], asciiBitLength = ascii.length * 8;
var hash = sha256.h = sha256.h || [], k = sha256.k = sha256.k || [], primeCounter = k.length;
var isComposite = {};
for (var candidate = 2; primeCounter < 64; candidate++) {
if (!isComposite[candidate]) {
for (var i = 0; i < 313; i += candidate) { isComposite[i] = candidate; }
hash[primeCounter] = (mathPow(candidate, 0.5) * maxWord) | 0;
k[primeCounter++] = (mathPow(candidate, 1 / 3) * maxWord) | 0;
}
}
ascii += '\x80';
while (ascii.length % 64 - 56) ascii += '\x00';
for (i = 0; i < ascii.length; i++) {
var j = ascii.charCodeAt(i);
if (j >> 8) return '';
words[i >> 2] |= j << ((3 - i) % 4) * 8;
}
words[words.length] = ((asciiBitLength / maxWord) | 0);
words[words.length] = (asciiBitLength);
for (j = 0; j < words.length;) {
var w = words.slice(j, j += 16), oldHash = hash;
hash = hash.slice(0, 8);
for (i = 0; i < 64; i++) {
var w15 = w[i - 15], w2 = w[i - 2];
var a = hash[0], e = hash[4];
var temp1 = hash[7]
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25))
+ ((e & hash[5]) ^ ((~e) & hash[6]))
+ k[i]
+ (w[i] = (i < 16) ? w[i] : (
w[i - 16]
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15 >>> 3))
+ w[i - 7]
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2 >>> 10))
) | 0);
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22))
+ ((a & hash[1]) ^ (a & hash[2]) ^ (hash[1] & hash[2]));
hash = [(temp1 + temp2) | 0].concat(hash);
hash[4] = (hash[4] + temp1) | 0;
}
for (i = 0; i < 8; i++) { hash[i] = (hash[i] + oldHash[i]) | 0; }
}
for (i = 0; i < 8; i++) {
for (j = 3; j + 1; j--) {
var b = (hash[i] >> (j * 8)) & 255;
result += ((b < 16) ? 0 : '') + b.toString(16);
}
}
return result;
}
function b64u(s) {
try {
return btoa(unescape(encodeURIComponent(s)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
} catch (e) { return ''; }
}
function unb64u(s) {
try { return decodeURIComponent(escape(atob(s.replace(/-/g, '+').replace(/_/g, '/')))); }
catch (e) { return ''; }
}
function rand(n) {
var s = '', chars = '0123456789abcdef';
try {
var a = new Uint8Array(n);
(window.crypto || window.msCrypto).getRandomValues(a);
for (var i = 0; i < n; i++) s += chars[a[i] & 15];
} catch (e) {
for (var j = 0; j < n; j++) s += chars[Math.floor(Math.random() * 16)];
}
return s;
}
function resolveLang(lang) {
if (lang && lang !== 'auto' && STR[lang]) return lang;
var nav = ((navigator.languages && navigator.languages[0]) || navigator.language || 'en').slice(0, 2).toLowerCase();
return STR[nav] ? nav : 'en';
}
function resolveTheme(theme) {
if (theme === 'light' || theme === 'dark') return theme;
try { return (window.matchMedia && matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; }
catch (e) { return 'light'; }
}
function powBits(challenge) {
try {
var dot = challenge.lastIndexOf('.');
var p = JSON.parse(unb64u(challenge.slice(0, dot)));
var d = p && typeof p.d === 'number' ? p.d : 12;
if (d <= 0 || d > 24) d = 12;
return Math.round(d / 4) * 4;
} catch (e) { return 12; }
}
function solvePow(challenge, bits) {
var zeros = Math.max(1, Math.round(bits / 4));
var prefix = new Array(zeros + 1).join('0');
for (var p = 0; p < 2000000; p++) {
var h = sha256(challenge + '.' + p);
if (h && h.slice(0, zeros) === prefix) return { p: p, h: h };
}
return { p: p, h: '' };
}
var STYLE = '.vg-root{display:inline-block;width:100%;max-width:340px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;direction:ltr}'
+ '.vg-box{border:1px solid #d4d4d8;border-radius:10px;background:#fff;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.07);cursor:pointer;transition:border-color .2s,box-shadow .2s;-webkit-user-select:none;user-select:none}'
+ '.vg-box:focus-visible{outline:2px solid #10b981;outline-offset:2px}'
+ '.vg-dark{background:#18181b;border-color:#3f3f46}'
+ '.vg-main{display:flex;align-items:center;gap:11px;padding:15px 16px;min-height:52px}'
+ '.vg-cb{width:24px;height:24px;border:2px solid #a1a1aa;border-radius:5px;background:#fff;flex:none;display:flex;align-items:center;justify-content:center;position:relative;transition:border-color .15s,background .15s}'
+ '.vg-dark .vg-cb{background:transparent;border-color:#52525b}'
+ '.vg-box:hover .vg-cb{border-color:#10b981}'
+ '.vg-cb svg{width:15px;height:15px;opacity:0;transform:scale(.3);transition:opacity .18s ease,transform .18s ease}'
+ '.vg-ok .vg-cb{background:#10b981;border-color:#10b981}'
+ '.vg-ok .vg-cb svg{opacity:1;transform:scale(1)}'
+ '.vg-label{font-size:14px;color:#3f3f46;font-weight:500;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}'
+ '.vg-dark .vg-label{color:#e4e4e7}'
+ '.vg-ok .vg-label{color:#059669;font-weight:600}'
+ '.vg-err .vg-label{color:#dc2626;font-weight:500}'
+ '.vg-brand{display:flex;align-items:center;gap:5px;color:#71717a;font-size:11.5px;font-weight:700;letter-spacing:.01em;flex:none}'
+ '.vg-brand svg{width:18px;height:18px;display:block}'
+ '.vg-foot{display:flex;justify-content:flex-end;align-items:center;gap:5px;padding:4px 12px 6px;border-top:1px solid #f4f4f5;background:#fafafa}'
+ '.vg-dark .vg-foot{border-top-color:#27272a;background:#131316}'
+ '.vg-foot svg{width:10px;height:10px;display:block;color:#a1a1aa}'
+ '.vg-foot a{font-size:10.5px;color:#a1a1aa;text-decoration:none;font-weight:500}'
+ '.vg-foot a:hover{color:#10b981;text-decoration:underline}'
+ '.vg-spinner{position:absolute;inset:2px;border-radius:50%;border:2px solid #e4e4e7;border-top-color:#10b981;opacity:0;transition:opacity .15s;animation:vg-spin .8s linear infinite}'
+ '.vg-dark .vg-spinner{border-color:#3f3f46}'
+ '.vg-checking .vg-spinner{opacity:1}'
+ '.vg-ok{border-color:#10b981 !important;box-shadow:0 0 0 3px rgba(16,185,129,.14)}'
+ '.vg-err{border-color:#f87171 !important}'
+ '@keyframes vg-spin{to{transform:rotate(360deg)}}'
+ '@media (prefers-reduced-motion:reduce){.vg-spinner{animation-duration:2s}}';
var LOGO = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" fill="#10b981" fill-opacity="0.14" stroke="#10b981" stroke-width="1.5" stroke-linejoin="round"/><path d="m8.6 12 2.4 2.4 4.4-4.8" stroke="#10b981" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var CHECK = '<svg viewBox="0 0 24 24" fill="none"><path d="m5 13 4.2 4.2L19 7" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var MINI = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>';
function ensureStyle() {
if (document.getElementById('vg-style')) return;
var st = document.createElement('style');
st.id = 'vg-style';
st.textContent = STYLE;
document.head.appendChild(st);
}
function render(target, opts) {
opts = opts || {};
var el = typeof target === 'string' ? document.querySelector(target) : target;
if (!el || !el.appendChild) return null;
ensureStyle();
var lang = resolveLang(opts.lang);
var t = STR[lang];
var theme = resolveTheme(opts.theme);
var sitekey = opts.sitekey || '';
var inputName = opts.inputName || 'veilguard-token';
var mode = opts.mode === 'static' ? 'static' : 'server';
var challengeUrl = opts.challengeUrl || ('/veilguard-challenge?sitekey=' + encodeURIComponent(sitekey));
var st = { mm: 0, ds: 0, kd: 0, td: 0, pd: 0, scc: 0, load: Date.now(), token: '', done: false, busy: false };
var lastXY = null;
function track(evt, fn) { document.addEventListener(evt, fn, { passive: true, capture: true }); }
track('mousemove', function (e) {
st.mm++;
if (lastXY) { var dx = e.clientX - lastXY[0], dy = e.clientY - lastXY[1]; st.ds += Math.sqrt(dx * dx + dy * dy); }
lastXY = [e.clientX, e.clientY];
});
track('keydown', function () { st.kd++; });
track('touchstart', function () { st.td++; });
track('pointerdown', function () { st.pd++; });
track('scroll', function () { st.scc++; });
el.innerHTML = '';
el.className += ' vg-root';
var box = document.createElement('div');
box.className = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
box.setAttribute('role', 'checkbox');
box.setAttribute('aria-checked', 'false');
box.setAttribute('aria-label', t.nb);
box.tabIndex = 0;
box.innerHTML = '<div class="vg-main">'
+ '<div class="vg-cb"><span class="vg-spinner"></span>' + CHECK + '</div>'
+ '<span class="vg-label" aria-live="polite">' + t.nb + '</span>'
+ '<div class="vg-brand">' + LOGO + '<span>VeilGuard</span></div>'
+ '</div>'
+ '<div class="vg-foot">' + MINI + '<a href="' + LINK + '" target="_blank" rel="noopener noreferrer">veilguard.online</a></div>';
var input = document.createElement('input');
input.type = 'hidden';
input.name = inputName;
el.appendChild(box);
el.appendChild(input);
var label = box.querySelector('.vg-label');
function setState(s) {
var cls = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
if (s === 'checking') cls += ' vg-checking';
if (s === 'ok') cls += ' vg-ok';
if (s === 'err') cls += ' vg-err';
box.className = cls;
box.setAttribute('aria-checked', s === 'ok' ? 'true' : 'false');
label.textContent = s === 'checking' ? t.ck : s === 'ok' ? t.vd : s === 'err' ? t.er : t.nb;
}
function detect() {
var flags = 0;
try { if (navigator.webdriver === true || !!window.webdriver) flags |= 1; } catch (e) {}
if (BOT_UA.test(navigator.userAgent)) flags |= 2;
try {
if (window._phantom || window.callPhantom || window.__nightmare || window.domAutomation || window.__selenium_unwrapped) flags |= 4;
} catch (e) {}
try { if ((window.outerWidth === 0 || window.outerHeight === 0) && window.innerWidth > 0) flags |= 8; } catch (e) {}
try {
var c = document.createElement('canvas');
var gl = c.getContext && (c.getContext('webgl') || c.getContext('experimental-webgl'));
if (gl) {
var dbg = gl.getExtension('WEBGL_debug_renderer_info');
var r = dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : '';
if (typeof r === 'string' && /swiftshader|llvmpipe|software|basic render/i.test(r)) flags |= 16;
}
} catch (e) {}
try { if (!navigator.languages || !navigator.languages.length) flags |= 32; } catch (e) {}
var isChromeUA = navigator.userAgent.indexOf('Chrome/') !== -1 && !/Edg|OPR|SamsungBrowser/i.test(navigator.userAgent);
if (isChromeUA && !window.chrome) flags |= 64;
if (isChromeUA && (!navigator.plugins || navigator.plugins.length === 0)) flags |= 128;
var score = 100;
if (flags & 31) { score = 0; }
else {
if (flags & 32) score -= 20;
if (flags & 64) score -= 15;
if (flags & 128) score -= 10;
var dt = Date.now() - st.load;
if (dt < 350) score -= 30;
if (st.mm === 0 && st.td === 0 && st.kd === 0 && st.pd <= 1) score -= 25;
else if (st.mm > 0 && st.ds < 40) score -= 10;
if (score < 0) score = 0;
if (score > 100) score = 100;
}
return { flags: flags, score: score };
}
function staticChallenge() {
var now = Date.now();
return b64u(JSON.stringify({ n: rand(16), i: now, e: now + 300000, k: sitekey, d: 12 }));
}
function activate() {
if (st.done || st.busy) return;
st.busy = true;
setState('checking');
var started = Date.now();
var finish = function (challenge) {
if (!challenge) { setState('err'); st.busy = false; return; }
try {
var det = detect();
var pow = solvePow(challenge, powBits(challenge));
var att = {
v: 1, ch: challenge, ts: Date.now(), sc: det.score, hl: det.flags,
lg: lang, ua: navigator.userAgent.slice(0, 180),
ev: { mm: st.mm, ds: Math.round(st.ds), kd: st.kd, td: st.td, pd: st.pd, sc: st.scc, dt: started - st.load },
pw: pow
};
st.token = 'v1.' + b64u(JSON.stringify(att));
input.value = st.token;
} catch (e) { st.token = ''; }
var wait = Math.max(0, 550 + Math.random() * 350 - (Date.now() - started));
setTimeout(function () {
st.busy = false;
if (st.token) {
st.done = true;
setState('ok');
if (typeof opts.callback === 'function') { try { opts.callback(st.token); } catch (e2) {} }
} else { setState('err'); }
}, wait);
};
if (mode === 'static') { finish(staticChallenge()); return; }
try {
fetch(challengeUrl, { credentials: 'omit' })
.then(function (r) { if (!r.ok) throw new Error('http ' + r.status); return r.json(); })
.then(function (d) { finish(d && d.challenge ? String(d.challenge) : ''); })
.catch(function () { setState('err'); st.busy = false; });
} catch (e) { setState('err'); st.busy = false; }
}
box.addEventListener('click', activate);
box.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); activate(); }
});
return {
reset: function () { st.done = false; st.busy = false; st.token = ''; input.value = ''; setState('idle'); },
getResponse: function () { return st.token; },
getSiteKey: function () { return sitekey; }
};
}
function auto() {
var nodes = document.querySelectorAll('.veilguard[data-sitekey]');
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (n.getAttribute('data-vg-init') === '1') continue;
n.setAttribute('data-vg-init', '1');
render(n, {
sitekey: n.getAttribute('data-sitekey') || '',
lang: n.getAttribute('data-lang') || 'auto',
theme: n.getAttribute('data-theme') || 'auto',
mode: n.getAttribute('data-mode') || 'server',
challengeUrl: n.getAttribute('data-challenge-url') || undefined,
inputName: n.getAttribute('data-input-name') || 'veilguard-token'
});
}
}
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', auto); }
else { auto(); }
window.VeilGuard = { render: render, auto: auto, version: '1.0.0' };
})();
<!-- VeilGuard widget — place inside your <form> -->
<div class="veilguard" data-sitekey="vg_YOUR_SITE_KEY" data-challenge-url="veilguard-challenge.php"></div>
<!-- Load the widget once per page (before </body>) -->
<script src="veilguard-widget.js"></script>
2 · التحقق: يعمل على خادمك
<?php
/**
* VeilGuard v1.0.0 — self-hosted verification library for PHP
* Docs: https://veilguard.online
*/
define('VG_MIN_SCORE', 55);
define('VG_TTL', 300); // seconds — challenge lifetime
define('VG_GRACE', 60); // seconds — grace after expiry
define('VG_MIN_SOLVE_MS', 250);
function vg_b64u_decode(string $s): string {
return base64_decode(strtr($s, '-_', '+/'));
}
/** Issue a signed single-use challenge (used by your challenge endpoint). */
function vg_issue_challenge(string $site_key, string $secret): string {
$now = (int) (microtime(true) * 1000);
$payload = json_encode(array(
'n' => bin2hex(random_bytes(8)),
'i' => $now,
'e' => $now + VG_TTL * 1000,
'k' => $site_key,
'd' => 12,
));
$body = rtrim(strtr(base64_encode($payload), '+/', '-_'), '=');
return $body . '.' . hash_hmac('sha256', $body, $secret);
}
/**
* Verify a token submitted by the visitor.
* Returns array('success' => bool, 'score' => int, 'reason' => string)
*/
function vg_verify_token(string $token, string $secret, string $site_key, int $min_score = VG_MIN_SCORE): array {
$fail = function (string $reason): array { return array('success' => false, 'reason' => $reason); };
if (strlen($token) > 4096 || strpos($token, 'v1.') !== 0) {
return $fail('Malformed token');
}
$json = json_decode(vg_b64u_decode(substr($token, 3)), true);
if (!is_array($json) || !isset($json['ch'])) {
return $fail('Malformed payload');
}
// 1. Cryptographic challenge validation (HMAC-SHA256)
$ch = $json['ch'];
$dot = strrpos($ch, '.');
if ($dot === false) { return $fail('Malformed challenge'); }
$body = substr($ch, 0, $dot);
$sig = substr($ch, $dot + 1);
if (!hash_equals(hash_hmac('sha256', $body, $secret), $sig)) {
return $fail('Invalid challenge signature');
}
$p = json_decode(vg_b64u_decode($body), true);
if (!is_array($p)) { return $fail('Malformed challenge payload'); }
if (($p['k'] ?? '') !== $site_key) { return $fail('Site key mismatch'); }
// 2. Timing window — server clock only
$now = (int) (microtime(true) * 1000);
if ($now > $p['e'] + VG_GRACE * 1000) { return $fail('Challenge expired'); }
if ($now < $p['i']) { return $fail('Challenge issued in the future'); }
if ($now - $p['i'] < VG_MIN_SOLVE_MS) { return $fail('Solved impossibly fast'); }
// 3. Proof-of-work bound to this challenge
$pow = $json['pw'] ?? null;
if (!is_array($pow) || !isset($pow['p'])) { return $fail('Missing proof of work'); }
$hash = hash('sha256', $ch . '.' . $pow['p']);
$zeros = (int) ceil($p['d'] / 4);
if (substr($hash, 0, $zeros) !== str_repeat('0', $zeros)) {
return $fail('Invalid proof of work');
}
// 4. Automation fingerprints reported by the widget (bitmask)
$hl = (int) ($json['hl'] ?? 255);
if ($hl & 1) { return $fail('Automation framework detected'); }
if ($hl & 2) { return $fail('Known bot user agent'); }
if ($hl & 4) { return $fail('Automation framework detected'); }
if ($hl & 8) { return $fail('Headless browser detected'); }
if ($hl & 16) { return $fail('Software renderer detected'); }
// 5. Behavioral score threshold
$score = (int) ($json['sc'] ?? 0);
if ($score < $min_score) { return $fail('Score below threshold'); }
// 6. Single-use nonce — replay protection
if (!vg_nonce_fresh($p['n'], $p['e'])) { return $fail('Token replay detected'); }
return array('success' => true, 'score' => $score);
}
/** File-based single-use nonce store (shared-hosting friendly). */
function vg_nonce_fresh(string $nonce, int $expires_ms): bool {
$file = sys_get_temp_dir() . '/veilguard-nonces.json';
$used = array();
if (is_file($file)) {
$used = json_decode((string) @file_get_contents($file), true) ?: array();
}
$now = (int) (microtime(true) * 1000);
foreach ($used as $k => $exp) {
if ((int) $exp < $now) { unset($used[$k]); }
}
if (isset($used[$nonce])) { return false; }
$used[$nonce] = $expires_ms + 60000;
@file_put_contents($file, json_encode($used), LOCK_EX);
return true;
}
<?php
/**
* VeilGuard challenge endpoint — serves single-use challenges to the widget.
* File: veilguard-challenge.php
*/
require __DIR__ . '/veilguard.php';
$siteKey = 'vg_YOUR_SITE_KEY'; // public
$secret = 'vgs_YOUR_SECRET_KEY'; // private — never expose in HTML
header('Content-Type: application/json');
header('Cache-Control: no-store');
echo json_encode(array('challenge' => vg_issue_challenge($siteKey, $secret)));
<?php
/**
* VeilGuard example — a bot-proof signup form.
* File: signup.php
*/
require __DIR__ . '/veilguard.php';
$siteKey = 'vg_YOUR_SITE_KEY';
$secret = 'vgs_YOUR_SECRET_KEY';
$error = null;
$success = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['veilguard-token'] ?? '';
$result = vg_verify_token($token, $secret, $siteKey);
if (!$result['success']) {
http_response_code(403);
$error = 'Bot detected: ' . $result['reason'];
} else {
// Human confirmed — continue with your logic here.
$success = 'Welcome, human! Trust score: ' . $result['score'];
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Signup — protected by VeilGuard</title>
</head>
<body>
<?php if ($success): ?><p style="color:green"><?= htmlspecialchars($success) ?></p><?php endif; ?>
<?php if ($error): ?><p style="color:red"><?= htmlspecialchars($error) ?></p><?php endif; ?>
<form method="post">
<input type="email" name="email" placeholder="Email" required>
<!-- VeilGuard widget -->
<div class="veilguard" data-sitekey="<?= htmlspecialchars($siteKey) ?>"
data-challenge-url="veilguard-challenge.php"></div>
<button type="submit">Sign up</button>
</form>
<script src="veilguard-widget.js"></script>
</body>
</html>
1 · الأداة: الصقها في نموذجك
/*! VeilGuard v1.0.0 - self-hosted bot protection - https://veilguard.online */
(function () {
'use strict';
var LINK = 'https://veilguard.online';
var STR = {
en: { nb: "I'm not a bot", vd: 'Verified!', ck: 'Verifying…', er: 'Verification failed — click to retry' },
de: { nb: 'Ich bin kein Bot', vd: 'Verifiziert!', ck: 'Prüfung…', er: 'Fehlgeschlagen — zum Wiederholen klicken' },
es: { nb: 'No soy un bot', vd: '¡Verificado!', ck: 'Verificando…', er: 'Error — haz clic para reintentar' },
fr: { nb: "Je ne suis pas un robot", vd: 'Vérifié !', ck: 'Vérification…', er: 'Échec — cliquez pour réessayer' },
pt: { nb: 'Não sou um bot', vd: 'Verificado!', ck: 'Verificando…', er: 'Falhou — clique para tentar de novo' },
ru: { nb: 'Я не робот', vd: 'Проверено!', ck: 'Проверка…', er: 'Ошибка — нажмите для повтора' },
zh: { nb: '我不是机器人', vd: '已验证!', ck: '验证中…', er: '验证失败 — 点击重试' },
ja: { nb: 'ロボットではありません', vd: '認証済み!', ck: '確認中…', er: '失敗 — クリックで再試行' },
ar: { nb: 'لست روبوتًا', vd: 'تم التحقق!', ck: 'جارٍ التحقق…', er: 'فشل — انقر للمحاولة مجددًا' }
};
var BOT_UA = /bot|crawl|spider|slurp|headless|phantom|selenium|puppeteer|playwright|webdriver|curl|wget|python|scrapy|axios|go-http|httpclient|okhttp|libwww|lighthouse|facebookexternalhit|telegram|whatsapp|discord/i;
function sha256(ascii) {
function rightRotate(value, amount) { return (value >>> amount) | (value << (32 - amount)); }
var mathPow = Math.pow, maxWord = mathPow(2, 32), result = '';
var words = [], asciiBitLength = ascii.length * 8;
var hash = sha256.h = sha256.h || [], k = sha256.k = sha256.k || [], primeCounter = k.length;
var isComposite = {};
for (var candidate = 2; primeCounter < 64; candidate++) {
if (!isComposite[candidate]) {
for (var i = 0; i < 313; i += candidate) { isComposite[i] = candidate; }
hash[primeCounter] = (mathPow(candidate, 0.5) * maxWord) | 0;
k[primeCounter++] = (mathPow(candidate, 1 / 3) * maxWord) | 0;
}
}
ascii += '\x80';
while (ascii.length % 64 - 56) ascii += '\x00';
for (i = 0; i < ascii.length; i++) {
var j = ascii.charCodeAt(i);
if (j >> 8) return '';
words[i >> 2] |= j << ((3 - i) % 4) * 8;
}
words[words.length] = ((asciiBitLength / maxWord) | 0);
words[words.length] = (asciiBitLength);
for (j = 0; j < words.length;) {
var w = words.slice(j, j += 16), oldHash = hash;
hash = hash.slice(0, 8);
for (i = 0; i < 64; i++) {
var w15 = w[i - 15], w2 = w[i - 2];
var a = hash[0], e = hash[4];
var temp1 = hash[7]
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25))
+ ((e & hash[5]) ^ ((~e) & hash[6]))
+ k[i]
+ (w[i] = (i < 16) ? w[i] : (
w[i - 16]
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15 >>> 3))
+ w[i - 7]
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2 >>> 10))
) | 0);
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22))
+ ((a & hash[1]) ^ (a & hash[2]) ^ (hash[1] & hash[2]));
hash = [(temp1 + temp2) | 0].concat(hash);
hash[4] = (hash[4] + temp1) | 0;
}
for (i = 0; i < 8; i++) { hash[i] = (hash[i] + oldHash[i]) | 0; }
}
for (i = 0; i < 8; i++) {
for (j = 3; j + 1; j--) {
var b = (hash[i] >> (j * 8)) & 255;
result += ((b < 16) ? 0 : '') + b.toString(16);
}
}
return result;
}
function b64u(s) {
try {
return btoa(unescape(encodeURIComponent(s)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
} catch (e) { return ''; }
}
function unb64u(s) {
try { return decodeURIComponent(escape(atob(s.replace(/-/g, '+').replace(/_/g, '/')))); }
catch (e) { return ''; }
}
function rand(n) {
var s = '', chars = '0123456789abcdef';
try {
var a = new Uint8Array(n);
(window.crypto || window.msCrypto).getRandomValues(a);
for (var i = 0; i < n; i++) s += chars[a[i] & 15];
} catch (e) {
for (var j = 0; j < n; j++) s += chars[Math.floor(Math.random() * 16)];
}
return s;
}
function resolveLang(lang) {
if (lang && lang !== 'auto' && STR[lang]) return lang;
var nav = ((navigator.languages && navigator.languages[0]) || navigator.language || 'en').slice(0, 2).toLowerCase();
return STR[nav] ? nav : 'en';
}
function resolveTheme(theme) {
if (theme === 'light' || theme === 'dark') return theme;
try { return (window.matchMedia && matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; }
catch (e) { return 'light'; }
}
function powBits(challenge) {
try {
var dot = challenge.lastIndexOf('.');
var p = JSON.parse(unb64u(challenge.slice(0, dot)));
var d = p && typeof p.d === 'number' ? p.d : 12;
if (d <= 0 || d > 24) d = 12;
return Math.round(d / 4) * 4;
} catch (e) { return 12; }
}
function solvePow(challenge, bits) {
var zeros = Math.max(1, Math.round(bits / 4));
var prefix = new Array(zeros + 1).join('0');
for (var p = 0; p < 2000000; p++) {
var h = sha256(challenge + '.' + p);
if (h && h.slice(0, zeros) === prefix) return { p: p, h: h };
}
return { p: p, h: '' };
}
var STYLE = '.vg-root{display:inline-block;width:100%;max-width:340px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;direction:ltr}'
+ '.vg-box{border:1px solid #d4d4d8;border-radius:10px;background:#fff;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.07);cursor:pointer;transition:border-color .2s,box-shadow .2s;-webkit-user-select:none;user-select:none}'
+ '.vg-box:focus-visible{outline:2px solid #10b981;outline-offset:2px}'
+ '.vg-dark{background:#18181b;border-color:#3f3f46}'
+ '.vg-main{display:flex;align-items:center;gap:11px;padding:15px 16px;min-height:52px}'
+ '.vg-cb{width:24px;height:24px;border:2px solid #a1a1aa;border-radius:5px;background:#fff;flex:none;display:flex;align-items:center;justify-content:center;position:relative;transition:border-color .15s,background .15s}'
+ '.vg-dark .vg-cb{background:transparent;border-color:#52525b}'
+ '.vg-box:hover .vg-cb{border-color:#10b981}'
+ '.vg-cb svg{width:15px;height:15px;opacity:0;transform:scale(.3);transition:opacity .18s ease,transform .18s ease}'
+ '.vg-ok .vg-cb{background:#10b981;border-color:#10b981}'
+ '.vg-ok .vg-cb svg{opacity:1;transform:scale(1)}'
+ '.vg-label{font-size:14px;color:#3f3f46;font-weight:500;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}'
+ '.vg-dark .vg-label{color:#e4e4e7}'
+ '.vg-ok .vg-label{color:#059669;font-weight:600}'
+ '.vg-err .vg-label{color:#dc2626;font-weight:500}'
+ '.vg-brand{display:flex;align-items:center;gap:5px;color:#71717a;font-size:11.5px;font-weight:700;letter-spacing:.01em;flex:none}'
+ '.vg-brand svg{width:18px;height:18px;display:block}'
+ '.vg-foot{display:flex;justify-content:flex-end;align-items:center;gap:5px;padding:4px 12px 6px;border-top:1px solid #f4f4f5;background:#fafafa}'
+ '.vg-dark .vg-foot{border-top-color:#27272a;background:#131316}'
+ '.vg-foot svg{width:10px;height:10px;display:block;color:#a1a1aa}'
+ '.vg-foot a{font-size:10.5px;color:#a1a1aa;text-decoration:none;font-weight:500}'
+ '.vg-foot a:hover{color:#10b981;text-decoration:underline}'
+ '.vg-spinner{position:absolute;inset:2px;border-radius:50%;border:2px solid #e4e4e7;border-top-color:#10b981;opacity:0;transition:opacity .15s;animation:vg-spin .8s linear infinite}'
+ '.vg-dark .vg-spinner{border-color:#3f3f46}'
+ '.vg-checking .vg-spinner{opacity:1}'
+ '.vg-ok{border-color:#10b981 !important;box-shadow:0 0 0 3px rgba(16,185,129,.14)}'
+ '.vg-err{border-color:#f87171 !important}'
+ '@keyframes vg-spin{to{transform:rotate(360deg)}}'
+ '@media (prefers-reduced-motion:reduce){.vg-spinner{animation-duration:2s}}';
var LOGO = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" fill="#10b981" fill-opacity="0.14" stroke="#10b981" stroke-width="1.5" stroke-linejoin="round"/><path d="m8.6 12 2.4 2.4 4.4-4.8" stroke="#10b981" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var CHECK = '<svg viewBox="0 0 24 24" fill="none"><path d="m5 13 4.2 4.2L19 7" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var MINI = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>';
function ensureStyle() {
if (document.getElementById('vg-style')) return;
var st = document.createElement('style');
st.id = 'vg-style';
st.textContent = STYLE;
document.head.appendChild(st);
}
function render(target, opts) {
opts = opts || {};
var el = typeof target === 'string' ? document.querySelector(target) : target;
if (!el || !el.appendChild) return null;
ensureStyle();
var lang = resolveLang(opts.lang);
var t = STR[lang];
var theme = resolveTheme(opts.theme);
var sitekey = opts.sitekey || '';
var inputName = opts.inputName || 'veilguard-token';
var mode = opts.mode === 'static' ? 'static' : 'server';
var challengeUrl = opts.challengeUrl || ('/veilguard-challenge?sitekey=' + encodeURIComponent(sitekey));
var st = { mm: 0, ds: 0, kd: 0, td: 0, pd: 0, scc: 0, load: Date.now(), token: '', done: false, busy: false };
var lastXY = null;
function track(evt, fn) { document.addEventListener(evt, fn, { passive: true, capture: true }); }
track('mousemove', function (e) {
st.mm++;
if (lastXY) { var dx = e.clientX - lastXY[0], dy = e.clientY - lastXY[1]; st.ds += Math.sqrt(dx * dx + dy * dy); }
lastXY = [e.clientX, e.clientY];
});
track('keydown', function () { st.kd++; });
track('touchstart', function () { st.td++; });
track('pointerdown', function () { st.pd++; });
track('scroll', function () { st.scc++; });
el.innerHTML = '';
el.className += ' vg-root';
var box = document.createElement('div');
box.className = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
box.setAttribute('role', 'checkbox');
box.setAttribute('aria-checked', 'false');
box.setAttribute('aria-label', t.nb);
box.tabIndex = 0;
box.innerHTML = '<div class="vg-main">'
+ '<div class="vg-cb"><span class="vg-spinner"></span>' + CHECK + '</div>'
+ '<span class="vg-label" aria-live="polite">' + t.nb + '</span>'
+ '<div class="vg-brand">' + LOGO + '<span>VeilGuard</span></div>'
+ '</div>'
+ '<div class="vg-foot">' + MINI + '<a href="' + LINK + '" target="_blank" rel="noopener noreferrer">veilguard.online</a></div>';
var input = document.createElement('input');
input.type = 'hidden';
input.name = inputName;
el.appendChild(box);
el.appendChild(input);
var label = box.querySelector('.vg-label');
function setState(s) {
var cls = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
if (s === 'checking') cls += ' vg-checking';
if (s === 'ok') cls += ' vg-ok';
if (s === 'err') cls += ' vg-err';
box.className = cls;
box.setAttribute('aria-checked', s === 'ok' ? 'true' : 'false');
label.textContent = s === 'checking' ? t.ck : s === 'ok' ? t.vd : s === 'err' ? t.er : t.nb;
}
function detect() {
var flags = 0;
try { if (navigator.webdriver === true || !!window.webdriver) flags |= 1; } catch (e) {}
if (BOT_UA.test(navigator.userAgent)) flags |= 2;
try {
if (window._phantom || window.callPhantom || window.__nightmare || window.domAutomation || window.__selenium_unwrapped) flags |= 4;
} catch (e) {}
try { if ((window.outerWidth === 0 || window.outerHeight === 0) && window.innerWidth > 0) flags |= 8; } catch (e) {}
try {
var c = document.createElement('canvas');
var gl = c.getContext && (c.getContext('webgl') || c.getContext('experimental-webgl'));
if (gl) {
var dbg = gl.getExtension('WEBGL_debug_renderer_info');
var r = dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : '';
if (typeof r === 'string' && /swiftshader|llvmpipe|software|basic render/i.test(r)) flags |= 16;
}
} catch (e) {}
try { if (!navigator.languages || !navigator.languages.length) flags |= 32; } catch (e) {}
var isChromeUA = navigator.userAgent.indexOf('Chrome/') !== -1 && !/Edg|OPR|SamsungBrowser/i.test(navigator.userAgent);
if (isChromeUA && !window.chrome) flags |= 64;
if (isChromeUA && (!navigator.plugins || navigator.plugins.length === 0)) flags |= 128;
var score = 100;
if (flags & 31) { score = 0; }
else {
if (flags & 32) score -= 20;
if (flags & 64) score -= 15;
if (flags & 128) score -= 10;
var dt = Date.now() - st.load;
if (dt < 350) score -= 30;
if (st.mm === 0 && st.td === 0 && st.kd === 0 && st.pd <= 1) score -= 25;
else if (st.mm > 0 && st.ds < 40) score -= 10;
if (score < 0) score = 0;
if (score > 100) score = 100;
}
return { flags: flags, score: score };
}
function staticChallenge() {
var now = Date.now();
return b64u(JSON.stringify({ n: rand(16), i: now, e: now + 300000, k: sitekey, d: 12 }));
}
function activate() {
if (st.done || st.busy) return;
st.busy = true;
setState('checking');
var started = Date.now();
var finish = function (challenge) {
if (!challenge) { setState('err'); st.busy = false; return; }
try {
var det = detect();
var pow = solvePow(challenge, powBits(challenge));
var att = {
v: 1, ch: challenge, ts: Date.now(), sc: det.score, hl: det.flags,
lg: lang, ua: navigator.userAgent.slice(0, 180),
ev: { mm: st.mm, ds: Math.round(st.ds), kd: st.kd, td: st.td, pd: st.pd, sc: st.scc, dt: started - st.load },
pw: pow
};
st.token = 'v1.' + b64u(JSON.stringify(att));
input.value = st.token;
} catch (e) { st.token = ''; }
var wait = Math.max(0, 550 + Math.random() * 350 - (Date.now() - started));
setTimeout(function () {
st.busy = false;
if (st.token) {
st.done = true;
setState('ok');
if (typeof opts.callback === 'function') { try { opts.callback(st.token); } catch (e2) {} }
} else { setState('err'); }
}, wait);
};
if (mode === 'static') { finish(staticChallenge()); return; }
try {
fetch(challengeUrl, { credentials: 'omit' })
.then(function (r) { if (!r.ok) throw new Error('http ' + r.status); return r.json(); })
.then(function (d) { finish(d && d.challenge ? String(d.challenge) : ''); })
.catch(function () { setState('err'); st.busy = false; });
} catch (e) { setState('err'); st.busy = false; }
}
box.addEventListener('click', activate);
box.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); activate(); }
});
return {
reset: function () { st.done = false; st.busy = false; st.token = ''; input.value = ''; setState('idle'); },
getResponse: function () { return st.token; },
getSiteKey: function () { return sitekey; }
};
}
function auto() {
var nodes = document.querySelectorAll('.veilguard[data-sitekey]');
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (n.getAttribute('data-vg-init') === '1') continue;
n.setAttribute('data-vg-init', '1');
render(n, {
sitekey: n.getAttribute('data-sitekey') || '',
lang: n.getAttribute('data-lang') || 'auto',
theme: n.getAttribute('data-theme') || 'auto',
mode: n.getAttribute('data-mode') || 'server',
challengeUrl: n.getAttribute('data-challenge-url') || undefined,
inputName: n.getAttribute('data-input-name') || 'veilguard-token'
});
}
}
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', auto); }
else { auto(); }
window.VeilGuard = { render: render, auto: auto, version: '1.0.0' };
})();
<!-- VeilGuard widget — place inside your <form> -->
<div class="veilguard" data-sitekey="vg_YOUR_SITE_KEY" data-challenge-url="/veilguard-challenge"></div>
<!-- Load the widget once per page (before </body>) -->
<script src="veilguard-widget.js"></script>
2 · التحقق: يعمل على خادمك
/**
* VeilGuard v1.0.0 — self-hosted verification library for Node.js
* Docs: https://veilguard.online
*/
"use strict";
const crypto = require("crypto");
const VG_MIN_SCORE = 55;
const VG_TTL_MS = 5 * 60 * 1000;
const VG_GRACE_MS = 60 * 1000;
const VG_MIN_SOLVE_MS = 250;
function b64uDecode(s) {
return Buffer.from(s, "base64url").toString("utf8");
}
/** Issue a signed single-use challenge (used by your challenge endpoint). */
function issueChallenge(siteKey, secret) {
const now = Date.now();
const payload = JSON.stringify({
n: crypto.randomBytes(8).toString("hex"),
i: now,
e: now + VG_TTL_MS,
k: siteKey,
d: 12,
});
const body = Buffer.from(payload, "utf8").toString("base64url");
const sig = crypto.createHmac("sha256", secret).update(body, "utf8").digest("hex");
return body + "." + sig;
}
// Single-use nonce store (process lifetime). Swap for Redis/DB in multi-instance setups.
const usedNonces = new Map();
/** Verify a token submitted by the visitor. */
function verifyToken(token, secret, siteKey, opts) {
opts = opts || {};
const minScore = opts.minScore || VG_MIN_SCORE;
const fail = (reason) => ({ success: false, reason });
if (typeof token !== "string" || token.length > 4096 || !token.startsWith("v1.")) {
return fail("Malformed token");
}
let att;
try { att = JSON.parse(b64uDecode(token.slice(3))); } catch (e) { return fail("Malformed payload"); }
if (!att || typeof att.ch !== "string") return fail("Malformed payload");
// 1. Cryptographic challenge validation (HMAC-SHA256)
const ch = att.ch;
const dot = ch.lastIndexOf(".");
if (dot <= 0) return fail("Malformed challenge");
const body = ch.slice(0, dot);
const sig = ch.slice(dot + 1);
const expected = crypto.createHmac("sha256", secret).update(body, "utf8").digest("hex");
const a = Buffer.from(sig);
const b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return fail("Invalid challenge signature");
}
let p;
try { p = JSON.parse(b64uDecode(body)); } catch (e) { return fail("Malformed challenge payload"); }
if (p.k !== siteKey) return fail("Site key mismatch");
// 2. Timing window — server clock only
const now = Date.now();
if (now > p.e + VG_GRACE_MS) return fail("Challenge expired");
if (now < p.i) return fail("Challenge issued in the future");
if (now - p.i < VG_MIN_SOLVE_MS) return fail("Solved impossibly fast");
// 3. Proof-of-work bound to this challenge
if (!att.pw || typeof att.pw.p === "undefined") return fail("Missing proof of work");
const hash = crypto.createHash("sha256").update(ch + "." + att.pw.p, "utf8").digest("hex");
const zeros = Math.ceil(p.d / 4);
if (!hash.startsWith("0".repeat(zeros))) return fail("Invalid proof of work");
// 4. Automation fingerprints reported by the widget (bitmask)
const hl = typeof att.hl === "number" ? att.hl : 255;
if (hl & 1) return fail("Automation framework detected");
if (hl & 2) return fail("Known bot user agent");
if (hl & 4) return fail("Automation framework detected");
if (hl & 8) return fail("Headless browser detected");
if (hl & 16) return fail("Software renderer detected");
// 5. Behavioral score threshold
const score = typeof att.sc === "number" ? att.sc : 0;
if (score < minScore) return fail("Score below threshold");
// 6. Single-use nonce — replay protection
if (usedNonces.has(p.n)) return fail("Token replay detected");
usedNonces.set(p.n, p.e + VG_GRACE_MS);
if (usedNonces.size > 50000) {
const t = Date.now();
for (const [k, exp] of usedNonces) { if (exp < t) usedNonces.delete(k); }
}
return { success: true, score };
}
module.exports = { issueChallenge, verifyToken };
/**
* VeilGuard + Express example — https://veilguard.online
* Install: npm install express
*/
"use strict";
const express = require("express");
const { issueChallenge, verifyToken } = require("./veilguard.js");
const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(express.static(__dirname)); // serves veilguard-widget.js + your pages
const SITE_KEY = "vg_YOUR_SITE_KEY";
const SECRET_KEY = "vgs_YOUR_SECRET_KEY";
// 1. Challenge endpoint — the widget calls this automatically.
app.get("/veilguard-challenge", (req, res) => {
res.set("Cache-Control", "no-store").json({ challenge: issueChallenge(SITE_KEY, SECRET_KEY) });
});
// 2. Protect any POST route — read the hidden "veilguard-token" field.
app.post("/signup", (req, res) => {
const result = verifyToken(req.body["veilguard-token"] || "", SECRET_KEY, SITE_KEY);
if (!result.success) {
return res.status(403).send("Bot detected: " + result.reason);
}
// Human confirmed — continue with your logic.
res.send("Welcome, human! Trust score: " + result.score);
});
app.listen(3000, () => console.log("VeilGuard example running on http://localhost:3000"));
1 · الأداة: الصقها في نموذجك
/*! VeilGuard v1.0.0 - self-hosted bot protection - https://veilguard.online */
(function () {
'use strict';
var LINK = 'https://veilguard.online';
var STR = {
en: { nb: "I'm not a bot", vd: 'Verified!', ck: 'Verifying…', er: 'Verification failed — click to retry' },
de: { nb: 'Ich bin kein Bot', vd: 'Verifiziert!', ck: 'Prüfung…', er: 'Fehlgeschlagen — zum Wiederholen klicken' },
es: { nb: 'No soy un bot', vd: '¡Verificado!', ck: 'Verificando…', er: 'Error — haz clic para reintentar' },
fr: { nb: "Je ne suis pas un robot", vd: 'Vérifié !', ck: 'Vérification…', er: 'Échec — cliquez pour réessayer' },
pt: { nb: 'Não sou um bot', vd: 'Verificado!', ck: 'Verificando…', er: 'Falhou — clique para tentar de novo' },
ru: { nb: 'Я не робот', vd: 'Проверено!', ck: 'Проверка…', er: 'Ошибка — нажмите для повтора' },
zh: { nb: '我不是机器人', vd: '已验证!', ck: '验证中…', er: '验证失败 — 点击重试' },
ja: { nb: 'ロボットではありません', vd: '認証済み!', ck: '確認中…', er: '失敗 — クリックで再試行' },
ar: { nb: 'لست روبوتًا', vd: 'تم التحقق!', ck: 'جارٍ التحقق…', er: 'فشل — انقر للمحاولة مجددًا' }
};
var BOT_UA = /bot|crawl|spider|slurp|headless|phantom|selenium|puppeteer|playwright|webdriver|curl|wget|python|scrapy|axios|go-http|httpclient|okhttp|libwww|lighthouse|facebookexternalhit|telegram|whatsapp|discord/i;
function sha256(ascii) {
function rightRotate(value, amount) { return (value >>> amount) | (value << (32 - amount)); }
var mathPow = Math.pow, maxWord = mathPow(2, 32), result = '';
var words = [], asciiBitLength = ascii.length * 8;
var hash = sha256.h = sha256.h || [], k = sha256.k = sha256.k || [], primeCounter = k.length;
var isComposite = {};
for (var candidate = 2; primeCounter < 64; candidate++) {
if (!isComposite[candidate]) {
for (var i = 0; i < 313; i += candidate) { isComposite[i] = candidate; }
hash[primeCounter] = (mathPow(candidate, 0.5) * maxWord) | 0;
k[primeCounter++] = (mathPow(candidate, 1 / 3) * maxWord) | 0;
}
}
ascii += '\x80';
while (ascii.length % 64 - 56) ascii += '\x00';
for (i = 0; i < ascii.length; i++) {
var j = ascii.charCodeAt(i);
if (j >> 8) return '';
words[i >> 2] |= j << ((3 - i) % 4) * 8;
}
words[words.length] = ((asciiBitLength / maxWord) | 0);
words[words.length] = (asciiBitLength);
for (j = 0; j < words.length;) {
var w = words.slice(j, j += 16), oldHash = hash;
hash = hash.slice(0, 8);
for (i = 0; i < 64; i++) {
var w15 = w[i - 15], w2 = w[i - 2];
var a = hash[0], e = hash[4];
var temp1 = hash[7]
+ (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25))
+ ((e & hash[5]) ^ ((~e) & hash[6]))
+ k[i]
+ (w[i] = (i < 16) ? w[i] : (
w[i - 16]
+ (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15 >>> 3))
+ w[i - 7]
+ (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2 >>> 10))
) | 0);
var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22))
+ ((a & hash[1]) ^ (a & hash[2]) ^ (hash[1] & hash[2]));
hash = [(temp1 + temp2) | 0].concat(hash);
hash[4] = (hash[4] + temp1) | 0;
}
for (i = 0; i < 8; i++) { hash[i] = (hash[i] + oldHash[i]) | 0; }
}
for (i = 0; i < 8; i++) {
for (j = 3; j + 1; j--) {
var b = (hash[i] >> (j * 8)) & 255;
result += ((b < 16) ? 0 : '') + b.toString(16);
}
}
return result;
}
function b64u(s) {
try {
return btoa(unescape(encodeURIComponent(s)))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
} catch (e) { return ''; }
}
function unb64u(s) {
try { return decodeURIComponent(escape(atob(s.replace(/-/g, '+').replace(/_/g, '/')))); }
catch (e) { return ''; }
}
function rand(n) {
var s = '', chars = '0123456789abcdef';
try {
var a = new Uint8Array(n);
(window.crypto || window.msCrypto).getRandomValues(a);
for (var i = 0; i < n; i++) s += chars[a[i] & 15];
} catch (e) {
for (var j = 0; j < n; j++) s += chars[Math.floor(Math.random() * 16)];
}
return s;
}
function resolveLang(lang) {
if (lang && lang !== 'auto' && STR[lang]) return lang;
var nav = ((navigator.languages && navigator.languages[0]) || navigator.language || 'en').slice(0, 2).toLowerCase();
return STR[nav] ? nav : 'en';
}
function resolveTheme(theme) {
if (theme === 'light' || theme === 'dark') return theme;
try { return (window.matchMedia && matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; }
catch (e) { return 'light'; }
}
function powBits(challenge) {
try {
var dot = challenge.lastIndexOf('.');
var p = JSON.parse(unb64u(challenge.slice(0, dot)));
var d = p && typeof p.d === 'number' ? p.d : 12;
if (d <= 0 || d > 24) d = 12;
return Math.round(d / 4) * 4;
} catch (e) { return 12; }
}
function solvePow(challenge, bits) {
var zeros = Math.max(1, Math.round(bits / 4));
var prefix = new Array(zeros + 1).join('0');
for (var p = 0; p < 2000000; p++) {
var h = sha256(challenge + '.' + p);
if (h && h.slice(0, zeros) === prefix) return { p: p, h: h };
}
return { p: p, h: '' };
}
var STYLE = '.vg-root{display:inline-block;width:100%;max-width:340px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;direction:ltr}'
+ '.vg-box{border:1px solid #d4d4d8;border-radius:10px;background:#fff;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.07);cursor:pointer;transition:border-color .2s,box-shadow .2s;-webkit-user-select:none;user-select:none}'
+ '.vg-box:focus-visible{outline:2px solid #10b981;outline-offset:2px}'
+ '.vg-dark{background:#18181b;border-color:#3f3f46}'
+ '.vg-main{display:flex;align-items:center;gap:11px;padding:15px 16px;min-height:52px}'
+ '.vg-cb{width:24px;height:24px;border:2px solid #a1a1aa;border-radius:5px;background:#fff;flex:none;display:flex;align-items:center;justify-content:center;position:relative;transition:border-color .15s,background .15s}'
+ '.vg-dark .vg-cb{background:transparent;border-color:#52525b}'
+ '.vg-box:hover .vg-cb{border-color:#10b981}'
+ '.vg-cb svg{width:15px;height:15px;opacity:0;transform:scale(.3);transition:opacity .18s ease,transform .18s ease}'
+ '.vg-ok .vg-cb{background:#10b981;border-color:#10b981}'
+ '.vg-ok .vg-cb svg{opacity:1;transform:scale(1)}'
+ '.vg-label{font-size:14px;color:#3f3f46;font-weight:500;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}'
+ '.vg-dark .vg-label{color:#e4e4e7}'
+ '.vg-ok .vg-label{color:#059669;font-weight:600}'
+ '.vg-err .vg-label{color:#dc2626;font-weight:500}'
+ '.vg-brand{display:flex;align-items:center;gap:5px;color:#71717a;font-size:11.5px;font-weight:700;letter-spacing:.01em;flex:none}'
+ '.vg-brand svg{width:18px;height:18px;display:block}'
+ '.vg-foot{display:flex;justify-content:flex-end;align-items:center;gap:5px;padding:4px 12px 6px;border-top:1px solid #f4f4f5;background:#fafafa}'
+ '.vg-dark .vg-foot{border-top-color:#27272a;background:#131316}'
+ '.vg-foot svg{width:10px;height:10px;display:block;color:#a1a1aa}'
+ '.vg-foot a{font-size:10.5px;color:#a1a1aa;text-decoration:none;font-weight:500}'
+ '.vg-foot a:hover{color:#10b981;text-decoration:underline}'
+ '.vg-spinner{position:absolute;inset:2px;border-radius:50%;border:2px solid #e4e4e7;border-top-color:#10b981;opacity:0;transition:opacity .15s;animation:vg-spin .8s linear infinite}'
+ '.vg-dark .vg-spinner{border-color:#3f3f46}'
+ '.vg-checking .vg-spinner{opacity:1}'
+ '.vg-ok{border-color:#10b981 !important;box-shadow:0 0 0 3px rgba(16,185,129,.14)}'
+ '.vg-err{border-color:#f87171 !important}'
+ '@keyframes vg-spin{to{transform:rotate(360deg)}}'
+ '@media (prefers-reduced-motion:reduce){.vg-spinner{animation-duration:2s}}';
var LOGO = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" fill="#10b981" fill-opacity="0.14" stroke="#10b981" stroke-width="1.5" stroke-linejoin="round"/><path d="m8.6 12 2.4 2.4 4.4-4.8" stroke="#10b981" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var CHECK = '<svg viewBox="0 0 24 24" fill="none"><path d="m5 13 4.2 4.2L19 7" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var MINI = '<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 5.5v6c0 4.8-3.3 9-8 10.7C7.3 20.5 4 16.3 4 11.5v-6L12 2.5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></svg>';
function ensureStyle() {
if (document.getElementById('vg-style')) return;
var st = document.createElement('style');
st.id = 'vg-style';
st.textContent = STYLE;
document.head.appendChild(st);
}
function render(target, opts) {
opts = opts || {};
var el = typeof target === 'string' ? document.querySelector(target) : target;
if (!el || !el.appendChild) return null;
ensureStyle();
var lang = resolveLang(opts.lang);
var t = STR[lang];
var theme = resolveTheme(opts.theme);
var sitekey = opts.sitekey || '';
var inputName = opts.inputName || 'veilguard-token';
var mode = opts.mode === 'static' ? 'static' : 'server';
var challengeUrl = opts.challengeUrl || ('/veilguard-challenge?sitekey=' + encodeURIComponent(sitekey));
var st = { mm: 0, ds: 0, kd: 0, td: 0, pd: 0, scc: 0, load: Date.now(), token: '', done: false, busy: false };
var lastXY = null;
function track(evt, fn) { document.addEventListener(evt, fn, { passive: true, capture: true }); }
track('mousemove', function (e) {
st.mm++;
if (lastXY) { var dx = e.clientX - lastXY[0], dy = e.clientY - lastXY[1]; st.ds += Math.sqrt(dx * dx + dy * dy); }
lastXY = [e.clientX, e.clientY];
});
track('keydown', function () { st.kd++; });
track('touchstart', function () { st.td++; });
track('pointerdown', function () { st.pd++; });
track('scroll', function () { st.scc++; });
el.innerHTML = '';
el.className += ' vg-root';
var box = document.createElement('div');
box.className = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
box.setAttribute('role', 'checkbox');
box.setAttribute('aria-checked', 'false');
box.setAttribute('aria-label', t.nb);
box.tabIndex = 0;
box.innerHTML = '<div class="vg-main">'
+ '<div class="vg-cb"><span class="vg-spinner"></span>' + CHECK + '</div>'
+ '<span class="vg-label" aria-live="polite">' + t.nb + '</span>'
+ '<div class="vg-brand">' + LOGO + '<span>VeilGuard</span></div>'
+ '</div>'
+ '<div class="vg-foot">' + MINI + '<a href="' + LINK + '" target="_blank" rel="noopener noreferrer">veilguard.online</a></div>';
var input = document.createElement('input');
input.type = 'hidden';
input.name = inputName;
el.appendChild(box);
el.appendChild(input);
var label = box.querySelector('.vg-label');
function setState(s) {
var cls = 'vg-box' + (theme === 'dark' ? ' vg-dark' : '');
if (s === 'checking') cls += ' vg-checking';
if (s === 'ok') cls += ' vg-ok';
if (s === 'err') cls += ' vg-err';
box.className = cls;
box.setAttribute('aria-checked', s === 'ok' ? 'true' : 'false');
label.textContent = s === 'checking' ? t.ck : s === 'ok' ? t.vd : s === 'err' ? t.er : t.nb;
}
function detect() {
var flags = 0;
try { if (navigator.webdriver === true || !!window.webdriver) flags |= 1; } catch (e) {}
if (BOT_UA.test(navigator.userAgent)) flags |= 2;
try {
if (window._phantom || window.callPhantom || window.__nightmare || window.domAutomation || window.__selenium_unwrapped) flags |= 4;
} catch (e) {}
try { if ((window.outerWidth === 0 || window.outerHeight === 0) && window.innerWidth > 0) flags |= 8; } catch (e) {}
try {
var c = document.createElement('canvas');
var gl = c.getContext && (c.getContext('webgl') || c.getContext('experimental-webgl'));
if (gl) {
var dbg = gl.getExtension('WEBGL_debug_renderer_info');
var r = dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : '';
if (typeof r === 'string' && /swiftshader|llvmpipe|software|basic render/i.test(r)) flags |= 16;
}
} catch (e) {}
try { if (!navigator.languages || !navigator.languages.length) flags |= 32; } catch (e) {}
var isChromeUA = navigator.userAgent.indexOf('Chrome/') !== -1 && !/Edg|OPR|SamsungBrowser/i.test(navigator.userAgent);
if (isChromeUA && !window.chrome) flags |= 64;
if (isChromeUA && (!navigator.plugins || navigator.plugins.length === 0)) flags |= 128;
var score = 100;
if (flags & 31) { score = 0; }
else {
if (flags & 32) score -= 20;
if (flags & 64) score -= 15;
if (flags & 128) score -= 10;
var dt = Date.now() - st.load;
if (dt < 350) score -= 30;
if (st.mm === 0 && st.td === 0 && st.kd === 0 && st.pd <= 1) score -= 25;
else if (st.mm > 0 && st.ds < 40) score -= 10;
if (score < 0) score = 0;
if (score > 100) score = 100;
}
return { flags: flags, score: score };
}
function staticChallenge() {
var now = Date.now();
return b64u(JSON.stringify({ n: rand(16), i: now, e: now + 300000, k: sitekey, d: 12 }));
}
function activate() {
if (st.done || st.busy) return;
st.busy = true;
setState('checking');
var started = Date.now();
var finish = function (challenge) {
if (!challenge) { setState('err'); st.busy = false; return; }
try {
var det = detect();
var pow = solvePow(challenge, powBits(challenge));
var att = {
v: 1, ch: challenge, ts: Date.now(), sc: det.score, hl: det.flags,
lg: lang, ua: navigator.userAgent.slice(0, 180),
ev: { mm: st.mm, ds: Math.round(st.ds), kd: st.kd, td: st.td, pd: st.pd, sc: st.scc, dt: started - st.load },
pw: pow
};
st.token = 'v1.' + b64u(JSON.stringify(att));
input.value = st.token;
} catch (e) { st.token = ''; }
var wait = Math.max(0, 550 + Math.random() * 350 - (Date.now() - started));
setTimeout(function () {
st.busy = false;
if (st.token) {
st.done = true;
setState('ok');
if (typeof opts.callback === 'function') { try { opts.callback(st.token); } catch (e2) {} }
} else { setState('err'); }
}, wait);
};
if (mode === 'static') { finish(staticChallenge()); return; }
try {
fetch(challengeUrl, { credentials: 'omit' })
.then(function (r) { if (!r.ok) throw new Error('http ' + r.status); return r.json(); })
.then(function (d) { finish(d && d.challenge ? String(d.challenge) : ''); })
.catch(function () { setState('err'); st.busy = false; });
} catch (e) { setState('err'); st.busy = false; }
}
box.addEventListener('click', activate);
box.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); activate(); }
});
return {
reset: function () { st.done = false; st.busy = false; st.token = ''; input.value = ''; setState('idle'); },
getResponse: function () { return st.token; },
getSiteKey: function () { return sitekey; }
};
}
function auto() {
var nodes = document.querySelectorAll('.veilguard[data-sitekey]');
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (n.getAttribute('data-vg-init') === '1') continue;
n.setAttribute('data-vg-init', '1');
render(n, {
sitekey: n.getAttribute('data-sitekey') || '',
lang: n.getAttribute('data-lang') || 'auto',
theme: n.getAttribute('data-theme') || 'auto',
mode: n.getAttribute('data-mode') || 'server',
challengeUrl: n.getAttribute('data-challenge-url') || undefined,
inputName: n.getAttribute('data-input-name') || 'veilguard-token'
});
}
}
if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', auto); }
else { auto(); }
window.VeilGuard = { render: render, auto: auto, version: '1.0.0' };
})();
<!-- VeilGuard widget — place inside your <form> -->
<div class="veilguard" data-sitekey="vg_YOUR_SITE_KEY" data-challenge-url="/veilguard-challenge"></div>
<!-- Load the widget once per page (before </body>) -->
<script src="veilguard-widget.js"></script>
2 · التحقق: يعمل على خادمك
"""
VeilGuard v1.0.0 — self-hosted verification library for Python
Docs: https://veilguard.online
"""
import base64
import hashlib
import hmac
import json
import os
import time
MIN_SCORE = 55
TTL_MS = 5 * 60 * 1000
GRACE_MS = 60 * 1000
MIN_SOLVE_MS = 250
_used_nonces = {} # single-use nonce store (process lifetime)
def _b64u_decode(s: str) -> bytes:
return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
def issue_challenge(site_key: str, secret: str) -> str:
"""Issue a signed single-use challenge (used by your challenge endpoint)."""
now = int(time.time() * 1000)
payload = json.dumps({
"n": os.urandom(8).hex(),
"i": now,
"e": now + TTL_MS,
"k": site_key,
"d": 12,
}, separators=(",", ":"))
body = base64.urlsafe_b64encode(payload.encode()).rstrip(b"=").decode()
sig = hmac.new(secret.encode(), body.encode(), hashlib.sha256).hexdigest()
return body + "." + sig
def verify_token(token: str, secret: str, site_key: str, min_score: int = MIN_SCORE) -> dict:
"""Verify a token submitted by the visitor."""
def fail(reason: str) -> dict:
return {"success": False, "reason": reason}
if not isinstance(token, str) or len(token) > 4096 or not token.startswith("v1."):
return fail("Malformed token")
try:
att = json.loads(_b64u_decode(token[3:]))
except Exception:
return fail("Malformed payload")
if not isinstance(att, dict) or not isinstance(att.get("ch"), str):
return fail("Malformed payload")
# 1. Cryptographic challenge validation (HMAC-SHA256)
ch = att["ch"]
dot = ch.rfind(".")
if dot <= 0:
return fail("Malformed challenge")
body, sig = ch[:dot], ch[dot + 1:]
expected = hmac.new(secret.encode(), body.encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
return fail("Invalid challenge signature")
try:
p = json.loads(_b64u_decode(body))
except Exception:
return fail("Malformed challenge payload")
if p.get("k") != site_key:
return fail("Site key mismatch")
# 2. Timing window — server clock only
now = int(time.time() * 1000)
if now > p["e"] + GRACE_MS:
return fail("Challenge expired")
if now < p["i"]:
return fail("Challenge issued in the future")
if now - p["i"] < MIN_SOLVE_MS:
return fail("Solved impossibly fast")
# 3. Proof-of-work bound to this challenge
pw = att.get("pw") or {}
if "p" not in pw:
return fail("Missing proof of work")
h = hashlib.sha256((ch + "." + str(pw["p"])).encode()).hexdigest()
zeros = -(-p["d"] // 4) # ceil division
if not h.startswith("0" * zeros):
return fail("Invalid proof of work")
# 4. Automation fingerprints reported by the widget (bitmask)
flags = att.get("hl", 255)
for bit, reason in ((1, "Automation framework detected"),
(2, "Known bot user agent"),
(4, "Automation framework detected"),
(8, "Headless browser detected"),
(16, "Software renderer detected")):
if flags & bit:
return fail(reason)
# 5. Behavioral score threshold
score = att.get("sc", 0)
if score < min_score:
return fail("Score below threshold")
# 6. Single-use nonce — replay protection
if p["n"] in _used_nonces:
return fail("Token replay detected")
_used_nonces[p["n"]] = p["e"] + GRACE_MS
if len(_used_nonces) > 50000:
cutoff = time.time() * 1000
for k in [k for k, v in _used_nonces.items() if v < cutoff]:
del _used_nonces[k]
return {"success": True, "score": score}
"""
VeilGuard + Flask example — https://veilguard.online
Install: pip install flask
"""
from flask import Flask, request, jsonify, send_from_directory
from veilguard import issue_challenge, verify_token
app = Flask(__name__)
SITE_KEY = "vg_YOUR_SITE_KEY"
SECRET_KEY = "vgs_YOUR_SECRET_KEY"
@app.get("/")
def index():
return send_from_directory(".", "index.html") # your form page
@app.get("/veilguard-widget.js")
def widget():
return send_from_directory(".", "veilguard-widget.js")
@app.get("/veilguard-challenge")
def challenge():
resp = jsonify({"challenge": issue_challenge(SITE_KEY, SECRET_KEY)})
resp.headers["Cache-Control"] = "no-store"
return resp
@app.post("/signup")
def signup():
result = verify_token(request.form.get("veilguard-token", ""), SECRET_KEY, SITE_KEY)
if not result["success"]:
return jsonify(result), 403
# Human confirmed — continue with your logic.
return jsonify(result)
if __name__ == "__main__":
app.run(port=3000)
يمكن أيضًا إدارة المفاتيح من لوحة الإدارة.
إحصائياتك الخاصة
VeilGuard لا يرسل أي شيء إلى الخارج. إذا أردت معرفة عدد مرات استخدام الكابتشا خاصتك، سجّل النتائج بنفسك - مقطعان صغيران وملف JSON واحد، دون قاعدة بيانات.
1 · في معالج النموذج
<?php
// form-handler.php — your existing form handler.
require __DIR__ . '/veilguard.php';
// Your key pair from the integration section.
$siteKey = 'vg_YOUR_SITE_KEY';
$secret = 'vgs_YOUR_SECRET_KEY';
$result = vg_verify_token($_POST['veilguard-token'] ?? '', $secret, $siteKey);
// --- Statistics: one row per attempt, stored only on your server. ---
$file = __DIR__ . '/data/stats.json';
$rows = is_file($file) ? (json_decode((string) file_get_contents($file), true) ?: []) : [];
$rows[] = array(
't' => gmdate('c'), // when
'v' => $result['success'] ? 'human' : 'bot', // verdict
's' => (int) ($result['score'] ?? 0), // score
);
@mkdir(dirname($file), 0755, true);
file_put_contents($file, json_encode($rows), LOCK_EX);
if (!$result['success']) {
http_response_code(403);
exit($result['reason'] ?? 'Bot detected');
}
// Human confirmed — continue with your logic here.
2 · stats.php - صفحة مستقلة
<?php
// stats.php — your VeilGuard statistics as a standalone page.
$file = __DIR__ . '/data/stats.json';
$rows = is_file($file) ? (json_decode((string) file_get_contents($file), true) ?: []) : [];
// 14-day skeleton + totals.
$days = array();
for ($i = 13; $i >= 0; $i--) {
$days[gmdate('Y-m-d', time() - $i * 86400)] = array('h' => 0, 'b' => 0);
}
$humans = 0;
foreach ($rows as $r) {
if (!is_array($r)) continue;
$human = ($r['v'] ?? '') === 'human';
if ($human) $humans++;
$day = substr((string) ($r['t'] ?? ''), 0, 10);
if (isset($days[$day])) $days[$day][$human ? 'h' : 'b']++;
}
$total = count($rows);
$bots = $total - $humans;
$today = $days[gmdate('Y-m-d')]['h'] + $days[gmdate('Y-m-d')]['b'];
$max = 1;
foreach ($days as $d) { $max = max($max, $d['h'] + $d['b']); }
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>VeilGuard stats</title>
<style>
body { margin: 0; padding: 2rem 1rem; font: 14px/1.5 system-ui, sans-serif;
background: #fafaf9; color: #1c1917; }
.card { max-width: 26rem; margin: 0 auto; border: 1px solid #e7e5e4;
border-radius: .75rem; padding: 1.25rem; background: #fff; }
.nums { display: grid; grid-template-columns: repeat(4, 1fr); gap: .75rem; }
.nums b { display: block; font-size: 1.25rem; line-height: 1.75rem; }
.nums span { font-size: .75rem; opacity: .7; }
.chart { display: flex; align-items: flex-end; gap: 4px; height: 72px;
margin-top: 1rem; }
.chart i { flex: 1; display: flex; flex-direction: column;
justify-content: flex-end; gap: 2px; height: 100%; font-style: normal; }
.chart .h { display: block; border-radius: 2px; background: #10b981; }
.chart .b { display: block; border-radius: 2px; background: #ef4444; }
.legend { display: flex; gap: 1rem; margin-top: .5rem; font-size: .75rem; opacity: .8; }
@media (prefers-color-scheme: dark) {
body { background: #1c1917; color: #fafaf9; }
.card { background: inherit; border-color: #44403c; }
}
</style>
</head>
<body>
<div class="card">
<div class="nums">
<div><b><?= $total ?></b><span>verifications</span></div>
<div><b><?= $humans ?></b><span>humans</span></div>
<div><b><?= $bots ?></b><span>bots</span></div>
<div><b><?= $today ?></b><span>today</span></div>
</div>
<div class="chart" role="img" aria-label="verifications per day">
<?php foreach ($days as $d): ?>
<i>
<span class="h" style="height:<?= (int) round($d['h'] / $max * 68) ?>px"></span>
<span class="b" style="height:<?= (int) round($d['b'] / $max * 68) ?>px"></span>
</i>
<?php endforeach; ?>
</div>
<div class="legend">
<span><b style="color:#10b981">•</b> humans</span>
<span><b style="color:#ef4444">•</b> bots</span>
</div>
</div>
</body>
</html>
كيف يمكن أن يبدو
1248
عمليات التحقق
1173
بشر
75
بوتات
14
اليوم
بيانات تجريبية - بطاقتك ستعرض أرقامك الحقيقية.
ملف الإحصائيات يبقى على خادمك - وككل شيء في VeilGuard، لا يُرسل أي شيء. امنع الوصول عبر الويب إلى مجلد data/ (مثلًا عبر .htaccess) كما هو موضح في الحزمة.
- تحققات غير محدودة
- مواقع غير محدودة
- كل اللغات التسع
- تحقق ذاتي الاستضافة
- بلا تتبّع ولا ملفات تعريف ارتباط
- الاستخدام التجاري مسموح
الأسئلة الشائعة
تواصل معنا
أسئلة عن التكامل أو ملاحظات أو بوت تسلل عبر الحماية؟ راسلنا - هذا النموذج يطبّق ما يبشّر به VeilGuard: فهو محمي بكابتشا VeilGuard نفسها.
- نرد عادة خلال 48 ساعة
- تُخزَّن رسالتك على هذا الخادم فقط
- لا تتبّع ولا رسائل بريدية
تم إرسال الرسالة!
شكرًا لك! خُزِّنت رسالتك على هذا الخادم وستُقرأ قريبًا.