انتقل إلى المحتوى
أدلة API

واجهة API لكشف احتيال IP: قيّم عمليات التسجيل والدخول والدفع في Node وPython

أضف واجهة API لكشف احتيال IP إلى التسجيل والدخول والدفع: شيفرة Node وPython شغّلناها على ست عناوين IP عامة، مع المخرجات الحقيقية والتخزين المؤقت وحدود المعدّل.

بقلمفريق GeoIPHubتاريخ النشرتاريخ التحديثمدة القراءةقراءة في 13 دقيقة
نافذة تطبيق يتصل بها كابل متوهج من عقدة ساطعة، تمثّل إضافة كشف احتيال IP بطلب API واحد

يصل تسجيلان بفارق ثانية، وكلاهما يُخفي عنوان منزل صاحبه. أحدهما من عقدة خروج Tor في هامبورغ. والآخر من iCloud Private Relay، الذي تصفه Apple بأنه "جزء من اشتراك iCloud+" يحميك "عند تصفح الويب في Safari" (دعم Apple). القاعدة التي تحظر "كل ما هو مخفي" سترفض الاثنين، وأحدهما عميل دافع لدى Apple.

التمييز بين هذين هو المهمة. وهو شريحة واحدة من نطاق ما يغطيه ذكاء عناوين IP، ويتّسع له ملف واحد من أقل من 50 سطرًا. ستجد أدناه الطلب، واستجابة حقيقية واحدة، ونسختي Node وPython مع التخزين المؤقت ومعالجة الأعطال، وما طبعتاه لست عناوين، ومواضع قصور الفحص.

ساعة إيقاف بجانب نافذة تطبيق تمتلئ ببطاقة استجابة، تمثّل التكامل في دقائق
طلب واحد، استجابة واحدة، كل الحقول.

الطلب الواحد الذي تحتاجه واجهة API لكشف احتيال IP

التكامل كله طلب GET واحد مع عنوان IP في المسار ومفتاحك في ترويسة x-api-key. يوثّق مرجع API الشكل نفسه:

GeoIPHub API
curl https://api.geoiphub.com/v1/lookup/45.83.220.7 \ -H "x-api-key: YOUR_API_KEY"

ثلاثة تفاصيل تُوقع الناس في المحاولة الأولى:

  • عنوان IP يوضع في المسار. /v1/lookup/{ip} هو الشكل الوحيد. نسخة سلسلة الاستعلام تُعيد التوجيه بدل إعادة البيانات.
  • لا ترسل العنصر النائب. أرسلنا النص الحرفي YOUR_API_KEY فحصلنا على 401 مع {"message":"Invalid API key"}. اترك الترويسة إلى أن تحصل على مفتاح حقيقي؛ فالاستدعاءات المجهولة تعمل بمعدل 30 في الدقيقة.
  • عنوان IP واحد لكل استدعاء. يأخذ كل طلب عنوانًا واحدًا، فالسكربت الذي يفحص قائمة يرسل طلبًا لكل عنوان ويحتسب كل طلب ضمن حدّك.

أنشئ مفتاحًا مجانيًا في لوحة التحكم حين تتجاوز مرحلة الاختبار. تمنح الخطة المجانية ذلك المفتاح 1,500 استعلام يوميًا، مع تضمين جميع الحقول.

ما الذي عاد لعنوان Mullvad، وأي الحقول نتفرّع عليها

الاستجابة نحو 2.7 كيلوبايت من JSON المتداخل في عشر مجموعات، ويحتاج فحص الاحتيال إلى مجموعتين منها: detection وscoring. هذا هو الاستعلام عن 45.83.220.7، مقتطعًا لكن بقيم لم تُمسّ:

GeoIPHub API
{ "ip": "45.83.220.7", "asn": { "asn": 39351, "org": "ESAB-AS - 31173 Services AB", "asn_type": "hosting", "connection_type": "datacenter" }, "geo": { "country_code": "SE", "city": "Malmö" }, "detection": { "is_vpn": true, "is_proxy": false, "is_tor": false, "is_hosting": true, "is_relay": false, "vpn_provider": "Mullvad (31173 Services)", "vpn_confidence": 85 }, "scoring": { "fraud_score": 46, "confidence": 0.56, "recommended_action": "review", "detection_methods": [ "known_vpn_server", "datacenter_ip" ] }, "meta": { "last_classified_at": "2026-10-16T07:40:17.524Z" } }

