جميع الأنظمة تعمل بكفاءة 6 منطقة خارجية دفع دون KYC
Hands-on الدليل الميداني

سجلّات الخادم على VPS: البيانات التي قرّرتَ ألّا تحتفظ بها

نقلتَ الجهاز إلى الخارج، ودفعت بعملة Monero، ووضعت كل شيء خلف نفق. ثم كتب nginx سطرًا واحدًا. كل طلب يُجيب عنه خادمك يترك سجلًّا مؤرَّخًا بمن سأل، ومن أين، وعن ماذا — وعلى صورة قياسية (stock image) يبقى ذلك السجلّ أسبوعين في /var/log، وشهرًا في قاعدة بيانات fail2ban، وبقدر ما تحتفظ به نسخك الاحتياطية، وهي عادةً مدة أطول من كليهما. يتّجه هذا الدليل نحو الجهاز لا الشبكة: أن تقرّر بنفسك ماذا يكتب خادمك، وأن تُقلِّم العنوان قبل بناء السطر بدلاً من تنظيفه لاحقًا، وأن تحتفظ بما يكفي لحجب مهاجم وتصحيح خطأ 500 دون أن تُراكم تاريخًا لزوّارك تفضّل ألّا تكون محتفظًا به.

حُدِّث في 2026-09-15 · قراءة 15 دقيقة · عمليات الأسطول
في هذه الصفحة
  1. الطلب الذي أجبتَ عنه صار الآن سجلًّا تحتفظ به
  2. ستة سجلّات، اثنان منها يُسمّيان الأشخاص
  3. قلِّم وقت الكتابة، لا وقت التدوير
  4. ما يكسره التقليم، والمهامّ الثلاث التي يؤدّيها السجلّ فعلاً
  5. journald وauth.log، والأثر الذي يقود إليك أنت
  6. سياسة احتفاظك هي أيًّا كان ما تقوله نسخك الاحتياطية
  7. النُّسَخ التي لا تتحكّم بها
  8. التقليل بالتصميم، لا الحذف عند الإشعار
  9. خطوة بخطوة
SP·01

الطلب الذي أجبتَ عنه صار الآن سجلًّا تحتفظ به

كل ما عدا هذا في هذه السلسلة يتّجه إلى الخارج: أخفِ المصدر، وشفِّر القرص، واختر الولاية القضائية، وامنع شخصًا آخر من الاحتفاظ بسجلّ عنك. أما هذا الدليل فيتّجه نحو الجهاز نفسه، لأن الخادم الذي يُجيب عن الطلبات يكتب مَن أرسلها — بشكل افتراضي، في عدّة أماكن في آنٍ واحد، بمدة احتفاظ لم يخترها أحد.

انظر إلى ما تحتفظ به صورة قياسية (stock image) بعد أسبوعين من النشر. يحتوي /var/log/nginx/access.log على سطر واحد لكل طلب: العنوان، والطابع الزمني، والمسار، والمُحيل (referrer)، وسلسلة وكيل المستخدم، يُدوَّر يوميًا ويُحتفَظ به أربعة عشر يومًا. يحتوي /var/log/auth.log على كل جلسة SSH، بما في ذلك عنوان المصدر لكل جلسة من جلساتك أنت. ويحمل سجلّ journald الأحداث نفسها مجدَّدًا، مضافًا إليها كل ما طبعته خدماتك إلى stderr. وإذا كان Docker يعمل، فلكل حاوية ملفّ سجلّ بصيغة JSON لا يحمل، في وضعه الافتراضي، أي حدّ لحجمه على الإطلاق. وإذا كان fail2ban يعمل، فله سجلّ بكل عنوان حجبه على الإطلاق، وقاعدة بيانات SQLite تقول الشيء نفسه، والعناوين في كليهما كاملة.

لا شيء من ذلك ضارّ بذاته، ومعظمه مفيد فعلاً — لمدة ساعة تقريبًا بعد أن يحدث خطأ ما. المشكلة في الشكل: دقّة كاملة، محفوظة لمدة طويلة، بالمصادفة لا بالقرار. السؤال الذي يستحق أن تطرحه عن كل واحد من هذه الملفّات ليس "هل هذا حسّاس؟" بل "ماذا كنتُ سأفعل فعلاً بالسطر الذي كتبته قبل ثلاثة أسابيع؟" بالنسبة للغالبية الساحقة من الأسطر على الغالبية الساحقة من الخوادم، الجواب هو لا شيء، والسجلّ الذي لن تقرأه أبدًا مسؤوليةٌ خالصة — تجاه اختراق، وتجاه نسخة احتياطية تعيش أطول من الجهاز، وتجاه أيًّا كان مَن سيطلبه يومًا ما.

تقليل البيانات هو الاسم الرسمي لهذا الحل، وهو أقلّ الأفكار إثارةً للجدل في حماية البيانات: اجمع ما تحتاجه المهمّة، واحتفظ به طالما احتاجته المهمّة، ثم توقّف. وما يلي يُطبِّق ذلك على جهاز تُشغّله فعلاً، على افتراض أنك أجريتَ بالفعل الساعة الأولى بعد النشر وأن هناك ما يستحق الحماية على الجهاز.

SP·02

ستة سجلّات، اثنان منها يُسمّيان الأشخاص

قبل أن تغيّر أي شيء، اعرف الجرد. يكتب VPS صغير يعمل بـDebian أو Ubuntu ويُشغّل خدمة ويب عادةً ستة تدفّقات، وهي تتداخل أكثر مما يتوقّع الناس — فالحدث نفسه غالبًا ما ينتهي به المطاف في ثلاثة ملفّات بثلاث مدد احتفاظ مختلفة.

