انحرافات غريبة عن عالم تكنولوجيا المعلومات - 4

صورة

يقوم موقع Daily WTF بجمع قصص مضحكة ووحشية و / أو حزينة من عالم تكنولوجيا المعلومات لمدة 15 عامًا. قمت بترجمة العديد من القصص التي بدت مثيرة للاهتمام بالنسبة لي. تم تغيير جميع أسماء الشركات والأسماء. يمكن العثور على الإصدارات السابقة تحت عنوان " الانحرافات الغريبة ".

القصة الأولى. نهاية الشهر


[الأصل]

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

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

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

أظهر كاتو للمستشار الجديد كيف يقوم البنك بإعداد تقارير نهاية اليوم. "انظر ، لدينا متغيرات عالمية ليوم العمل السابق ، واليوم ، ويوم العمل التالي. لديهم التنسيق YYMMDD لتسهيل التعامل معه. "

"نعم ، نعم ، حصلت عليه. أنا أفهم ذلك. في أي شكل هم؟ "

"... آه ... لا أستطيع إلا أن أفترض أن هذه هي السنة والشهر واليوم."

"نعم ، نعم ، حسناً. ممتاز. ثم أذهب إلى العمل. "

بعد هذه المحادثة ، كان لدى Kato شعور سيء بهذا. لكنه حاول التخلص منه. وقال المستشار أن كل شيء تم إعداده وجاهز. إنه يعرف بالضبط ما يفعله ، أليس كذلك؟ ألقى كاتو هذا من رأسه وتوقف عن القلق حتى وصل وقت مراجعة الكود ووجد مثل هذه اللؤلؤة:

 TH.DATE = R.DATES(EB.DAT.NEXT.WORKING.DAY)[1,6]:"01" CALL CDT('ES00',TH.DATE,"-1C") WTODAY = OCONV(DATE(),"DY") : FMT(OCONV(DATE(),"DM"),'R%2') : FMT(OCONV(DATE(),"DD"),'R%2') IF TH.DATE EQ WTODAY THEN 

اشرح باختصار ما يحدث هنا:

  1. خذ يوم العمل التالي وغيّر اليوم إلى 01 للحصول على أول يوم من الشهر.
  2. نقوم بتغيير هذا التاريخ بطرح يوم تقويمي واحد في تقويم إسبانيا.
  3. نأخذ التاريخ من الخادم ونترجمه إلى التنسيق YYYYMMDD ، مع استدعاء الأمر Date ثلاث مرات.
  4. إذا كان التاريخ المحسوب في الخطوة 2 يساوي التاريخ المحسوب في الخطوة 3 ، ابدأ العملية.

حسنا ، في الواقع ... كان يعمل. بالنسبة للجزء الاكبر. ما لم يكن ذلك لسبب ما ، فإن نهاية اليوم لا تحدث بعد منتصف الليل في اليوم قبل الأخير من الشهر - وهذا ليس نادرًا جدًا. في هذه الحالة ، ستعتقد الشفرة عن طريق الخطأ أن هذا هو آخر يوم في الشهر ، وسوف تبدأ في إنشاء التقرير. الأمر الذي يسير على ما يرام مع مشكلة أخرى: إذا حدث نفس الشيء في اليوم الأخير من الشهر ، فلن يبدأ إنشاء التقرير عن طريق الخطأ. وأفضل خطأ: إذا تبين أن آخر يوم في الشهر هو يوم الأحد ، فلن يتم تثبيت تقويم الخادم عليه أبدًا ، لأنه يتخطى أيام غير العمل.

الحديث عن غير أيام العمل: بما أن Inibank يقع في الولايات المتحدة الأمريكية ، فلا يوجد سبب لاستخدام تقويم إسبانيا. نعم ، ستكون الأشهر والأسابيع هي نفسها ، لكن في تقويم البرنامج الإسباني ، يلزمك تحديد أيام العطل الرسمية في الولايات المتحدة الأمريكية ، وإلا فإن البرنامج سوف يستمر في العمل. أخيرًا ، كما لو أن كل هذا لم يكن كافيًا بعد ، فإن المكالمة ثلاثية التاريخ تعني أنه قد تكون هناك خلافات عند البدء تمامًا في منتصف الليل: يتم طلب قيمة الشهر قبل منتصف الليل ، واليوم الذي يليه.