هذا العنوان تابع لـ 31173 Services، الشبكة التي يشغّل عليها Mullvad VPN خوادمه، في مالمو. وحين فحصناه في 13 أكتوبر لم يكن في قائمة خوادم Mullvad المنشورة. انطلقت إشارتان. جاءت known_vpn_server من جدولنا لشبكات استضافة VPN المعروفة، ولهذا فإن vpn_confidence تساوي 85 لا 95 التي تعطيها مطابقة قائمة خوادم Mullvad نفسها. وتقول datacenter_ip إنه استضافة لا اتصال منزلي. معًا تعطيان درجة 46، تقع في review: فالـ VPN وحده ليس احتيالًا.

ثلاثة حقول تحمل القرار:

الحقلما يحتويهكيف تستخدمه الشيفرة
scoring.recommended_actionallow (الدرجة حتى 25)، review (حتى 50)، step_up (حتى 75)، block (فوق 75)التفرّع
scoring.fraud_scoreالدرجة المركّبة من 0 إلى 100 خلف ذلك الإجراءالتسجيل والضبط لاحقًا
scoring.detection_methodsالإشارات التي انطلقت، مثل tor_exit أو openvpn_openالسبب الذي تخزّنه وتعرضه على المراجعين

علامات detection (is_vpn وis_proxy وis_tor وis_hosting وis_relay) هي الحقائق الخام خلف الدرجة. استخدمها في القواعد القليلة التي يختلف فيها عملك عن النطاقات الافتراضية. وللدرجة أيضًا إشارات تبرئة: ISP سكني يخصم 20 نقطة وزاحف موثَّق يخصم 30، فلا تدفع علامة وحدها عنوانًا إلى block. يشرح دليل درجة احتيال IP ما الذي يحرّك الرقم.

نسخة Node: fetch وتخزين مؤقت لمدة ساعة وأربعة فروع

هذا هو الملف نفسه الذي شغّلناه، على Node 24 دون أي اعتماديات. يحتاج إلى Node 18 أو أحدث لوجود fetch المدمج:

GeoIPHub API
// screen.mjs: score an IP before a signup, login or checkout. Node 18+, no dependencies. const API = "https://api.geoiphub.com/v1/lookup/"; const KEY = process.env.GEOIPHUB_API_KEY; // without a key: 30 lookups a minute, for testing only const TTL_MS = 60 * 60 * 1000; // reuse a verdict for an hour const cache = new Map(); // ip -> { at, data } async function lookup(ip) { const hit = cache.get(ip); if (hit && Date.now() - hit.at < TTL_MS) return hit.data; const headers = KEY ? { "x-api-key": KEY } : {}; const res = await fetch(API + encodeURIComponent(ip), { headers, signal: AbortSignal.timeout(3000), }); if (res.status === 429) throw new Error(`rate limited, retry after ${res.headers.get("retry-after")}s`); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); cache.set(ip, { at: Date.now(), data }); return data; } export async function screen(ip, flow) { let data; try { data = await lookup(ip); } catch (err) { return { action: "review", reason: `lookup failed: ${err.message}` }; // fail soft, never block on our outage } const { scoring, detection } = data; let action = scoring.recommended_action; // "allow" | "review" | "step_up" | "block" if (detection.is_tor) action = "block"; // no step-up through a Tor exit if (flow === "checkout" && action === "review") action = "step_up"; // stricter where money moves const flags = ["is_vpn", "is_proxy", "is_tor", "is_hosting", "is_relay"].filter((f) => detection[f]); return { action, score: scoring.fraud_score, flags, methods: scoring.detection_methods }; } // Try it: node screen.mjs signup 45.33.32.156 104.28.46.99 const [flow, ...ips] = process.argv.slice(2); for (const ip of ips) { const v = await screen(ip, flow); console.log(ip.padEnd(15), v.action.padEnd(8), v.score ?? "-", v.flags?.join(",") || "-", v.methods?.join(",") || v.reason); }