سجلّ nginx للوصول هو الذي يُسمّي زوّارك. سطر واحد لكل طلب، يحمل عنوان العميل، والمسار الدقيق، والمُحيل، وسلسلة وكيل مستخدم مفصّلة بما يكفي لتكون بصمة ضعيفة بمفردها. سجلّ nginx للأخطاء هو ما ينساه الناس: فهو يسجّل client: 203.0.113.9 عند كل مهلة upstream، وكل خطأ 403، وكل طلب مشوَّه — وخلافًا لسجلّ الوصول، صيغته ثابتة ولا يمكن تخصيصها بقالب.

سجلّ المصادقة/var/log/auth.log في عائلة Debian، و/var/log/secure في عائلة RHEL — يُسمّي أنت. كل سطر قبول بمفتاح عام (publickey) يحمل عنوان مصدرك وبصمة المفتاح الذي فتح الجلسة. أي شخص يقرأ شهرًا منه يعرف من أي الشبكات تُدير خادمك، وفي أي ساعات، وكم مفتاحًا مختلفًا تملك. وعلى جهاز مملوك بهوية مجهولة، غالبًا ما يكون ذلك الملفّ أكثر كشفًا من أي شيء ولّده زوّارك.

سجلّ journald يحمل نسخة من معظم ما سبق، مضافًا إليه المخرجان القياسيان stdout وstderr لكل وحدة. وفي التثبيت الافتراضي، يُسمح له بالنموّ حتى 10% من نظام الملفّات، بحدّ أقصى 4 GB، قبل أن يبدأ بإسقاط أقدم المُدخلات. وعلى أي قرص بسعة 40 GB أو أكثر، ذلك السقف هو الـ4 GB كاملة، وهي عند الأحجام التي يُنتجها خادم صغير أشهر عديدة.

سجلّات التطبيق هي الورقة المجهولة. إطار عمل في وضع debug يسجّل عناوين URL كاملة بما فيها سلاسل الاستعلام، وسلاسل الاستعلام تحمل بشكل روتيني رموز الجلسات، وروابط إعادة تعيين كلمة المرور، وعبارات البحث. ويمكن ضبط PHP-FPM ليكتب سجلّ وصول خاصًا به، يُكرِّر سطر الطلب الذي كتبه nginx بالفعل، من ملفّ لا تلمسه إعدادات nginx إطلاقًا.

