سؤال

"الرجل العادي لا يريد أن يكون حرا. إنه يريد ببساطة أن يكون آمنا". - hl menken

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

قراءة من دفق:

بحسب ال جنو ج تعليمي برمجة الحصول على خط:

ستكدس وظيفة GetLine تلقائيا كتلة الذاكرة حسب الحاجة، عبر دالة RealLoc، لذلك لا يوجد نقص في الفضاء - أحد الأسباب التي تجعل GetLine آمنا. [..] لاحظ أن GetLine يمكن أن تتعامل بأمان مع خط المدخلات الخاصة بك، بغض النظر عن المدة.

أفترض أن getline ينبغي، تحت جميع المدخلات, ، منع أ تجاوز سعة المخزن المؤقت من الحدوث عند القراءة من دفق.

  • هو افتراضي الصحيح؟ هل هناك مدخلات و / أو مخططات تخصيص يمكن أن يؤدي ذلك إلى استغلال؟ على سبيل المثال ما إذا كانت الشخصية الأولى من الدفق هي بعض شخصية تحكم غريبة, ، ربما 0x08 Backspace (CTL-H).
  • هل تم القيام بأي عمل لإثبات GetLine الرياضيا بشكل آمن؟

تعود Malloc خالية من الفشل:

إذا صادح malloc خطأ يرجع malloc مؤشرا فارغا. هذا يعرض مخاطرة أمنية نظرا لأن المرء لا يزال بإمكانه تطبيق حساب مؤشر إلى مؤشر NULL (0x0)، وبالتالي Wikipedia توصي

/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should handle 
       the error here as appropriate. */
} 

SSCANF آمن:

عند استخدام sscanf. لقد حصلت على عادة تخصيص السلاسل التي يجب استخراجها بحجم سلسلة الإدخال نأمل تجنب احتمال تجاوز التجاوز. علي سبيل المثال:

const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];

sscanf(inputStr, formatStr, str1, str2);

نظرا لأن STR1 و STR2 هي حجم INPUTSTR ولم يتم قراءة أي شخصيات أكثر من Strlen (Inputstr) من Inputstr، يبدو الأمر مستحيلا جميع القيم الممكنة للحصول على inputstr للتسبب في تجاوز سعة المخزن المؤقت؟

  • هل انا صائب؟ هل هناك حالات ركنية غريبة لم أفكر فيها؟
  • هل هناك طرق أفضل لكتابة هذا؟ المكتبات التي حلها بالفعل؟

اسئلة عامة:

بينما قمت بنشر عدد كبير من الأسئلة، لا أتوقع أن يجيب أي شخص جميعهم. الأسئلة هي أكثر من الإرشادات إلى أنواع الإجابات التي أبحث عنها. أريد حقا أن أتعلم عقلية C آمنة.

  • ما هي التعابير الآمنة الآمنة الأخرى هناك؟
  • ما الحالات الزاوية التي أحتاج إليها تحقق دائما?
  • كيف يمكنني كتابة اختبارات الوحدات لفرض هذه القواعد؟
  • كيف يمكنني فرض القيود بطريقة اختبار أو بطريقة تصحيح؟
  • أي تقنيات أو أدوات تحليل ثابتة ثابتة أو أدوات ل C؟
  • ما الممارسات C الآمنة التي تتبعها وكيف تبررها بنفسك والآخرين؟

موارد:

تم استعارة العديد من الموارد من الإجابات.

هل كانت مفيدة؟

المحلول

  1. قراءة من تيار

حقيقة ان getline() "سوف تكثف تلقائيا كتلة الذاكرة حسب الحاجة" يعني أن هذا يمكن استخدامه كهجوم من الحرمان، لأنه سيكون تافهة لتوليد المدخلات التي كانت طويلة تستنفد الذاكرة المتاحة للعملية (أو أسوأ، النظام!). بمجرد حدوث حالة خارج الذاكرة، قد تحدث نقاط الضعف الأخرى أيضا. نادرا ما يكون سلوك التعليمات البرمجية في ذاكرة منخفضة / لا لطيفة للغاية وتنبؤه. IMHO أنه أكثر أمانا لتحديد الحدود العليا المعقولة على كل شيء، خاصة في التطبيقات الحساسة للأمن.

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

  1. sscanf.

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

  1. التعليقات العامة

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

نصائح أخرى

أعتقد مثال SSCANF الخاص بك خطأ. لا يزال بإمكانه تجاوزه عند استخدامه بهذه الطريقة.

جرب هذا، الذي يحدد الحد الأقصى لعدد البايتات لقراءة:

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

ألق نظرة على مقالة IBM Dev حول الحماية من الضواحي المخزن المؤقت.

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

مكان جيد للبدء في النظر إلى هذا هو موقع ترميز ويلر ديفيد ويلر.

كتابه المجاني على الإنترنت "برمجة آمنة لنظام التشغيل Linux و Unix Howto"مورد ممتاز يتم تحديثه بانتظام.

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

أي أداة تحليل ثابت، مثل الخليب، هي مجرد أداة. لا توجد أداة يمكن أن تحل محل الفكر الإنساني! بالمختصر، "أحمق مع أداة لا يزال أحمق". وبعد من الخطأ أن نعتقد أن أدوات التحليل (مثل الخلخال) بديلا عن التدريب والمعرفة الأمنية

لقد استخدمت شخصيا موارد ديفيد لعدة سنوات الآن وتجد أنها ممتازة.

طور Yannick Moy نظام شرط مسبق للأهل / فلويد أضعف ل C أثناء دكتوراه و تطبيقها على مكتبة ريت سلاسل المدارة. وبعد وجد عددا من الأخطاء (انظر الصفحة 197 من مذكراته). والخبر السار هو أن المكتبة أكثر أمانا الآن لعمله.

يمكنك أيضا أن ننظر إلى موقع Les Hatton الخاص ب هنا وفي كتابه Safer C. التي يمكنك الحصول عليها من الأمازون.

لا تستخدم gets() للمدخلات، واستخدام fgets(). وبعد ليستخدم fgets(), ، إذا تم تخصيص المخزن المؤقت الخاص بك تلقائيا (أي "على المكدس")، ثم استخدم هذا IDIOM:

char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)

هذا سيبقى العمل إذا قررت تغيير حجم buf. وبعد أنا أفضل هذا النموذج إلى:

#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)

لأن النموذج الأول يستخدم buf لتحديد الوسيطة الثانية، وهي أكثر وضوحا.


تحقق قيمة العودة من fclose().


مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top