أربعة قرارات فيه مقصودة:

  • يبدأ من recommended_action ويتجاوزه في موضعين فقط. عقدة خروج Tor تُحظر دائمًا، وعند الدفع يصبح review هو step_up، لأن هناك تخرج الأموال.
  • يفشل بليونة. المهلة أو خطأ 5xx أو 429 تعيد review لا block. إذا تعذّر الوصول إلى واجهتنا، تستمر عمليات التسجيل لديك وتُسجَّل لنظرة ثانية.
  • المهلة 3 ثوان. من جانبنا، يُجاب عن عنوان IP معروف من قاعدة بيانات معيَّنة في الذاكرة في أقل من ميلي ثانية، فيكون معظم الانتظار زمن ذهاب وإياب الشبكة. أما عنوان IP لم نره من قبل فيُصنَّف مباشرة في أقل من 2.5 ثانية، وتغطي 3 ثوان تلك الحالة.
  • يبقى المفتاح على خادمك. استدعِ الواجهة من الخادم الخلفي، وليس من شيفرة المتصفح أبدًا، حيث يستطيع أي شخص قراءة المفتاح.

نسخة Python تحتاج إلى User-Agent وإلا حصلت على 403

ترد واجهتنا على قيمة User-Agent الافتراضية لمكتبة Python urllib، وهي Python-urllib/3.x، بالرد 403 Forbidden. أعدنا إنتاج ذلك في 16 أكتوبر الساعة 05:36 UTC باستدعاء urllib.request.urlopen مجرد: HTTP Error 403: Forbidden. لا يواجه curl ولا requests هذه المشكلة لأنهما يرسلان قيم User-Agent مختلفة. لذلك يعيّن ملف Python قيمته الخاصة، مع تعليق يوضح السبب:

GeoIPHub API
# screen.py: score an IP before a signup, login or checkout. Python 3.8+, standard library only. import json, os, sys, time, urllib.error, urllib.request API = "https://api.geoiphub.com/v1/lookup/" KEY = os.environ.get("GEOIPHUB_API_KEY") # without a key: 30 lookups a minute, for testing only TTL = 3600 # reuse a verdict for an hour _cache = {} # ip -> (time, data) def lookup(ip): hit = _cache.get(ip) if hit and time.time() - hit[0] < TTL: return hit[1] # Set a User-Agent: our API answers urllib's default "Python-urllib/3.x" with 403 Forbidden. headers = {"User-Agent": "signup-screen/1.0"} if KEY: headers["x-api-key"] = KEY req = urllib.request.Request(API + ip, headers=headers) with urllib.request.urlopen(req, timeout=3) as resp: data = json.load(resp) _cache[ip] = (time.time(), data) return data def screen(ip, flow): try: data = lookup(ip) except urllib.error.HTTPError as e: wait = e.headers.get("Retry-After") if e.code == 429 else None return {"action": "review", "reason": f"HTTP {e.code}" + (f", retry after {wait}s" if wait else "")} except (OSError, ValueError) as e: # network error, timeout, bad JSON return {"action": "review", "reason": f"lookup failed: {e}"} # fail soft scoring, detection = data["scoring"], data["detection"] action = scoring["recommended_action"] # "allow" | "review" | "step_up" | "block" if detection["is_tor"]: action = "block" # no step-up through a Tor exit if flow == "checkout" and action == "review": action = "step_up" # stricter where money moves flags = [f for f in ("is_vpn", "is_proxy", "is_tor", "is_hosting", "is_relay") if detection[f]] return {"action": action, "score": scoring["fraud_score"], "flags": flags, "methods": scoring["detection_methods"]} if __name__ == "__main__": flow, ips = sys.argv[1], sys.argv[2:] for ip in ips: v = screen(ip, flow) print(f"{ip:<15} {v['action']:<8}", v.get("score", "-"), ",".join(v.get("flags", [])) or "-", ",".join(v.get("methods", [])) or v.get("reason", ""))