سجلّات الحاويات هي الأكثر هدوءًا. مُشغِّل json-file الافتراضي في Docker لا يُدوِّر شيئًا ما لم تضبطه بنفسك، لذا يحتفظ /var/lib/docker/containers/*/*-json.log بكل ما قالته الحاوية منذ إنشائها. وهذه طريقة شائعة لاكتشاف أن سياسة احتفاظ "14 يومًا" تحمل في الواقع أحد عشر شهرًا، وهي من فئة المفاجأة نفسها التي تخصّ قواعد جدار الحماية التي يكتبها Docker خلف ufw.

من بين الستة، اثنان يحملان مُعرِّفات عن بشر تستحق التقليل المتعمَّد: سجلّ nginx للوصول (زوّارك) وسجلّ المصادقة (أنت). أما البقية فتحتاج غالبًا إلى حدّ للحجم وساعة أقصر فحسب.

SP·03

قلِّم وقت الكتابة، لا وقت التدوير

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

التقليل الوحيد الذي يمكنك الاعتماد عليه فعلاً هو ما يحدث قبل كتابة السطر. في nginx، هذا يعني كتلة map تُقيَّم وقت الكتابة في السجلّ، تُعيد كتابة العنوان في متغيّر جديد، وlog_format يستخدم المتغيّر الجديد بدلاً من $remote_addr. العنوان الكامل لا يُسلسَل أبدًا. لا شيء لتنظيفه لاحقًا، ولا شيء لجدولته، ولا شيء يمكن أن يُخطئ في اليوم الذي لا تُراقب فيه.

# /etc/nginx/conf.d/00-privacy-log.conf  --  http context, loaded before the sites
map $remote_addr $ip_trunc {
    # IPv4: keep the /24, zero the host part
    ~(?<v4>\d+\.\d+\.\d+)\.     "${v4}.0";
    # IPv6: keep the first two groups, drop the rest
    ~(?<v6>[^:]+:[^:]+):          "${v6}::";
    # anything the two patterns cannot parse -- including compressed forms
    # like ::1 -- falls through here, i.e. fails closed rather than open
    default                       "0.0.0.0";
}

log_format privacy '$ip_trunc - - [$time_local] "$request" $status '
                   '$body_bytes_sent "$http_referer" "$http_user_agent" '
                   'rid=$request_id rt=$request_time';

ثلاثة تفاصيل تحسم ما إذا كان هذا يعمل فعلاً. أولًا، يجب أن تكون الـmap ضمن سياق http — فداخل كتلة server يرفض nginx البدء. ووضعها في conf.d/ باسم يُرتَّب أبجديًا مبكرًا هو أبسط طريقة للتأكد من ذلك.

ثانيًا، إذا كنتَ خلف Cloudflare أو أي حافة أخرى، تحقّق مما يحتويه $remote_addr. تستبدله وحدة realip بعنوان العميل الحقيقي في وقت مبكر من معالجة الطلب، وتحتفظ بعنوان الاتصال في $realip_remote_addr. وهذا الترتيب في صالحك: فالـmap تعمل وقت الكتابة في السجلّ، لذا تُقلِّم الزائر الحقيقي لا الحافة. لكن هذا يعني أيضًا أنك إن كنت لم تضبط realip، فأنت تُقلِّم عنوان Cloudflare ولا تتعلّم شيئًا، بينما يبقى العنوان الحقيقي في ترويسة.

ثالثًا، دقِّق بقية سلسلة الصيغة. تقليم $remote_addr لا يُحقّق شيئًا على الإطلاق إذا كان السطر لا يزال ينتهي بـ"$http_x_forwarded_for" أو يحمل $http_cf_connecting_ip — والعديد جدًا من الصيغ القياسية وصيغ لوحات التحكّم تتضمّن واحدًا من هذين. العنوان الكامل موجود في ترويسات الطلب؛ ولا يبقى خارج الملفّ إلا إذا تركتَ كل ترويسة تحمله خارج الصيغة.

لاحظ ما حلّ مكانه: $request_id، سلسلة عشوائية سداسية عشرية من 32 محرفًا يُولِّدها nginx لكل طلب. وهذا هو المحور الذي يقوم عليه القسم التالي.

SP·04

ما يكسره التقليم، والمهامّ الثلاث التي يؤدّيها السجلّ فعلاً

يعترض أحدهم دائمًا بأن السجلّات المُجهَّلة عديمة الفائدة، وهو مُحقّ جزئيًا — لأن "السجلّات" في الحقيقة ثلاث مهامّ غير مترابطة تلبس اسم ملفّ واحد، ومهمّة واحدة فقط منها تحتاج العنوان.

المهمّة الأولى: احجب مَن يُغرقك بالطلبات الآن. هذه تحتاج العنوان الكامل، وتحتاجه خلال ثوانٍ. لا تحتاجه غدًا. fail2ban هو الأداة المعتادة لهذا، وهو فعلاً لا يستطيع العمل من ملفّ مُقلَّم — فحجب 203.0.113.0 يحجب مُضيفًا بريئًا واحدًا ويترك المهاجم متّصلاً. لكن هذه المهمّة يكفيها ملفّ يعيش يومًا واحدًا، أو الاستغناء عن الملفّ كليًا: فـlimit_req وlimit_conn الخاصّان بـnginx يحتفظان بحالتهما في الذاكرة المشتركة، ويتصرّفان خلال ميكروثوانٍ بدلاً من فاصل فحص fail2ban الدوري، ولا يكتبان شيئًا على القرص.

المهمّة الثانية: معرفة سبب عودة ذلك الطلب بخطأ 500. هذه تحتاج ربطًا (correlation)، لا هوية. معرّف طلب (request ID) يمرّ من nginx إلى التطبيق يربط سطر الوصول، وخطأ upstream، وتتبّع مكدّس التطبيق (stack trace) بحدث واحد — وهذا هو ما كنتَ تحاول فعله فعلاً حين لجأتَ إلى العنوان. عمليًا، المعرّف أفضل: فهو ينجو من عميل على شبكة جوّالة يتغيّر عنوانه في منتصف الجلسة، ولا يفسد حين يتشارك أربعة زوّار عنوان CGNAT واحدًا.

المهمّة الثالثة: فهم حركة المرور عبر الزمن. الحجم، ومزيج رموز الحالة، وأي المسارات أكثر ازدحامًا، وهل الزاحف (crawler) خارج عن السيطرة. عنوان مُقلَّم مناسب تمامًا هنا، و/24 الذي ينجو من التقليم يكفي لمعرفة أن شبكة واحدة مسؤولة عن 40% من طلباتك.

إذن، التصميم ليس "سجِّل أقلّ" بل افصل بحسب الساعة: تدفّق بدقّة كاملة يعيش يومًا واحدًا ويُغذّي أدوات الحجب، وتدفّق مُقلَّم يعيش بقدر ما تريد للإحصاءات. يكتب nginx كليهما في اللحظة نفسها، فلا توجد خطوة معالجة بينهما ولا نافذة زمنية يبقى فيها الشيء الخطأ على القرص أطول مما ينبغي.

# inside the server block, or in a snippet included by it
access_log  /var/log/nginx/access.log       privacy;   # truncated, keep for weeks
access_log  /var/log/nginx/security.log     secip;     # full address, keep for a day

# static assets are pure noise in a privacy log -- drop them entirely
location ~* \.(?:css|js|svg|png|jpe?g|webp|woff2?|ico)$ {
    access_log off;
    expires 30d;
}

صيغة secip سطر واحد مقارنةً بالصيغة الأخرى — $remote_addr، والطابع الزمني، والطلب، والحالة، لا شيء آخر. إنه الملفّ الوحيد على الجهاز الذي يُسمح فيه لعنوان زائر كامل أن يستقرّ، وهذا يجعل مدة الاحتفاظ به قرارًا واحدًا في مكان واحد بدلاً من خاصية عليك التفكير فيها عبر ستة ملفّات.

SP·05

journald وauth.log، والأثر الذي يقود إليك أنت

خصوصية الزائر هي الجزء الذي يُكتَب عنه. أما أثر المسؤول فهو الجزء المهمّ على جهاز غايتُه كلّها أن يبقى اسمك غير مرتبط به، وهو يكمن كلّه تقريبًا في مكانين.

يسجّل /var/log/auth.log سطر Accepted publickey لكل جلسة تفتحها، مع عنوان مصدرك وبصمة مفتاحك. وعلى مدى شهر، هو جدول بعادات عملك وقائمة بالشبكات التي تستخدمها. إذا كنتَ تتصل دائمًا عبر النفق نفسه، فذلك عنوان واحد متكرّر ومُملّ نسبيًا. أما إذا كنتَ تتصل من أينما وجدتَ نفسك، فهو سجلّ رحلات.

يحمل سجلّ journald الأحداث نفسها مضافًا إليها كل ما طبعته وحداتك، وإعداداته الافتراضية سخيّة: SystemMaxUse= يساوي 10% من نظام الملفّات، وMaxRetentionSec= غير مضبوط إطلاقًا، ما يعني عدم وجود حدّ زمني على الإطلاق — بل حدّ للحجم فقط. وعلى خادم هادئ، يحتفظ هذا المزيج بأشهر.

هناك مقايضة حقيقية هنا وتستحق أن تُذكَر بصراحة لا أن يُلوَّح بها فحسب. السجلّات التي تصفك أنت هي نفسها السجلّات التي تُخبرك كيف دخل أحدهم. اضبط Storage=volatile ويعيش السجلّ في الذاكرة العشوائية (RAM) فقط، فيختفي عند إعادة التشغيل — خاص فعلاً، وعديم الفائدة فعلاً في الصباح الذي تجد فيه عملية لم تُشغّلها أنت، لأن أول إعادة تشغيل يجريها مهاجمك تمحو الدليل. وبالنسبة لمعظم الناس، الوسط المعقول هو تخزين دائم بحدّ أقصى صارم وساعة قصيرة: طويلة بما يكفي للتحقيق في حادثة تلاحظها خلال أسبوع، وقصيرة بما يكفي حتى لا يكون الملفّ مذكّرات يومية.

ملاحظتان تخصّان التنفيذ تُوقِعان الناس في الخطأ. تختلف صور Debian وUbuntu في ما إذا كان rsyslog مثبَّتًا؛ فإذا كان /var/log/auth.log موجودًا على جهازك فإن rsyslog هو من يكتبه، وحدود journald لا تحكم ذلك الملفّ على الإطلاق — بل هذا عمل logrotate. أما ForwardToSyslog= فهو ما يُغذّي rsyslog من السجلّ، لذا فإن تعطيله على جهاز يملك الاثنين معًا يمنعك من الاحتفاظ بنسختين من كل شيء تحت سياستَي احتفاظ مختلفتين.

SP·06

سياسة احتفاظك هي أيًّا كان ما تقوله نسخك الاحتياطية

هذه هي النقطة التي تنقض كل العمل الدقيق أعلاه، وهي غير مرئية ما لم تبحث عنها عمدًا.

لنقل إن logrotate يحتفظ بأربعة عشر يومًا من سجلّات nginx، وأنت راضٍ عن ذلك. أضِف الآن النسخة الاحتياطية خارج الموقع التي أصبتَ حين أعددتها: تشغيلة يومية بـBorg أو restic، بمدة احتفاظ تبلغ سبع أرشيفات يومية، وأربعة أسبوعية، وستة شهرية. كل واحد من تلك الأرشيفات يحتوي /var/log كما كان عليه يوم تشغيله. أقدم أرشيف شهري عمره ستة أشهر ويحمل الأربعة عشر يومًا من السجلّات التي كانت سارية حينها. مدة احتفاظك الفعلية بالسجلّات ليست أربعة عشر يومًا. إنها ستة أشهر، في مستودع مشفّر لا يمكنك تنفيذ grep عليه دون استعادته، على جهاز ثانٍ، في بلد مختلف.

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

# exclude logs from the backup, and prove it took
borg create --stats                       \
    --exclude '/var/log'                  \
    --exclude '/var/lib/docker/containers' \
    ::'{hostname}-{now:%Y-%m-%d}' /

# the check that matters: can you still find an address in the newest archive?
borg list ::"$(borg list --short --last 1)" | grep -c '^var/log/' || echo "clean"

وبما أنك في هذا الإطار الذهني، إليك أمرين آخرين قريبين من المشكلة نفسها. لقطات (snapshots) مزوّدك ليست لقطاتك أنت. تلتقط اللقطة على مستوى المُراقِب الوهمي (hypervisor) القرص كما كان، بما في ذلك سجلّات دوّرتها بعيدًا منذ ذلك الحين، وهي تعيش على تخزين المزوّد بموجب مدة احتفاظ المزوّد — وهذا سبب إضافي يؤكّد أن العناوين ما كان ينبغي أن تُكتَب كاملة من الأساس، لا سببًا لفعل شيء ذكي بعد وقوع الأمر.

ولا تلجأ إلى shred على VPS. الكتابة فوق ملفّ على قرص افتراضي رقيق التوفير (thin-provisioned)، فوق نظام ملفّات نسخ-عند-الكتابة (copy-on-write)، فوق قرص SSD يُعيد تخطيط الكتل لتوزيع البِلى (wear-levelling)، لا يكتب فوق الخلايا الفيزيائية التي حملته بشكل موثوق. الحذف الآمن على تخزين مُستأجَر ومُفترَض هو مسرحية لا أكثر. التقليل الذي ينجح هو ما فعلته وقت الكتابة؛ وكل ما يأتي بعد ذلك مجرّد محاولة لا يمكنك التحقّق منها. تشفير القرص يُغيّر هذه المعادلة — لكنه يُغيّرها قبل كتابة البيانات، وهو الدرس نفسه مجدَّدًا.

SP·07

النُّسَخ التي لا تتحكّم بها

التقليل على جهازك أنت طبقة واحدة من طبقات عدّة، والوضوح بشأن البقية هو ما يمنع هذا من أن يتحوّل إلى شعور زائف بالاكتمال.

شبكة استضافتك ترى سجلّ التدفّق (flow record). المصدر، والوجهة، والمنافذ، والبايتات، والتوقيت — لكل اتصال يدخل الجهاز ويخرج منه، سواء سجّلتَ شيئًا أم لا. لا يُغيّر أي إعداد على الخادم ذلك. وهذا جزء كبير من سبب كون الولاية القضائية التي يقع فيها الجهاز متغيّرًا حقيقيًا لا مسألة تسويقية؛ فـما تُلزَم به الشبكة قانونًا يختلف اختلافًا هائلاً من بلد إلى آخر.

شبكة توصيل المحتوى (CDN) أو الحافة الخاصة بك تُسجِّل عند الحافة. إذا كان Cloudflare يُنهي TLS نيابةً عنك، فهو يملك سطر الطلب وعنوان العميل قبل أن يتدخّل خادمك، بجدول احتفاظ خاص به، وخاضع لإجراءاته القانونية الخاصة. تقليم سجلّك عند المصدر (origin) لا يصل إليه رجوعًا. تشغيل حافة تملكها أنت هو النسخة من هذا التي تستطيع فعلاً ضبط إعداداتها — وقواعد التسجيل في هذا الدليل تنطبق على جهاز الحافة أولًا، لأنه المكان الذي تصل إليه العناوين غير المُقلَّمة.

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

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

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

SP·08

التقليل بالتصميم، لا الحذف عند الإشعار

يستحق الأمر دقّة هنا، لأن الاثنين كثيرًا ما يُخلَط بينهما، والفرق بينهما هو الفرق كلّه بين ممارسة هندسية معيارية وشيء لا ينبغي أن تفعله.

أن تقرّر مسبقًا ماذا تجمع خدمتك وإلى متى تحتفظ به هو ممارسة عادية وموثَّقة ومُشجَّع عليها. بموجب اللائحة العامة لحماية البيانات (GDPR)، هذان مبدآن من المبادئ الجوهرية — تقليل البيانات وتحديد مدة الاحتفاظ — ومدة احتفاظ أقصر بالسجلّات ضابطٌ يطلبه المدقّقون، لا ضابط يعترضون عليه. لا يوجد التزام عام على مُشغِّل موقع أو زبون استضافة في الاتحاد الأوروبي بالاحتفاظ بسجلّات حركة المرور؛ فالتوجيه القاضي بالاحتفاظ الشامل الذي أوحى يومًا بعكس ذلك أبطلته محكمة العدل الأوروبية عام 2014، والقوانين الوطنية التي نجت منه تُلزم في معظمها مزوّدي الاتصالات، لا مَن يُشغّل خادم ويب. والولايات المتحدة أيضًا ليس فيها تفويض عام بالاحتفاظ يخصّ مُشغِّلي المواقع.

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

الفرق نفسه يمتدّ عبر بقية هذا الموقع: هناك خطّ حقيقي بين هندسة الخصوصية وبين نهج "bulletproof" الذي يُسوِّق نفسه على أنه حصانة. وتصميم خدمة لا تُراكم أبدًا تاريخًا لزوّارها يقع براحة في الجانب الصحيح من ذلك الخطّ، في الخانة نفسها التي تضمّ تشفير أقراصك وعدم طلب عنوان بريد إلكتروني من المستخدمين لا تحتاجه.

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

SP·09

خطوة بخطوة

  1. 01

    اكتشف ما يحتفظ به الجهاز بالفعل

    لا تضبط أي شيء قبل أن تقيس. غاية هذه الجولة هي إيجاد الملفّ الذي نسيتَه، وهو في معظم الأجهزة إما سجلّ حاوية أو سجلّ تطبيق لم ينظر إليه أحد منذ النشر.

    # biggest log files anywhere on the box, largest last
    sudo du -ah /var/log /var/lib/docker/containers 2>/dev/null \
      | sort -h | tail -20
    
    # how much disk the journal holds, and how far back it goes
    journalctl --disk-usage
    journalctl --output=short-iso | head -1
    
    # how old is the oldest nginx line still on disk?
    zcat -f /var/log/nginx/access.log* 2>/dev/null | head -1

    ثم انظر إلى سطر واحد من كل ملفّ واسأل ماذا يُعرِّف. الأمر أدناه يعدّ كم عنوانًا كاملاً مختلفًا يمكن استرجاعه حاليًا من سجلّات الويب لديك — وهو عادةً الرقم الذي يُقيم الحجّة لبقية هذا الدليل.

    zcat -f /var/log/nginx/access.log* 2>/dev/null \
      | grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}' | sort -u | wc -l

    دوِّن ما تجده. ستُشغّل هذه الأوامر نفسها في النهاية لتُثبت أن التغيير أخذ مفعوله.

  2. 02

    قلِّم عنوان العميل قبل أن يكتب nginx السطر

    أنشئ الـmap والصيغتين في ملفّ يُحمَّل ضمن سياق http. في Debian وUbuntu، يُضمَّن /etc/nginx/conf.d/ من nginx.conf قبل إعدادات المواقع، وهو بالضبط المكان الذي ينتمي إليه هذا.

    sudo tee /etc/nginx/conf.d/00-privacy-log.conf > /dev/null <<'EOF'
    # The client address is reduced here, at log time, and never written in full
    # to the long-retention file. $realip_remote_addr still holds the connecting
    # address if you need it while debugging a proxy problem.
    map $remote_addr $ip_trunc {
        ~(?<v4>\d+\.\d+\.\d+)\.     "${v4}.0";
        ~(?<v6>[^:]+:[^:]+):          "${v6}::";
        # anything the two patterns cannot parse -- including compressed forms
        # like ::1 -- falls through here, i.e. fails closed rather than open
        default                       "0.0.0.0";
    }
    
    # Long retention: no full address, no forwarded-for header, request id instead.
    log_format privacy '$ip_trunc - - [$time_local] "$request" $status '
                       '$body_bytes_sent "$http_referer" "$http_user_agent" '
                       'rid=$request_id rt=$request_time';
    
    # Short retention: the one file allowed to hold a complete address.
    log_format secip   '$remote_addr [$time_local] "$request" $status';
    EOF
    
    sudo nginx -t && sudo systemctl reload nginx

    الآن وجِّه الموقع إليهما. في كتلة server الخاصة بك، استبدل سطر access_log الحالي بهذا الزوج، وأوقف التسجيل عن الأصول الثابتة (static assets) طالما أنت هناك.

    access_log  /var/log/nginx/access.log    privacy;
    access_log  /var/log/nginx/security.log  secip;

    أعد التحميل، وافتح صفحة، واقرأ النتيجة. يجب أن ينتهي الحقل الأول بـ.0، ويجب أن يحمل السطر قيمة rid=.

    sudo nginx -t && sudo systemctl reload nginx
    curl -s -o /dev/null https://your-domain.example/
    sudo tail -1 /var/log/nginx/access.log

    إذا كان العنوان لا يزال كاملاً، فإن كتلة server تتجاوز الصيغة في مكان ما أبعد — grep -rn access_log /etc/nginx/ يجد السطر الذي يفوز.

  3. 03

    احتفظ بالعناوين الكاملة فقط حيث يتصرّف شيء بناءً عليها

    إذا كان fail2ban يعمل، فهو يقرأ حاليًا الملفّ الذي قلَّمته للتوّ. وجِّهه بدلاً من ذلك إلى سجلّ الأمان، وامنح ذلك السجلّ عمر يوم واحد حتى تنتهي صلاحية العناوين الكاملة من تلقاء نفسها.

    sudo tee /etc/fail2ban/jail.d/nginx-privacy.local > /dev/null <<'EOF'
    [nginx-http-auth]
    enabled  = true
    logpath  = /var/log/nginx/error.log
    
    [nginx-botsearch]
    enabled  = true
    logpath  = /var/log/nginx/security.log
    maxretry = 6
    findtime = 10m
    bantime  = 1h
    EOF
    
    sudo fail2ban-client reload

    ثم تعامل مع ذاكرة fail2ban الخاصة، التي لا يلمسها معظم الناس أبدًا: فهو يحتفظ بسجلّه الخاص بعمليات الحجب تحت الإعداد الافتراضي rotate 4 weekly، وبقاعدة بيانات SQLite لكل حجب أصدره. dbpurgeage هو ما يُنهي صلاحية الصفوف في تلك القاعدة — اضبطه على قيمة قريبة من أطول مدة حجب لديك بدلاً من تركه على الافتراضي.

    sudo tee /etc/fail2ban/fail2ban.d/retention.local > /dev/null <<'EOF'
    [Definition]
    dbpurgeage = 2d
    loglevel   = NOTICE
    EOF
    
    sudo systemctl restart fail2ban

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

    # http context: keyed on the truncated address, so nothing complete is
    # held in memory either. 10m of shared state is plenty for a small site.
    limit_req_zone $ip_trunc zone=perip:10m rate=20r/s;
    
    # in the location you want protected
    limit_req zone=perip burst=40 nodelay;

    ربط المنطقة بمفتاح $ip_trunc بدلاً من $binary_remote_addr هو مقايضة متعمَّدة: فالحدّ الآن ينطبق على /24 كاملة دفعة واحدة، بحيث يتشارك مكتب مزدحم خلف كتلة واحدة ميزانيةً واحدة. لموقع صغير، هذا مناسب عادةً، بل يُعَدّ تحسينًا أحيانًا. أما إذا لم يكن كذلك، فاربط المنطقة بالعنوان الكامل — فحالة أداة تحديد المعدّل تعيش في الذاكرة المشتركة ولا تُكتَب على القرص أبدًا، لذا فهي ليست جزءًا مما تُقلِّله هنا.

  4. 04

    احصر سجلّ journald وأوقف النسخة الثانية

    يقبل journald ملفًّا تكميليًّا (drop-in)، وهو ينجو من ترقيات الحزم بطريقة لا ينجو بها تعديل journald.conf. القيم أدناه تحتفظ بأسبوع تقريبًا — يكفي للتحقيق في شيء تلاحظه يوم الاثنين وبدأ يوم الجمعة — ضمن سقف صارم قدره 200 MB.

    sudo mkdir -p /etc/systemd/journald.conf.d
    sudo tee /etc/systemd/journald.conf.d/retention.conf > /dev/null <<'EOF'
    [Journal]
    Storage=persistent
    SystemMaxUse=200M
    SystemMaxFileSize=20M
    MaxRetentionSec=7day
    MaxFileSec=1day
    ForwardToSyslog=no
    EOF
    
    sudo systemctl restart systemd-journald
    journalctl --disk-usage

    تُطبِّق إعادة التشغيل حدود الحجم فورًا؛ أما ساعة الاحتفاظ فتُفرَض مع تدوير الملفّات الجديدة، لذا فإن سجلّ journald الضخم الموجود مسبقًا يتقلّص عند التدوير التالي لا فورًا. يفرضه journalctl --vacuum-time=7d الآن إذا أردتَ استعادة مساحة القرص اليوم.

    ForwardToSyslog=no مهمّ على أي صورة تُشحَن مع rsyslog: فبدونه، يُضاف كل مُدخَل من journald أيضًا إلى /var/log/syslog، تحت جدول logrotate لا جدول journald، فيكون لديك نسختان بتاريخَي انتهاء صلاحية مختلفَين. تحقّق في أي حالة أنت قبل أن تفترض:

    systemctl is-active rsyslog 2>/dev/null || echo "rsyslog not running"
    ls -la /var/log/auth.log /var/log/syslog 2>/dev/null

    إذا كانت تلك الملفّات موجودة، فإن rsyslog يملكها والخطوة السادسة هي حيث يُضبَط احتفاظها. أما إذا لم تكن موجودة، فإن سجلّ journald هو النسخة الوحيدة وأنت لتوّك قد حصرتَ حجمه.

  5. 05

    أوقف التطبيق عن إعادة تسجيل ما أزلته

    لا شيء مما سبق يلمس تطبيقك، وتطبيق في الوضع الخطأ سيكتب بكل سرور العنوان الكامل، وURL الكامل، ورمز الجلسة في ملفّ خاصّ به. ثلاثة أشياء تستحق التحقّق.

    PHP-FPM يأتي بتوجيه access.log في إعدادات مجمعه (pool)، مُعطَّلًا بتعليق افتراضيًا لكن عددًا كبيرًا جدًا من لوحات التحكّم يُفعِّله. إذا كان مُفعَّلاً، فهو نسخة ثانية من كل سطر طلب، من ملفّ لم يلمسه عملك في nginx إطلاقًا.

    grep -rn '^access.log' /etc/php/*/fpm/pool.d/ || echo "fpm access log off"

    مستوى تسجيل إطار عملك يحدّد ما إذا كانت سلاسل الاستعلام وأجسام الطلبات تصل إلى القرص. يسجّل وضع debug في معظم أطر العمل عنوان URL كاملاً، ورابط إعادة تعيين كلمة المرور هو عنوان URL كامل. اضبط مستوى الإنتاج (production) وتأكّد أنه فعلاً المستوى المُحمَّل، لا المستوى الموجود في الملفّ الذي تظنّ أنه يُقرَأ.

    مُشغِّل Docker الافتراضي لا يُدوِّر أبدًا. أصلح ذلك على مستوى الخدمة (daemon) حتى ترث كل حاوية مستقبلية الحدّ. لاحظ أن هذا ينطبق على الحاويات التي تُنشأ بعد إعادة التشغيل — أما الحاويات الموجودة فتحتفظ بملفّها الحالي غير المحدود حتى تُعاد إنشاؤها.

    sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
    {
      "log-driver": "json-file",
      "log-opts": { "max-size": "10m", "max-file": "3" }
    }
    EOF
    
    sudo systemctl restart docker
    docker inspect --format '{{.HostConfig.LogConfig}}' $(docker ps -q) 2>/dev/null

    إذا كانت حاوية ما تحمل بالفعل ملفًّا ضخمًا، فإعادة إنشائها بـdocker compose up -d --force-recreate هو ما يُقلِّم التاريخ فعلاً — إعادة التشغيل وحدها تُبقي على ملفّ السجلّ نفسه.

  6. 06

    اضبط مدة الاحتفاظ عمدًا، في مكان واحد لكل ملفّ

    logrotate هو المكان الذي تعيش فيه الساعة لكل ما يكتبه rsyslog وnginx. عدِّل الفقرة (stanza) المشحونة بدلاً من إضافة فقرة ثانية: فقرتان تُسمّيان المسار نفسه تجعلان logrotate يفشل بخطأ إدخال مكرّر ويتوقّف عن تدوير ذلك الملفّ كليًا، وهذه هي الطريقة الأشيع التي يتحوّل بها تغيير في مدة الاحتفاظ بصمت إلى احتفاظ لا نهائي.

    # check for duplicates BEFORE editing, then again after
    sudo logrotate --debug /etc/logrotate.conf 2>&1 | grep -i 'duplicate\|error'

    بالنسبة لـnginx، يحتاج الملفّان ساعتين مختلفتين: يمكن للملفّ المُقلَّم أن يعيش أسابيع، أما الملفّ الذي يحمل العناوين الكاملة فلا ينبغي أن يبقى بعد نهاية اليوم. أضِف فقرة منفصلة لسجلّ الأمان — مسار مختلف، فلا تكرار — وقصِّر الفقرة المشحونة.

    sudo tee /etc/logrotate.d/nginx-security > /dev/null <<'EOF'
    # The only file on this box that holds complete client addresses.
    # One day, uncompressed so fail2ban can read it. Do not lengthen without
    # a reason you would be happy to write down here.
    /var/log/nginx/security.log {
        daily
        rotate 1
        maxage 1
        missingok
        notifempty
        nocompress
        create 0640 www-data adm
        sharedscripts
        postrotate
            [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
        endscript
    }
    EOF
    
    sudo sed -i 's/^\trotate 14$/\trotate 7/' /etc/logrotate.d/nginx
    sudo logrotate --debug /etc/logrotate.d/nginx-security

    أجرِ الحساب نفسه على /etc/logrotate.d/rsyslog إذا كان rsyslog مثبَّتًا — فإعداداته الافتراضية تحتفظ بأربعة أسابيع من auth.log، وهي أربعة أسابيع من جلسات SSH الخاصة بك أنت. وشغِّل جولة debug مرة أخيرة: فهي تطبع بالضبط الملفّات التي كانت ستُدوِّرها وتحذفها، وهذه هي الطريقة الوحيدة للتأكّد من أن الأرقام التي كتبتَها للتوّ هي الأرقام السارية فعلاً.

  7. 07

    أثبِت ذلك، بما في ذلك عبر النسخة الاحتياطية

    أعد تشغيل القياسات من الخطوة الأولى. يجب أن يتوقّف الآن عدد العناوين الكاملة المختلفة في سجلّ الاحتفاظ الطويل عن النموّ، وبعد دورة تدوير واحدة يجب أن يكون صفرًا.

    # should print 0 once the pre-change files have rotated out
    zcat -f /var/log/nginx/access.log* 2>/dev/null \
      | grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}' \
      | grep -v '\.0$' | sort -u | wc -l
    
    # and the journal should now be bounded
    journalctl --disk-usage

    ثم الفحص الذي لا يُجريه أحد تقريبًا: انظر داخل النسخة الاحتياطية. مستودع لا يزال يحمل /var/log يحتفظ بالدقّة ومدة الاحتفاظ اللتين أمضيتَ للتوّ بعد ظهر كامل تُزيلهما، وسيستمرّ في الاحتفاظ بهما طالما بقي أقدم أرشيف لديك.

    borg list | head -3          # borg lists oldest first
    borg list ::"$(borg list --short | head -1)" 2>/dev/null \
      | grep -c '^var/log/' || echo "no logs in oldest archive"

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

    أخيرًا، دوِّن الأرقام في مكان سيجده الشخص التالي: مدة الاحتفاظ التي اخترتها، والسبب، والتاريخ. يكفي تعليق أعلى ملفّ logrotate. الإعداد أعلاه قرار، والقرار الذي لا يستطيع أحد إعادة بنائه بعد عام يعود ليصبح افتراضيًا مجدَّدًا.