أضاف Kato تعليقًا ، يقترح طريقة لتغيير الكود:

 IF R.DATES(EB.DAT.TODAY)[5,2] # R.DATES(EB.DAT.LAST.WORKING.DAY)[5,2] THEN 

بعد خمس دقائق ، ذهب الخبير الاستشاري إلى مكتبه. "ماذا يعني هذا التعديل؟"

لم يكن كاتو في مزاج يجادل في تلك اللحظة. "رمزك مكسور ، صديق. كل هذا ليس ضروريا ".

"أرى ، أرى. هذا هو في الواقع مجرد إجراء التشغيل القياسي لصناعتنا. حسنا ، حسنا. "

شكك كاتو في ذلك كثيرًا ، لكن ببساطة تجاهل. ثم الصناعة خاطئة. شرحت كل شيء في التعليق ".

"نعم. نعم ، لقد قرأت ذلك. لكنني سوف أقرأها مرة أخرى. " واختفى فجأة كما ظهر.

تم إجراء تعديلات ، ووافق Kato على الكود ، واختفى الاستشاري في الضباب.

تساءل كاتو أحيانًا وهو يرقد على الفراش ليلًا: هل فهم الاستشاري حقًا ما فعله بشكل خاطئ ، أم أنه وافق فقط على النظر وتلقى الشيك واستمر في كتابة رمز فظيع في مكان ما مقابل ضعف راتب كاتو؟ في تلك البنوك التي لا يوجد فيها موظفون يمكنهم التحقق من الكود.

ولكن لا تستثمر كل هذه الأموال في البيتكوين. الأمر أسوأ هناك.

القصة الثانية. كيف يتم ذلك


[الأصل]

يحب الناس أكل الكلاب الساخنة حتى يكتشفون كيف يطبخون. معظمهم لا يسألون ، لأنهم لا يريدون أن يعرفوا ويواصلوا أكل الكلاب الساخنة. عند تطوير البرامج ، يتعين علينا في بعض الأحيان أن نسأل. ليس فقط لحل المشكلات ، ولكن أيضًا لأن بعض المبرمجين يخشون أن يتم تجميع البرامج الموجودة في سياراتهم ، التي تسير على طول الطريق السريع بسرعة 100 كم / ساعة ، من أشرطة وعصي. صناعتنا بأكملها تعمل بشكل سيء مع مهامها .

عمل بريت كمحلل للنظم في مركز MedStitute للبحوث الطبية. يستخدم MedStitute برمجيات خاصة تسمى MedTech لتخزين وتحليل البيانات. أعجب الأطباء والباحثون بنتائج MedTech ، لكن الزميل بريت تايري كان يعرف كيف تم إنشاؤها.

لم يتمكن البرنامج من الوصول إلى الواجهة الخلفية ، وتمت عملية التطوير بالكامل في واجهة المستخدم الرسومية "الماوس القابل للبرمجة". بدت هذه الواجهة كما لو كانت مكتوبة من قبل شخص درس البرمجة عن طريق نسخ مواقع الويب من التسعينيات ، ونظر في عشر دقائق من حديقة الجوراسي ، وبحث عن إجابات على StackOverflow حتى يتم تجميع شيء ما. كما أظهرت "لغة البرمجة" مستوى مشابهًا من التفكير في فلسفة التصميم. كل if يجب أن يكون لديه else . تستخدم بعض الوحدات القيم المنطقية ، بينما أعادت أخرى سلاسل فارغة للإشارة إلى قيم خاطئة. من الوثائق لم يكن واضحا في أي حالة حدث واحد أو آخر. في الواقع ، تحول كل if إلى ثلاثة عبارات.

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

