Cititorii ca tine ajută la sprijinirea MUO. Când efectuați o achiziție folosind link-uri de pe site-ul nostru, este posibil să câștigăm un comision de afiliat.
O condiție de cursă apare atunci când două operațiuni trebuie să aibă loc într-o anumită ordine, dar pot rula în ordine opusă.
De exemplu, într-o aplicație cu mai multe fire, două fire separate ar putea accesa o variabilă comună. Ca urmare, dacă un fir modifică valoarea variabilei, celălalt poate folosi în continuare versiunea mai veche, ignorând cea mai nouă valoare. Acest lucru va provoca rezultate nedorite.
Pentru a înțelege mai bine acest model, ar fi bine să examinăm îndeaproape procesul de comutare a procesului al procesorului.
Cum un procesor comută procesele
Sisteme de operare moderne poate rula mai mult de un proces simultan, numit multitasking. Când te uiți la acest proces în termeni de Ciclul de execuție al CPU, este posibil să descoperiți că multitasking-ul nu există cu adevărat.
În schimb, procesoarele comută constant între procese pentru a le rula simultan sau cel puțin acționează ca și cum ar face acest lucru. CPU poate întrerupe un proces înainte ca acesta să se încheie și reia un alt proces. Sistemul de operare controlează managementul acestor procese.
De exemplu, algoritmul Round Robin, unul dintre cei mai simpli algoritmi de comutare, funcționează după cum urmează:
În general, acest algoritm permite fiecărui proces să ruleze pentru cantități foarte mici de timp, așa cum stabilește sistemul de operare. De exemplu, aceasta ar putea fi o perioadă de două microsecunde.
CPU preia fiecare proces pe rând și execută comenzi care vor rula timp de două microsecunde. Apoi continuă cu următorul proces, indiferent dacă cel actual s-a terminat sau nu. Astfel, din punctul de vedere al unui utilizator final, mai mult de un proces par să ruleze simultan. Cu toate acestea, când te uiți în culise, procesorul încă face lucrurile în ordine.
Apropo, așa cum arată diagrama de mai sus, algoritmului Round Robin îi lipsește orice noțiune de optimizare sau prioritate de procesare. Drept urmare, este o metodă destul de rudimentară care este rar folosită în sistemele reale.
Acum, pentru a înțelege mai bine toate acestea, imaginați-vă că rulează două fire. Dacă firele accesează o variabilă comună, poate apărea o condiție de concurență.
Un exemplu de aplicație web și condiție de cursă
Consultați aplicația simplă Flask de mai jos pentru a reflecta asupra unui exemplu concret a tot ceea ce ați citit până acum. Scopul acestei aplicații este de a gestiona tranzacțiile de bani care vor avea loc pe web. Salvați următoarele într-un fișier numit bani.py:
din balon import Balon
din balon.ext.sqlalchemy import SQLAlchemyaplicație = Balon (__nume__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy (aplicație)clasăCont(db. Model):
id = db. Coloana (db. Număr întreg, cheie_primară = Adevărat)
suma = db. Coloana (db. Şir(80), unic = Adevărat)def__init__(sine, numără):
self.amount = sumadef__repr__(de sine):
întoarcere '' % auto.sumă@app.route("/")
defBună():
cont = Account.query.get(1) # Există un singur portofel.
întoarcere „Total Money = {}”.format (account.amount)@app.route("/send/")
deftrimite(Cantitate):
cont = Account.query.get(1)dacă int (account.amount) < suma:
întoarcere "Echilibru insuficient. Resetați banii cu /reset!)"account.amount = int (account.amount) - suma
db.session.commit()
întoarcere „Suma trimisă = {}”.format (suma)@app.route ("/resetare")
defresetare():
cont = Account.query.get(1)
cont.sumă = 5000
db.session.commit()
întoarcere „Resetarea banilor”.
dacă __name__ == „__main__”:
app.secret_key = 'Bună ziua!'
app.run()
Pentru a rula acest cod, va trebui să creați o înregistrare în tabelul de cont și să continuați tranzacțiile peste această înregistrare. După cum puteți vedea în cod, acesta este un mediu de testare, deci face tranzacții față de prima înregistrare din tabel.
din bani import db
db.create_toate()
din bani import Cont
cont = cont (5000)
db.sesiune.adăuga(cont)
db.sesiune.commit()
Acum ați creat un cont cu un sold de 5.000 USD. În cele din urmă, rulați codul sursă de mai sus utilizând următoarea comandă, cu condiția să aveți instalate pachetele Flask și Flask-SQLAlchemy:
pitonbani.py
Deci aveți aplicația web Flask care face un proces simplu de extracție. Această aplicație poate efectua următoarele operațiuni cu link-uri de solicitare GET. Deoarece Flask rulează implicit pe portul 5000, adresa pe care îl accesați este 127.0.0.1:5000/. Aplicația oferă următoarele puncte finale:
- 127.0.0.1:5000/ afișează soldul curent.
- 127.0.0.1:5000/send/{amount} scade suma din cont.
- 127.0.0.1:5000/resetare resetează contul la 5.000 USD.
Acum, în această etapă, puteți examina cum apare vulnerabilitatea condiției de cursă.
Probabilitatea unei vulnerabilități a condițiilor de cursă
Aplicația web de mai sus conține o posibilă vulnerabilitate a condiției de cursă.
Imaginați-vă că aveți 5.000 USD pentru a începe și creați două solicitări HTTP diferite care vor trimite 1 USD. Pentru aceasta, puteți trimite două solicitări HTTP diferite către link 127.0.0.1:5000/send/1. Să presupunem că, de îndată ce serverul web procesează prima cerere, CPU oprește acest proces și procesează a doua cerere. De exemplu, primul proces se poate opri după rularea următoarei linii de cod:
cont.sumă = int(cont.suma) - suma
Acest cod a calculat un nou total, dar nu a salvat încă înregistrarea în baza de date. Când începe a doua solicitare, va efectua același calcul, scăzând 1 USD din valoarea din baza de date - 5.000 USD - și stochând rezultatul. Când se reia primul proces, acesta își va stoca propria valoare – 4.999 USD – care nu va reflecta cel mai recent sold al contului.
Deci, două solicitări au fost finalizate și fiecare ar fi trebuit să scadă 1 USD din soldul contului, rezultând un nou sold de 4.998 USD. Dar, în funcție de ordinea în care serverul web le procesează, soldul final al contului poate fi de 4.999 USD.
Imaginați-vă că trimiteți 128 de solicitări pentru a efectua un transfer de 1 USD către sistemul țintă într-un interval de timp de cinci secunde. Ca rezultat al acestei tranzacții, extrasul de cont estimat va fi de 5.000 USD - 128 USD = 4.875 USD. Cu toate acestea, din cauza condițiilor de cursă, soldul final poate varia între 4.875 USD și 4.999 USD.
Programatorii sunt una dintre cele mai importante componente ale securității
Într-un proiect software, ca programator, ai destul de multe responsabilități. Exemplul de mai sus a fost pentru o aplicație simplă de transfer de bani. Imaginați-vă că lucrați la un proiect software care gestionează un cont bancar sau backend-ul unui site mare de comerț electronic.
Trebuie să fiți familiarizați cu astfel de vulnerabilități, astfel încât programul pe care l-ați scris pentru a le proteja să nu aibă vulnerabilități. Acest lucru necesită o responsabilitate puternică.
O vulnerabilitate a condiției de rasă este doar una dintre ele. Indiferent de tehnologia pe care o utilizați, trebuie să fiți atenți la vulnerabilitățile din codul pe care îl scrieți. Una dintre cele mai importante abilități pe care le puteți dobândi ca programator este familiarizarea cu securitatea software.