SP·10 — الأسئلة الشائعة

إجابات سريعة

هل تصفير الثُّمانية الأخيرة يُخفي هوية عنوان IP فعلاً؟

لا، ويستحق الأمر دقّة هنا بدلاً من طمأنة زائفة. التقليم إلى /24 هو تمويه هوية (pseudonymisation): فهو يُضيّق الزائر إلى كتلة من حتى 256 عنوانًا بدلاً من عنوان واحد، وهي على مزوّد إنترنت سكني حيّ بأكمله، وعلى شبكة شركة صغيرة يمكن أن تظلّ منظمة واحدة بعينها. اجمع /24 مع طابع زمني دقيق وسلسلة وكيل مستخدم مفصّلة، وإعادة التعرّف على الهوية تصبح ممكنة غالبًا لطرف مُصمِّم على ذلك.

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

هل سيستمرّ fail2ban في العمل إذا كان سجلّ الوصول لديّ مُقلَّمًا؟

لن يعمل من الملفّ المُقلَّم — فالتقاط <HOST> سيُطابق 203.0.113.0 ويحجب عنوانًا واحدًا لم يتّصل بك قطّ، تاركًا المصدر الحقيقي دون أن يُمَسّ. وأي شخص يُجهِّل سجلّاته دون ملاحظة هذا يكون قد أوقف fail2ban بصمت.