عرف بريت أن Tyree كان يعمل في مشروع آخر عشوائيًا حسب الحقل المحسوب ، لذلك اتصل به في Slack. "كيف قمت بترميز هذا المتغير العشوائي؟ هل يمنع Medtech هذا من الحدوث؟ "

"أتحدث عن المؤتمرات ، سأتصل بك لاحقًا" ، كتب تايري.

وبعد بضع دقائق دعا Tyree بريت.

يجب أن تبدأ بحقلين. دعنا نقول. دعنا نسميها $variable_choice ، أي سؤال الاختيار من متعدد ، و $variable_calced ، أي مجالك المحسوب. عندما تريد إنشاء متغير يقوم بالاختيار العشوائي استنادًا إلى حقل محسوب ، فأخبر Medtech أن هذا المتغير العشوائي يعتمد على $variable_choice . ثم يمكنك حذف $variable_choice ، وإعادة تسمية $variable_calced إلى $variable_choice "

"توقف ، يسمح لك النظام بذلك ، لكن لا يسمح لك بتخصيص الحقول المحسوبة بطريقة عشوائية؟ وهي لا تحقق ذلك؟ "

"آمل ألا يتغير أي شيء ، وهي لا تبدأ في التحقق من ذلك حتى الانتهاء من مشروعي" ، أجاب Tyree.

"يجب أن تتم هذه الدراسة على مدار عشر سنوات. ويعتمد إنجازها الناجح على ما إذا كان المطورون يعتبرون هذه الخدعة خطأ؟ "

لقد تمكنت من العثور على هذا الحل فقط. اسمحوا لي أن أعرف إذا وجدت أي شيء أفضل. "

لم يرض بريت مثل هذا الاختراق ، وعاد إلى دراسة الوثائق. لقد وجد حلاً "أفضل": يمكنك إنشاء حقل اختيار متعدد للقراءة فقط مع القيمة الافتراضية الوحيدة - قيمة الحقل المحسوب. لسوء الحظ ، يمكن للمستخدم تغيير القائمة عن غير قصد عن طريق الإجابة على سؤال الاختيار من متعدد قبل حساب قيمة الحقل المحسوب.

في النهاية ، كان الشيء الوحيد المتبقي لبريت هو أخذ قسط من الراحة ، والذهاب إلى المقصف وشراء بعض الكلاب الساخنة.

القصة الثالثة. قابلية والأجهزة


[الأصل]


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

في هذه العملية ، تخطى القرص الصلب 20 ميجابايت في جهاز الكمبيوتر الخاص بـ Matt. من مكتبه ، أرسل طلبًا إلى قسم تقنية المعلومات. لإنجاز التطبيق ، تم تعيين فني من Gary ، والذي ظهر بعد فترة من الزمن في مكعبات Matt ، حاملاً قرصًا ثابتًا جديدًا ومفك البراغي. أرسل غاري مات لتناول القهوة للتركيز على "المريض". بعد إجراء عملية جراحية بسيطة ، تم تشغيل الكمبيوتر الشخصي الخاص بـ Matt وعمله مع محرك أقراص صلبة كبير.

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

بعد نداء ذعر لقسم تكنولوجيا المعلومات ، ظهر غاري مرة أخرى في مكتبه مع مفك البراغي. عند فتح علبة الكمبيوتر ، صرخ على الفور: "انتظر لحظة! هل قمت بسحب جهاز كمبيوتر في مكان ما؟ "

مات عبوس. "حسنا ، نعم. هل هذا هو الشيء؟ "

"نعم بالطبع! غاري بدأ يلعن. "بدأ القرص الصلب في التسكع وقلل من كل شيء بداخله!"

انحنى مات على غاري ليرى بنفسه داخل الكمبيوتر. لاحظ على الفور أن القرص الصلب الجديد "مثبت" على الشريط.