هو المنطق نفسه في ملف Node، باستخدام المكتبة القياسية فقط، وقد اختُبر على Python 3.9.6. تُطلق urllib الاستثناء HTTPError لأي حالة غير 2xx، ولذلك تُقرأ 429 وترويسة Retry-After من الاستثناء.

ما طبعه السكربتان لست عناوين عامة

أعطى السكربتان الحكم نفسه لكل عنوان. شغّلنا ملف Node في الساعة 07:39 UTC وملف Python في الساعة 07:40 UTC من 16 أكتوبر 2026، دون مفتاح، على ست عناوين عامة. الأمر الأخير يعيد تشغيل عنوان Mullvad بصفته عملية دفع:

GeoIPHub API
node screen.mjs signup 45.33.32.156 104.28.46.99 45.83.220.7 91.90.122.25 102.129.235.17 185.220.101.1 node screen.mjs checkout 45.83.220.7
GeoIPHub API
45.33.32.156 allow 10 is_hosting datacenter_ip 104.28.46.99 allow 9 is_relay relay_ip 45.83.220.7 review 46 is_vpn,is_hosting known_vpn_server,datacenter_ip 91.90.122.25 step_up 64 is_hosting datacenter_blocklisted,no_ptr_datacenter,known_spammer 102.129.235.17 block 92 is_vpn known_vpn_server,openvpn_open,ipsec_open 185.220.101.1 block 100 is_tor,is_hosting tor_exit,datacenter_blocklisted,no_ptr_datacenter,known_spammer 45.83.220.7 step_up 46 is_vpn,is_hosting known_vpn_server,datacenter_ip

أما python3 screen.py بالوسائط نفسها فطبع الأسطر السبعة نفسها حرفًا بحرف. وهذا ما هو كل عنوان ولماذا استقر حيث استقر:

العنوانما هوالإجراءالإشارات التي انطلقت
45.33.32.156scanme.nmap.org، مضيف الاختبار العام لـ Nmap على Linodeallow، 10datacenter_ip فقط. الاستضافة وحدها ليست احتيالًا
104.28.46.99مخرج iCloud Private Relay على Cloudflareallow، 9relay_ip. تضع الدرجة سقفًا قدره 60 لعناوين الترحيل، فلا تصل أبدًا إلى block
45.83.220.7شبكة Mullvad في مالموreview، 46؛ وstep_up عند الدفعknown_vpn_server وdatacenter_ip
91.90.122.25خادم PIA VPN في شان، ليختنشتاين، على M247step_up، 64datacenter_blocklisted وno_ptr_datacenter وknown_spammer. لم يُوسم كـ VPN
102.129.235.17خادم VPN تجاري في واشنطن العاصمةblock، 92known_vpn_server، إضافةً إلى openvpn_open وipsec_open: أكملت فحوصاتنا مصافحات OpenVPN وIPsec
185.220.101.1عقدة خروج Tor في هامبورغblock، 100tor_exit وdatacenter_blocklisted وno_ptr_datacenter وknown_spammer

استقر التسجيلان المذكوران في المقدمة على طرفي نقيض: عقدة خروج Tor عند 100 ومستخدم Private Relay عند 9. هذا التباين هو سبب التقييم بدل حظر كل عنوان مخفي.

تتحرك النتائج مع تغيّر بياناتنا. حتى إعادة الكتابة هذه، كان هذا المقال يعرض 45.83.220.7 كاستضافة عادية بدرجة 10 وallow. وبحلول 8 أكتوبر كان ماسحنا قد ربطه بـ Mullvad، وهو الآن يسجّل 46. سجّل الإجراء والطرق مع كل قرار، كي يظهر تغيّر كهذا في بياناتك بدل أن يفاجئك.

أين تستدعيه: كل تسجيل ودخول ودفع

قيّم كل تسجيل وكل دخول وكل دفع، على الخادم، قبل أن تُثبّت الإجراء. هذه هي اللحظات الثلاث التي يربح فيها المحتال شيئًا، وهي نادرة بما يكفي لكل مستخدم لتتسع لها ميزانية يومية. لا تستدعه عند كل عرض صفحة.

ما يعنيه كل إجراء عمليًا:

الإجراءالتسجيلالدخولالدفع
allowأنشئ الحسابسجّل الدخولاقبل الدفعة
reviewأنشئه وعلّمه للمراجعةسجّل الدخول وسجّل السببتحوّله شيفرتنا إلى step_up
step_upتحقق من البريد أو الهاتف أولًااطلب العامل الثانيشغّل 3-D Secure
blockارفض برسالة محايدةارفض واحتسبه ضمن القفلارفض الطلب

النطاقات الافتراضية نقطة انطلاق، وينبغي أن تحرّكها بيانات الاحتيال الخاصة بك. يعرض دليلنا حدود درجة الاحتيال للدخول والتسجيل والدفع حدودًا ابتدائية لكل إجراء مع مبرراتها.

وقيّم العنوان الصحيح أيضًا. خلف موازن حمل أو CDN، يكون عنوان المقبس هو الوكيل لديك، ويوجد عنوان IP الخاص بالعميل في ترويسة إعادة توجيه يستطيع أي عميل تزويرها كذلك. خذه من القفزة التي أضافها وكيلك الموثوق، لا من القيمة الواقعة أقصى اليسار التي أرسلها العميل.

البقاء ضمن 1,500 استعلام يوميًا: التخزين المؤقت وحد المعدّل

تسمح الخطة المجانية بـ 1,500 استعلام يوميًا. خزّن مؤقتًا حسب IP في شيفرتك وستمتد الميزانية أبعد بكثير، لأن الشخص الواحد يسجّل ويدخل ويدفع عادةً من العنوان نفسه خلال ساعة.

لن تساعد الواجهة نفسها في التخزين المؤقت: كل استجابة تحمل Cache-Control: private, no-store، فلا تحتفظ بها أي ذاكرة HTTP مشتركة. يحتفظ السكربتان بالحكم ساعة في ذاكرة العملية. استخدم Redis أو ذاكرتك المؤقتة الحالية إن شغّلت أكثر من خادم. ولا تحتفظ بحكم أكثر من يوم، لأن مصادر التهديدات وقاعدة بيانات الاستعلام لدينا تتحدث يوميًا.

يختلف سلوك الحدّين، وقد اصطدمنا بأحدهما أثناء اختبار هذا المقال:

الاستدعاء بصفةالحدما يحدث بعده
بلا مفتاح30 استعلامًا في الدقيقة لكل عنوان IP للمتصلالطلب الثلاثون خلال دقيقة يعيد 429 ويحظر ذلك العنوان ساعتين
مفتاح مجاني1,500 استعلام يوميًا429 مع ترويسة Retry-After حتى يُعاد ضبط العدّاد اليومي

في الساعة 05:37 UTC من 16 أكتوبر أطلقنا دفعة من الطلبات المجهولة لرؤية 429. خلال عشرين ثانية أعادت الواجهة 429 مع Retry-After: 7199، وبقي كل استدعاء مجهول من عنواننا محظورًا ساعتين، بما في ذلك سكربتاتنا. مسار الفشل الليّن فيهما هو أول مخرجات حقيقية حصلنا عليها:

GeoIPHub API
45.33.32.156 review - - lookup failed: rate limited, retry after 6971s 45.33.32.156 review - - HTTP 429, retry after 6970s

لذا تعامل مع الحد المجهول كمسموح للاختبار، واستخدم مفتاحًا في الإنتاج. تفرّع حسب رمز الحالة وRetry-After، لا حسب نص رسالة 429.

أين يقصّر هذا الفحص