الحلّ هو الفصل الموصوف أعلاه: يكتب nginx ملفًّا ثانيًا قصير العمر بعناوين كاملة، ويقرأ fail2ban ذلك الملفّ. لاحظ أن /var/log/nginx/error.log يحمل هو الآخر عناوين كاملة ولا يمكن إعادة تهيئة صيغته، لذا تستمرّ الزنزانات (jails) المرتبطة به — زنزانات فشل المصادقة عادةً — في العمل بغضّ النظر. أما بالنسبة للإساءة الحجمية لا حشو بيانات الاعتماد (credential stuffing)، فإن limit_req الخاص بـnginx أفضل من الاثنين معًا: فهو يعمل داخل العملية نفسها، خلال ميكروثوانٍ، ولا يكتب شيئًا على الإطلاق.

كيف أُصحِّح خطأ 500 في بيئة الإنتاج دون عنوان العميل؟

بمعرّف طلب (request ID)، وهو عادةً أفضل مما كان عليه العنوان. يُولِّد nginx $request_id لكل طلب؛ ضعه في صيغة سجلّك، ومرِّره إلى upstream بـproxy_set_header X-Request-ID $request_id;، وسجِّله من التطبيق جنبًا إلى جنب مع تتبّع المكدّس. سلسلة واحدة تربط الآن سطر الوصول، وخطأ upstream، والاستثناء معًا.