"أوقفوا! أنك لا يجب أن تفعل هذا! " وأشار مات إلى قطعة من الشريط الاسكتلندي. "هل يجب عليّ الاتصال بمشرفك في الحال؟"

وجه غاري عبوس. "لا يعطيني التثبيتات الضرورية!

"ثم ابحث عن الشخص الذي لديه!"

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

القصة الرابعة. هكذا يؤثر PL / SQL على عقلك.


[الأصل]

سيبقى Oracle الأبطال من بين أغرب القرارات وأكثرها نجاحًا. اليوم ننظر إلى رمز PL / SQL قليلاً.

PL / SQL هي لغة غريبة ، وهي مزيج من SQL و P rocedural (إجرائي) L اللغوية (لغة) مع كائن المنحى لصقها على الجانب. بناء الجملة قادر بشكل رائع على خلق انطباع بأنه تم تطويره في السبعينيات ، وكل وظيفة جديدة أو تغيير للغة يواصل هذا التقليد.

يستند هيكل كل وحدة رمز PL / SQL إلى كتلة . كل كتلة هي مساحة اسم مستقلة. باختصار ، يبدو تشريحه هكذا:

 DECLARE -- variable declarations go here BEGIN -- code goes here EXCEPTIONS -- exception handling code goes here, using WHEN clauses END; 

إذا كنت تكتب إجراءً مخزّنًا أو معالجًا للأحداث ، DECLARE الكلمة الأساسية " DECLARE بـ CREATE [OR REPLACE] . يمكنك أيضًا تداخل الكتل داخل الكتل الأخرى ، وفي كثير من الأحيان يمكنك رؤية الكود منظم بهذه الطريقة:

 BEGIN DECLARE --stuff BEGIN --actions END; --more actions END; 

نعم ، بسرعة كبيرة يبدأ مربكا. ونعم ، إذا كنت ترغب في توفير معالجة خطأ منظمة على الأقل تقريبًا ، فيجب أن تبدأ في وضع كتل في بعضها البعض.

اللغة وقاعدة البيانات لها ميزات ممتعة أخرى. قبل الإصدار 12c ، لم يكن لديهم نوع عمود IDENTITY . في الإصدارات السابقة ، كان عليك استخدام كائن SEQUENCE وكتابة الإجراءات أو معالجات الأحداث التي تؤدي الترقيم التلقائي القسري. بشكل عام ، تم استخدام عامل التشغيل SELECT INTO… لتعيين قيمة لمتغير. المكافأة: يتطلب Oracle SQL دائمًا تحديد جدول في عبارة FROM ، لذلك يجب عليك استخدام جدول dual اختراعه ، مثل هذا:

 CREATE TRIGGER "SOME_TABLE_AUTONUMBER" BEFORE INSERT ON "SOME_TABLE" FOR EACH ROW BEGIN SELECT myseq.nextval INTO :new.id FROM dual; END; 

:new في هذا السياق يشير إلى السطر الذي نرقمه تلقائيًا. في الإصدارات القديمة من Oracle ، كانت هذه هي الطريقة "المعتادة" لإنشاء الأعمدة ذات الترقيم التلقائي. اكتشف Benoit طريقة أخرى أقل شيوعًا لإجراء العملية نفسها:

 CREATE OR REPLACE TRIGGER "SCHEMA1"."TABLE1_TRIGGER" BEFORE INSERT ON "SCHEMA1"."TABLE1" FOR EACH ROW BEGIN DECLARE pl_error_id table1.error_id%TYPE; CURSOR get_seq IS SELECT table1_seq.nextval FROM dual; BEGIN OPEN get_seq; FETCH get_seq INTO pl_error_id; IF get_seq%NOTFOUND THEN raise_application_error(-20001, 'Sequence TABLE1_SEQ does not exist'); CLOSE get_seq; END IF; CLOSE get_seq; :new.error_id := pl_error_id; END; END table1_trigger; 

