For English version, see README_en.md
جمهوری اسلامی با راهاندازی وبسایت جانفدا سعی کرد هواداران خود را بسیار گسترده نشان دهد. مقامات رسمی جمهوری اسلامی نظیر رئیسجمهور، روسای سایر قوا، صداوسیما و رسانههای حکومتی با استناد به آمار ثبتنامشدگان این وبسایت بارها ادعا کردهاند بیش از ۳۰ میلیون نفر در این وبسایت ثبتنام کرده، و آن را نشان از همراهی گستردهی مردم با جمهوری اسلامی دانستهاند. اما چند خطای ناشیانهی برنامهنویسی در این وبسایت، منجر به یک رسوایی بزرگ شد و آمار واقعی ثبتنامکنندگان را افشا کرد. در این مخزن، اسناد و شواهد این رسوایی ارائه میشود.
این وبسایت جهت نمایش تعدادی از پیام کاربران (که در این متن به اختصار آن را کامنت مینامیم) روی وبسایت از یک سرویس Ajax استفاده میکند. فراخوانی این سرویس با اجرای دستور زیر در ترمینال به راحتی قابل انجام است:
curl -s 'https://janfadaa.ir/ajax' --data-raw 'action=getComments' | jq
این سرویس، نخستین اشتباه برنامهنویسان این وبسایت است: هر رکورد دریافتی این سرویس، شناسهی رکوردهای ثبتشده در وبسایت را نشان میدهد. این شناسهها، اعداد صعودی با حداکثر مقدار (در حال حاضر) ۳,۹۱۳,۷۷۷ است. این وبسایت در همین لحظه ادعا میکند تعداد ثبتنامشدگان بالای ۳۰ میلیون نفر است. در زیر قسمتی از پاسخ نمونهی این سرویس مشاهده میشود (هر پاسخ شامل ۱۵ کامنت است که به عنوان مثال ۳ کامنت در پاسخ زیر ارائه میشود).
{
"data": {
"all": [
{
"id": 3913777,
"fullname": "",
"description": "هرکاری از دستم بر بیاد انجام میدهم برای کشورم",
"date": "۱۴۰۵/۰۲/۰۵,۱۹:۴۳"
},
{
"id": 3913775,
"fullname": "",
"description": "تا پای جان از این انقلاب دفاع خواهم کرد جانم فدای رهبر",
"date": "۱۴۰۵/۰۲/۰۵,۱۹:۴۳"
},
{
"id": 3913770,
"fullname": "",
"description": "من جان \n فدای کشور عزیزم ایران میشوم",
"date": "۱۴۰۵/۰۲/۰۵,۱۹:۴۳"
},
...
],
"next": 1777138019
},
"success": true
}
پس از آنکه این موضوع در یک توییت اینجانب برملا شد، رسانههای حکومتی تلاش کردند وانمود کنند که شناسهی مذکور، شناسهی کامنتها است و نه شناسهی ثبتنامکنندگان. بدین ترتیب آنها تلاش داشتند وانمود کنند تعداد رکوردهای ثبتنام در این وبسایت بالای ۳۰ میلیون و تعداد کامنتها نزدیک ۴ میلیون بودهاست. اما این رسانهها اطلاع نداشتند برنامهنویسان ناشی این وبسایت در کد شواهد کافی برای رد این ادعا گذاشتهاند:
ابتدا باید دقت کنیم که در این وبسایت فقط یک فرم ثبتنام وجود دارد که کاربر پس از درج نام کامل، شمارهی تلفن همراه، استان، شهر، تاریخ تولد، جنسیت و سطح تحصیلات (اکثر موارد به صورت اختیاری) میتواند به دلخواه خود یک کامنت (پیام کاربر) وارد کند. کامنت میتواند خالی یا حاوی یک متن کوتاه باشد.
اما کافی است در سورسکد این وبسایت، فایل website_snapshot/assets/js/public.js را ببینیم. هنگام ارسال درخواست ثبتنام، یک رکورد ثبتنام به صورت زیر به سرورهای این وبسایت ارسال میشود:
const data = {
action: 'verificationSms',
verificationCode: verificationCode,
mobile: mobile,
fullname: fullname,
state: state,
city: city,
birthday: birthday,
gender: gender,
education: education,
description: description,
formToken: formToken,
}حالا اگر عناصر موجود در رکورد ثبتنام را با عناصر رکورد خروجی سرویس Ajax مقایسه کنیم میبینیم عناصر مشترکی شامل fullname و description دارند. یعنی در واقع هنگامی که یک رکورد ثبتنام به وبسایت ارسال میشود، این وبسایت با اختصاص یک شناسه id و زمان ثبتنام date آن را وارد دادهپایگاه خود میکند. در واقع id شناسهی رکورد ثبتنام است، که از آنجا که یک کاربر میتواند بارها ثبتنام کند تعداد آن بیشتر از تعداد واقعی کاربران ثبتنام کننده است. اما موضوع به همینجا تمام نمیشود.
برای دریافت کل رکوردهای بازگشتدادهشده از سرویس Ajax میتوانید از اسکریپت سادهی crawler.py که توسط اینجانب با کمک Gemini/Antigravity نوشتهشده استفاده کنید. پیشنهاد میشود در صورت تمایل، بخش «جزئیات فنی دریافت رکوردها» را ملاحظه فرمائید.
تحلیل این رکوردها، حقایق جدیدی در مورد این رسوایی تاریخی مشخص میکند.
کل رکوردهای بازگشتی وبسایت مذکور تا لحظهی تنظیم این سند تنها ۱۶۵۴ مورد است که در فایل janfadaa_comments_chain.jsonl موجود است. پنهانکاری سایت در نمایش ندادن فهرست کامل ثبتنامیها نشاندهنده بیاعتباری کل ادعاهای این وبسایت است.
قدیمیترین کامنت با شناسه 117 (۸ فروردین ۱۴۰۵) و کامنت بعدی با شناسه 1803827 (۱۹ فروردین) ثبت شده است. در این فاصله ۱۱ روزه، هیچ کامنت قابل دسترسی دیگری توسط سرویس فوق وجود ندارد.
اگر به فواصل نامنظم کامنتهای بازگشتی از سرویس دقت کنیم (بعضی اوقات شناسههای کاملا متوالی و زمانهای پیاپی و بعضی اوقات پرشهای بسیار بزرگ در شناسهها و زمان کامنتهای بازگشتی حتی در یک بلوک ۱۵ پیامی) متوجه میشویم تعداد بسیار کمی از کامنتها توسط گردانندگان این وبسایت برای نمایش دستچین شدهاند. اما این نکته چه اهمیتی دارد؟ پاسخ در بخش بعد است.
در همین ۱۶۵۴ کامنت قابل دسترسی، پیامهایی وجود دارد که ۲ یا چندین مرتبه تکرار شدهاند. تکراری بودن پیامهایی همچون «جانم فدای ایران» توسط کاربران مختلف قابل انتظار است و منجر به یافتهی خاصی نمیشود. ولی موضوع زمانی جالب میشود که ببینیم بعضی پیامها نظیر مورد زیر شامل متن طولانی و غیر متداول قطعا توسط یک کاربر درج شدهاند:
جانم فدای رهبرم سید مجتبی خامنه ای\nلبنان نباید تنها بماند\nایران بامعرفت همانطور که لبنان در کنارش ایستاد باید در کنارش بماند
این کامنت در دو روز ۲۱ و ۲۲ فروردین ثبت شدهاست:
- ۱۴۰۵/۰۱/۲۱,۲۰:۲۵,
id=2277832 - ۱۴۰۵/۰۱/۲۲,۱۴:۰۸,
id=2494010
با توجه به متن طولانی و غیرمعمول این کامنت قطعا توسط یک نفر ثبت شدهاست. تا اینجا ممکن است موضوع خیلی عجیب نباشد و بگوییم یک کاربر در دو روز مختلف یک متن را کپی و ثبتنام کرده. اما نکتهی کلیدی آن است که با وجود تعداد و درصد بسیار کم کامنتهای بازگشتی وبسایت (تنها ۱۶۵۴ مورد) و فاصلهی بسیار بین دو شناسهی ثبت شده (بیش از ۲۰۰ هزار واحد) اینکه دو کامنت کاملا یکسان با فاصلهی بسیار زیاد توسط گردانندگان وبسایت دستچین شدهاند فقط یک احتمال باقی میگذارد: حجم ثبتنامهای تکراری بسیار بسیار زیاد و احتمالا سیستمی بودهاست طوری که گردانندگان وبسایت با وجود دستچینکردن تعداد خیلی کمی از کامنتها نتوانستهاند جلوی افشای این رسوایی عظیم دیگر را بگیرند.
نمونههای دیگری از کامنتهای کاملا تکراری با الگوی طولانی و غیر معمول در همین مجموعهی کوچک دستچینشده عبارتند از:
میان دشمن و وطن ننگ بر آنکه شک کند \nننگ بر آنکه خواسته شمر به ما کمک کند\nجانم فدای ایران🇮🇷♥️
من به عنوان یک شهروند ایرانی حاضرم جانم را فدای وطنم کنم.
اسناد این رسوایی بزرگ و تاریخی به همین موارد خلاصه نمیشود. بزرگترین سند آن، عدم شفافیت وبسایت است. اگر قرار بود این آمارها حقیقت داشته باشد، مسوولان وبسایت از ابتدا رکوردهای بدون هویت (با حذف نام، تاریخ دقیق تولد و شماره تلفن همراه) را منتشر میکردند.
به نظرم این رسوایی تاریخی ریشه در موارد متعددی دارد. مثلا مسوولان جمهوری اسلامی، مردم را مثل خود دارای هوش کم میدانند و گمان میکنند با تکرار هرروز ادعاهای نادرست و خلا اطلاعاتی ناشی از قطع اینترنت، میتواند به آنها تلقین کنند ۳۰ میلیون هوادار دارند. حال آنکه مردم ایران بسیار باهوشتر از مسوولان جمهوری اسلامی هستند و به لطف شبکههای اجتماعی، بسیار سریع از واقعیتها مطلع میشوند.
مثلا این وبسایت ادعا میکند فقط از افراد بالای ۱۲ سال ثبتنام میکند. جمعیت بالای ۱۲ سال ایران حدود ۷۰ تا ۷۳ میلیون نفر تخمین زده میشود که میلیونها نفر از آنها خارج از کشور هستند و عموما امکان ثبتنام در این وبسایت نداشتهاند. ادعای ۳۰ میلیون ثبتنامشده یعنی از هر دو نفر ایرانی بالای ۱۲ سال ساکن کشور، یک نفر در آن ثبتنام کردهاند. طراحان این حرکت حتی تصور نکردهاند هرکسی میتواند با یک پرس و جوی ساده از همشهریان خود متوجه شود واقعا چند درصد از مردم در این وبسایت ثبتنام کردهاند.
کافی است نمودار رشد تقریبا خطی آمارهای اعلام شده توسط مسوولان (دستکم تا عدد ۲۶ میلیون نفر، و پس از آن قدری الگوی رشد را کاهش دادند) را ببینید. چه بسا اگر کسی ترمز آنها را نکشیده بود، چهبسا به زودی اعلام میکردند ۱۰۰ میلیون ایرانی در این وبسایت ثبتنام کردهاند. همهی این رسوایی ناشی از یک حقیقت تلخ است: مسوولان جمهوری اسلامی عدد نمیفهمند.
برای بازتولید این نتایج، کافی است crawler.py را اجرا کنید. پیشنهاد میشود جزئیات فنی زیر را مطالعه فرمائید.
- امنیت: اگر تمایل دارید IP شما در این وبسایت که متعلق به رژیم جمهوری اسلامی ایران است ثبت نشود، پیشنهاد میشود حتما از پراکسی یا VPN (مثلا tor) استفاده کنید. کافی است در ابتدای
crawler.pyمقدار متغیرUSE_PROXYرا بهTrueتغییر داده و پورت پراکسی را در ابتدای اسکریپت مشخص کنید.
میتوان از سرویس Ajax فوق استفاده کرد و با تغییر مقدار pointer کل رکوردهایی که این سرویس برمیگرداند را ذخیره کرد. برای این کار کافی است مقدار next قرارگرفته در یک رکورد بازگشتی را به عنوان پوینتر درخواست بعدی به سرویس بیاوریم. مثلا:
curl -s 'https://janfadaa.ir/ajax' --data-raw 'action=getComments&pointer=1777138019' | jq
که در این درخواست، مقدار 1777138019 برابر مقدار next بازگشتی از پاسخ درخواست قبلی است. اینجا البته یک اشکال فنی کوچک وجود دارد: برنامهنویسان ناشی وبسایت جانفدا سرویس Ajax را طوری نوشتهاند که برای بعضی مقادیر واقعی pointer هیچ پاسخی برنمیگرداند. این چالش را با اندکی تفکر میتوان حل کرد.
مشاهدهی اصلی آن است که مقادیر pointer همواره نزولی هستند ولی در نگاه اول نمیتوان بین آنها الگوی دقیقی یافت. برای حل چالش فوق، میتوان هرگاه به یک pointer بنبست رسیدیم، مقدار آن را به اندازهی ثابتی (مثلا ۱۰۰ واحد، با توجه به سایر مقادیر) کسر کنیم و دوباره تلاش کنیم. در این حالت، حتی اگر مقدار pointer معتبر نباشد، سرویس آن را به نزدیکترین مقدار معتبر نگاشت کرده و خروجی آن رکورد را برمیگرداند.
با اجرای این برنامه ۱۴ مقدار pointer که در فایل broken_pointers.txt مشخص شدهاند و معادل ۱۴ بلوک، هریک به اندازهی ۱۵ کامنت (مجموعا ۲۱۰ کامنت کاربران) هستند به بنبست میخورند. هزار بار تلاش برای دریافت آنها (در اسکریپت broken_pointers_retry.py) نتیجهای ندارد. هرچند بهرهمند بودن از این ۲۱۰ کامنت اضافه تغییری در نتایج ایجاد نمیکند.