وهو يتفوّق على العنوان من ثلاث نواحٍ: فهو فريد لكل طلب لا لكل عميل، بحيث لا يتداخل فشلان متزامنان معًا؛ وهو ينجو من عميل جوّال يتغيّر عنوانه في منتصف الجلسة؛ وآمنٌ لعرضه للمستخدم على صفحة الخطأ، بحيث يصل تقرير الخلل بالمعرّف الدقيق الذي تحتاجه. أما في الحالة النادرة التي تحتاج العنوان فعلاً — هجوم موجَّه مثلاً — فسجلّ الأمان قصير الاحتفاظ يحمله ليوم واحد.

هل أنا مُلزَم قانونًا بالاحتفاظ بسجلّات الخادم؟

بالنسبة لمُشغِّل موقع أو زبون استضافة في الاتحاد الأوروبي، عمومًا لا. نظام الاحتفاظ الشامل الذي يتذكّره الناس بشكل جزئي جاء من توجيه الاحتفاظ بالبيانات لعام 2006، الذي أبطلته محكمة العدل الأوروبية عام 2014؛ والقوانين الوطنية المتبقّية تنطبق في معظمها على مزوّدي الاتصالات الإلكترونية — شركات الاتصالات ومزوّدي الإنترنت — لا على مَن يُشغّل خادم ويب. أما اللائحة العامة لحماية البيانات (GDPR) فتدفع في الاتجاه المعاكس، نحو الاحتفاظ بأقلّ لمدة أقصر. والولايات المتحدة أيضًا ليس فيها تفويض عام بالاحتفاظ يخصّ مُشغِّلي المواقع، رغم أن طلب حفظ يمكن أن يصل ويُغيّر ما يجوز لك فعله بالبيانات الموجودة في تلك اللحظة.

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