الكثير يحدث هنا. أولاً ، لاحظ أن قسم DECLARE يحتوي على عبارة CURSOR . تسمح لك المؤشرات بالتكرار من خلال السجلات. إنها باهظة الثمن وفي عالم Oracle يعد موردًا يجب تحريره.

يستخدم معالج الأحداث (المشغل) هذا كتلة متداخلة بدون سبب. كما يستخدم المتغير الإضافي pl_error_id ، والذي يمكن الاستغناء عنه.

لكن الجزء الغريب حقًا هو IF get_seq%NOTFOUND block IF get_seq%NOTFOUND . كل شيء بسيط للغاية: إنه يتحقق من حالة عدم إرجاع المؤشر لسلسلة. بالنسبة لهذا المؤشر ، لا يمكن أن يحدث هذا من الناحية النظرية ، وبالتالي فإن العمليات الداخلية لا يتم تنفيذها أبدًا. تسلسل دائما بإرجاع قيمة. وهذا جيد ، بالنظر إلى الكود الذي يذهب أبعد من ذلك.

raise_application_error هو تناظري للرمي في Oracle. ترتفع هذه العبارة في رصة الكتل القابلة للتنفيذ حتى تعثر على قسم EXCEPTIONS لمعالجة الخطأ. لاحظ أننا نغلق المؤشر بعد هذا البيان - وهذا في الواقع ، نحن لا نغلق المؤشر مطلقًا . المؤشرات ، كما ذكرنا أعلاه ، باهظة الثمن ، ولا يسمح Oracle إلا باستخدام عدد محدود منها.

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

القصة الخامسة. تسجيلات الدخول المشفرة مزدوجة


[الأصل]

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

عند تطبيقه بشكل صحيح ، لا يعتمد النظام على نوع العميل. يمكنني الوصول إلى الخدمة من خلال متصفح أو عميل كثيف أو من خلال cURL. إذا تم تنفيذها بشكل غير صحيح ، فستحصل على ما حدث للأميرة .

لقد نجحت في حل مشكلة سحب الإحصائيات التي تحتاجها من الواجهة الخلفية ، لكنها لم تستطع معرفة طريقة المصادقة. لذلك ، درست رمز الواجهة الأمامية لفهم كيفية إجراء المصادقة:

 crypt = new JSEncrypt(); crypt.setPublicKey('<removed>'); challenge = "<removed>"; function doChallengeResponse() { document.loginForm.password.value.replace(/&/g, '%26'); document.loginForm.password.value.replace(/\\+/g, '%2B'); document.loginForm.password.value = crypt.encrypt(document.loginForm.password.value); document.loginForm.response.value = document.loginForm.password.value; document.loginForm.password.value = ''; document.loginForm.submit(); } 

من ناحية ، يمكنني أن أفترض أن هذا الرمز قديم جدًا ، بالنظر إلى document.loginForm المستخدم للتفاعلات مع عناصر DOM. من ناحية أخرى ، تم إصدار JSEncrypt لأول مرة في عام 2013 ، مما يعطينا أقصى شريط عمري.

نقوم بتمرير معلمات الوصول إلى الواجهة الخلفية من خلال تقديم النموذج ، والذي يتطلب ، وفقًا لمطور الرمز ، التنظيف - يتم استبدال الكل & + في كلمة المرور ، ولكن ... هذا ليس ضروريًا ، لأن النموذج يجب أن يفي بطلب POST ، بالإضافة إلى ذلك ، قمنا بتشفير البيانات .

هنا هو ما أعتقد. الكود هو في الواقع قديم جدا. قام المطور بنسخه من منشور على StackOverflow حوالي عام 2005 ، والذي لم يستخدم التشفير وإرسال النموذج عبر POST . سنة بعد سنة ، تم إضافة تغييرات صغيرة إليها. لكن الآلية الأساسية لم تتغير أبداً.

فحصت أميرة السجل ووجدت أن التشفير لم يستخدم في الإصدار السابق. challenge , MD5, , , .

: , , , , cURL -. , SSL/TLS .

Source: https://habr.com/ru/post/ar446714/


All Articles