حكم IP إشارة واحدة، وتفوته أشياء. ظهرت أربع حالات أثناء كتابة هذا المقال:

  • قد يعود خادم VPN مدرج نظيفًا. كان خادم PIA 91.90.122.25 في قائمة PIA المنشورة في 4 أكتوبر، ومع ذلك تعيد واجهتنا is_vpn: false. لم يصل إلى step_up إلا لأنه في قائمة حظر DNS. ولولا ذلك التاريخ لمرّ كاستضافة عادية.
  • البروكسيات السكنية غير مرئية في معظمها لبيانات IP. الحركة الممرَّرة عبر اتصالات منزلية حقيقية تبدو كمستخدم منزلي. الحقل is_residential_proxy استدلال تقريبي من الشبكة وليس كشفًا، فلا تبنِ عليه قاعدة.
  • العناوين المشتركة تحمل تاريخ آخرين. تضع شركات الجوال وCGNAT مستخدمين كثيرين خلف عنوان واحد، ولهذا تضع الدرجة سقفًا لنطاقات CGNAT ولهذا ينبغي أن يبقى block نادرًا هناك.
  • مواقع الترحيل تقريبية. بالنسبة لـ 104.28.46.99 يقول تحديد الموقع لدينا إنه لواندا، أنغولا، بينما تضع قائمة مخارج Apple نفسها ذلك النطاق في لوساكا، زامبيا. استخدم علامة الترحيل لا المدينة في القرارات، كما يشرح دليلنا لتمرير Private Relay.

اقرن فحص IP بما تعرفه عن الحساب: عمر البريد والجهاز وتاريخ الدفع والسرعة. يبيّن دليل منظومة احتيال الدفع موقع إشارات IP بينها.

جرّبه على حركتك الخاصة أولًا

قبل ربطه بمسار حي، شغّل أيًّا من السكربتين على يوم من عناوين IP للتسجيلات السابقة بمفتاح مجاني، وقارن إجراءاته بما تعرفه أصلًا عن تلك الحسابات. إذا كان block سيلتقط حسابات حظرتها لاحقًا، وكان allow يغطي العملاء الذين أبقيتهم، فضعه أمام التسجيل أولًا، ثم الدخول، ثم الدفع. ولفحص عنوان واحد يدويًا، تعرض أداة فحص درجة احتيال IP الدرجة والأسباب نفسها دون أي شيفرة.

الأسئلة الشائعة

هل واجهة GeoIPHub API مجانية للبدء؟

نعم. تشمل الخطة المجانية 1,500 استعلام يوميًا مع جميع الحقول، ومفتاح API واحدًا، دون بطاقة ائتمان. وبدون مفتاح يمكنك إجراء 30 استعلامًا في الدقيقة للاختبار.

أي حقل ينبغي أن تتفرّع عليه شيفرتي؟

scoring.recommended_action. قيمته allow حتى درجة 25، وreview حتى 50، وstep_up حتى 75، وblock فوق 75. اقرأ scoring.detection_methods لتسجيل السبب.

لماذا يحصل سكربت Python لديّ على 403؟

ترد واجهتنا على User-Agent الافتراضي لمكتبة urllib، وهو Python-urllib/3.x، بالرد 403 Forbidden. عيّن ترويسة User-Agent خاصة بك وسيعيد الطلب نفسه 200.

هل يمكنني تخزين الاستجابة مؤقتًا؟

نعم، في شيفرتك أنت. ترسل الواجهة Cache-Control: private, no-store، ولذلك لن تحتفظ بها ذواكر HTTP المشتركة. نحن نعيد استخدام الحكم لمدة ساعة لكل عنوان IP.

هل سيحظر عملائي الحقيقيين؟

لا، إذا استخدمت الإجراءات الأربعة كما صُمّمت. عاد عنوان iCloud Private Relay بالنتيجة allow بدرجة 9 في تجربتنا، وعاد خادم سحابي عادي بـ allow عند 10. فقط عقدة خروج Tor وخادم VPN مؤكَّد بالفحص وصلا إلى block.

هل أحتاج إلى إرسال بيانات شخصية؟

لا. ترسل عنوان IP وتحصل على معلومات استخبارية عن ذلك العنوان. تبقى الأسماء والبريد الإلكتروني وبيانات البطاقة داخل نظامك.

بقلم

فريق GeoIPHub

الفريق الذي يقف وراء واجهة GeoIPHub API

بقلم فريق GeoIPHub، وهم من يبنون واجهة GeoIPHub API لاستخبارات IP وماسحها. تُعَدّ المسودات بمساعدة الذكاء الاصطناعي ويحررها الفريق، وأمثلة API استعلامات حقيقية على واجهة API الحية ما لم تُوصف بأنها توضيحية.

شارك هذا المقال
مزيد من المقالات

مقالات ذات صلة