ماذا عن السجلّات التي يحتفظ بها Cloudflare أو CDN الخاص بي؟

هي موجودة بغضّ النظر عمّا تفعله عند المصدر، وتقليم ملفّك الخاص لا يصل إليها رجوعًا عبر الحافة. إذا كان Cloudflare يُنهي TLS نيابةً عنك، فهو يرى الطلب الكامل وعنوان العميل الحقيقي قبل أن يتدخّل خادمك، ويحتفظ به بموجب جدوله الخاص، ويستجيب لإجراءاته القانونية الخاصة. وينطبق الأمر نفسه على أي جدار حماية تطبيقات ويب (WAF) أو خدمة تنقية DDoS مُستضافة.

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

هل ينبغي أن أُعطِّل سجلّات الوصول كليًا فحسب؟

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

الأرض الوسطى هي ما يبنيه هذا الدليل: احتفظ بالسطر، وأزِل المُعرِّف، وأضِف معرّف ربط (correlation ID)، ودَع الملفّ الوحيد الذي يحمل عناوين كاملة تنتهي صلاحيته خلال يوم. المكان الوحيد الذي يكون فيه access_log off صائبًا ببساطة هو الأصول الثابتة — الصور، وCSS، والخطوط — فهي حجم صرف ولا تُخبرك بشيء لا تستطيع الحصول عليه من طلب HTML الذي سبقها. تذكّر أن سجلّ الأخطاء يستمرّ في تسجيل عناوين العملاء عند الإخفاقات على أي حال، وهذا سبب إضافي لمنحه مدة احتفاظ قصيرة بدلاً من اللجوء إلى مفتاح الإيقاف.

طبّقها عمليًا

VPS متصل خلال 15 min، والمخصص يُسلَّم خلال 2–12 h. اشحن بدءًا من $30.00 بالعملات المشفّرة — دون أي هوية مرتبطة.

انشر خادم VPS