Читатели като вас помагат в подкрепа на MUO. Когато правите покупка чрез връзки на нашия сайт, ние може да спечелим комисионна за партньор.
Състезание възниква, когато две операции трябва да се изпълнят в определен ред, но те могат да се изпълняват в обратен ред.
Например в многопоточно приложение две отделни нишки могат да имат достъп до обща променлива. В резултат на това, ако една нишка промени стойността на променливата, другата все още може да използва по-старата версия, игнорирайки най-новата стойност. Това ще доведе до нежелани резултати.
За да разберете по-добре този модел, би било добре да разгледате внимателно процеса на превключване на процеса на процесора.
Как процесорът превключва процесите
Съвременни операционни системи може да изпълнява повече от един процес едновременно, наречен многозадачност. Когато погледнете този процес от гледна точка на Цикълът на изпълнение на процесора, може да откриете, че многозадачността всъщност не съществува.
Вместо това процесорите непрекъснато превключват между процесите, за да ги изпълняват едновременно или поне да действат така, сякаш го правят. Централният процесор може да прекъсне процес, преди да е завършил, и да възобнови друг процес. Операционната система контролира управлението на тези процеси.
Например алгоритъмът Round Robin, един от най-простите алгоритми за превключване, работи по следния начин:
Като цяло този алгоритъм позволява на всеки процес да се изпълнява за много малки отрязъци от време, според операционната система. Например, това може да бъде период от две микросекунди.
Централният процесор поема всеки процес на свой ред и изпълнява команди, които ще се изпълняват за две микросекунди. След това продължава към следващия процес, независимо дали текущият е приключил или не. По този начин, от гледна точка на крайния потребител, изглежда, че повече от един процес се изпълняват едновременно. Въпреки това, когато погледнете зад кулисите, процесорът все още прави нещата по ред.
Между другото, както показва диаграмата по-горе, в алгоритъма Round Robin липсват каквито и да е понятия за оптимизиране или приоритет на обработка. В резултат на това това е доста елементарен метод, който рядко се използва в реални системи.
Сега, за да разберете всичко това по-добре, представете си, че се изпълняват две нишки. Ако нишките имат достъп до обща променлива, може да възникне състояние на състезание.
Примерно уеб приложение и условия за състезание
Вижте простото приложение Flask по-долу, за да помислите върху конкретен пример за всичко, което сте прочели досега. Целта на това приложение е да управлява парични транзакции, които ще се извършват в мрежата. Запазете следното във файл с име money.py:
от колба импортиране Колба
от flask.ext.sqlalchemy импортиране SQLAlchemyприложение = Колба (__име__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy (приложение)класСметка(дб. Модел):
id = db. Колона (db. Цяло число, първичен_ключ = Вярно)
количество = db. Колона (db. низ (80), уникален = Вярно)деф__в него__(себе си, брои):
self.amount = сумадеф__repr__(себе си):
връщане '' % собствена сума@app.route("/")
дефздрасти():
акаунт = Account.query.get(1) # Има само един портфейл.
връщане "Общо пари = {}".формат (акаунт.сума)@app.route("/изпрати/")
дефизпрати(количество):
акаунт = Account.query.get(1)ако int (account.amount) < сума:
връщане „Недостатъчен баланс. Нулирайте парите с /reset!)"account.amount = int (account.amount) - сума
db.session.commit()
връщане „Изпратена сума = {}“.format (сума)@app.route("/reset")
дефнулиране():
акаунт = Account.query.get(1)
account.amount = 5000
db.session.commit()
връщане „Нулиране на парите“.
ако __име__ == "__основен__":
app.secret_key = 'heLLoTHisIsSeCReTKey!'
app.run()
За да изпълните този код, ще трябва да създадете запис в таблицата на акаунта и да продължите транзакциите върху този запис. Както можете да видите в кода, това е тестова среда, така че прави транзакции срещу първия запис в таблицата.
от пари импортиране db
db.create_all()
от пари импортиране Сметка
акаунт = акаунт (5000)
db.сесия.добавете(сметка)
db.сесия. ангажирам()
Вече сте създали акаунт с баланс от $5000. И накрая, стартирайте горния изходен код, като използвате следната команда, при условие че имате инсталирани пакети Flask и Flask-SQLAlchemy:
питонпари.py
Така че имате уеб приложението Flask, което извършва прост процес на извличане. Това приложение може да изпълнява следните операции с GET връзки за заявка. Тъй като Flask работи на порт 5000 по подразбиране, адресът, на който осъществявате достъп до него, е 127.0.0.1:5000/. Приложението предоставя следните крайни точки:
- 127.0.0.1:5000/ показва текущия баланс.
- 127.0.0.1:5000/изпрати/{сума} изважда сума от сметката.
- 127.0.0.1:5000/нулиране нулира сметката на $5000.
Сега, на този етап, можете да разгледате как възниква уязвимостта на състоянието на състезанието.
Вероятност за уязвимост на състезателни условия
Горното уеб приложение съдържа възможна уязвимост в състояние на състезание.
Представете си, че имате $5000 за начало и създайте две различни HTTP заявки, които ще изпратят $1. За целта можете да изпратите две различни HTTP заявки към връзката 127.0.0.1:5000/изпрати/1. Да предположим, че веднага щом уеб сървъра обработва първата заявка, процесорът спира този процес и обработва втората заявка. Например, първият процес може да спре след изпълнение на следния ред код:
account.amount = вътр(account.amount) - сума
Този код е изчислил нова обща сума, но все още не е запазил записа в базата данни. Когато започне втората заявка, тя ще извърши същото изчисление, като извади $1 от стойността в базата данни - $5000 - и съхрани резултата. Когато първият процес се възобнови, той ще съхрани собствената си стойност - $4,999 - която няма да отразява последното салдо по сметката.
И така, две заявки са изпълнени и всяка трябва да е извадила $1 от баланса на акаунта, което води до нов баланс от $4998. Но в зависимост от реда, в който уеб сървърът ги обработва, крайното салдо по сметката може да бъде $4999.
Представете си, че изпращате 128 заявки за извършване на превод от $1 към целевата система за период от време от пет секунди. В резултат на тази транзакция очакваното извлечение от сметката ще бъде $5000 - $128 = $4875. Въпреки това, поради условията на състезанието, крайният баланс може да варира между $4,875 и $4,999.
Програмистите са един от най-важните компоненти на сигурността
В софтуерен проект като програмист имате доста отговорности. Примерът по-горе беше за просто приложение за паричен превод. Представете си, че работите върху софтуерен проект, който управлява банкова сметка или бекенда на голям сайт за електронна търговия.
Трябва да сте запознати с такива уязвимости, така че програмата, която сте написали за защитата им, да не съдържа уязвимости. Това изисква силна отговорност.
Уязвимостта на състезателни условия е само една от тях. Без значение каква технология използвате, трябва да внимавате за уязвимости в кода, който пишете. Едно от най-важните умения, които можете да придобиете като програмист, е познаването на софтуерната сигурност.