SQL Injection — врста напада на базу података апликације при којем нападач убацује злонамерни SQL код у параметре упита, добијајући неовлашћени приступ подацима или могућност њихове измене. Према подацима OWASP (2025), SQL Injection остаје једна од критичних рањивости, способна да доведе до потпуне компромитације базе података. Инјекција SQL кода омогућава нападачу да чита, мења и брише записе, ау неким случајевима — да добије приступ оперативном систему сервера.
Главно
SQL Injection је рањивост која настаје када апликација формира SQL упит путем конкатенације стрингова са корисничким подацима. Нападач преноси у параметар упита посебно конструисан стринг који мења структуру SQL команде. Уместо да буде податак, унети стринг постаје део SQL кода, што омогућава нападачу да извршава произвољне упите бази података. Према Verizon Data Breach Report (2025), SQL Injection је присутан у 8% свих истражених цурења података.
Упркос познатости рањивости (први помени — крај 1990-их), SQL Injection се јавља у савременим апликацијама. Разлог — људски фактор: програмери допуштају конкатенацију стрингова у коду, legacy код се не рефакторише, а ORM оквири се користе неправилно (нпр. сирови упити са заменом вредности кроз стрингове). Према подацима Veracode (2026), око 14% свих скенираних апликација садржи бар једну SQLi рањивост.
Успешна SQL Injection даје нападачу широк спектар могућности: читање било којих табела базе података, укључујући хешеве лозинки и личне податке корисника; измена и брисање записа; извршавање административних операција (DROP TABLE, TRUNCATE); у неким конфигурацијама — даљинско извршавање команди преко xp_cmdshell (MSSQL) или INTO OUTFILE (MySQL). Последице — од цурења корисничких података до потпуног губитка контроле над системом.
SQL инјекције се класификују према начину извлачења података из базе. Избор метода зависи од тога како апликација обрађује резултате упита и поруке о грешкама. OWASP класификација издваја три главна типа: In-band (подаци се извлаче кроз исти канал), Inferential/Blind (логички закључци) и Out-of-band (подаци се преносе кроз други канал).
| Тип | Начин извлачења података | Сложеност | Учесталост |
|---|---|---|---|
| In-band (класични) | Непосредно кроз резултат упита | Ниска | Висока |
| Blind SQLi | Логички закључци на основу одговора сервера | Висока | Средња |
| Out-of-band | Кроз спољни канал (DNS, HTTP) | Средња | Ниска |
Најчешћи тип. Нападач убацује SQL код у параметар упита, а резултат убацивања је видљив директно у одговору сервера. Два подтипа: Error-based (кроз поруке о грешкама базе података) и UNION-based (кроз оператор UNION SELECT). Error-based користи информације из порука о грешкама, на пример синтаксну грешку MySQL која може да открије назив табеле или структуру упита. UNION-based омогућава спајање резултата легитимног упита са подацима из других табела базе података.
Користи се када апликација не приказује резултате упита или поруке о грешкама. Нападач поставља питања типа „да/не”, шаљући упите са логичким условима и анализирајући разлику у одговору сервера (нпр. време одговора или садржај странице). Time-based Blind SQLi користи функцију кашњења (SLEEP, WAITFOR DELAY) за потврду услова — ако се страница дуже учитава, услов је тачан. Овај метод је веома спор — извлачење једног записа може трајати сатима.
# Пример Blind SQL Injection (time-based)
# Ако је SQLi рањив, SLEEP(2) се извршава под условом
import requests
import time
payload = "' OR IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a',SLEEP(2),0) -- "
url = "http://example.com/user?id=1" + payload
start = time.time()
response = requests.get(url)
elapsed = time.time() - start
# Ако је одговор стигао после >2 секунде — прво слово лозинке је 'a'
print(f"Time: {elapsed:.2f}s - First letter is 'a'" if elapsed > 2 else "First letter is not 'a'")
Подаци се преносе не кроз HTTP одговор, већ кроз алтернативне канале: DNS упите, HTTP захтеве ка спољном серверу, SMTP. Користи се када апликација не враћа резултате упита и не приказује грешке. MySQL подржава функцију LOAD_FILE(), која може да иницира DNS упит, а MSSQL — xp_dirtree за слање података на удаљени SMB сервер. Out-of-band SQL Injection је ефикасан, али захтева додатна подешавања на серверу нападача и присуство одређених функција базе података.
Механизам SQL Injection се заснива на томе што SQL користи наводнике за стринг литерале. Ако апликација замењује кориснички унос директно у SQL упит без екранизације, нападач може да „затвори” стринг и дода произвољан SQL код. На пример, у упиту SELECT * FROM users WHERE name = '$input' замена ' OR '1'='1 претвара упит у SELECT * FROM users WHERE name = '' OR '1'='1', што враћа све кориснике.
Размотримо формулар за пријаву са упитом SELECT * FROM users WHERE username = '$user' AND password = '$pass'. Ако нападач унесе у поље username admin' -- , а лозинку остави празну, резултујући упит постаје SELECT * FROM users WHERE username = 'admin' -- ' AND password = ''. Знакови -- коментаришу остатак упита, а услов провере лозинке се искључује. Сервер враћа запис корисника admin, а нападач улази у систем без познавања лозинке.
# Пример SQL Injection — заобилажење аутентификације
# РАЊИВИ КОД: директна конкатенација стрингова
def login_vulnerable(username, password):
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
cursor.execute(query)
return cursor.fetchone() is not None
# username = "admin' --" поништава проверу лозинке
# БЕЗБЕДНА ВЕРЗИЈА: параметризовани упит
def login_secure(username, password):
query = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(query, (username, password))
return cursor.fetchone() is not None
Оператор UNION SELECT омогућава спајање резултата два SELECT упита. Ако нападач пронађе рањиви параметар, додаје UNION SELECT са упитом из друге табеле. На пример: ' UNION SELECT username, password FROM admins --. Услов успеха — број колона у оба упита мора да се поклапа. Број колона се одређује кроз ORDER BY (замена ' ORDER BY 1--, затим 2, 3... до грешке). Знајући број колона, нападач замењује UNION SELECT са истим бројем поља.
Мобилне апликације се сусрећу са SQL Injection како на серверској страни (API), тако и на клијенту — у локалним базама података (SQLite, Realm). Иако је ризик од серверске SQLi у мобилном API-ју сличан веб апликацијама, локална база података ствара додатни вектор. Ако апликација чува податке у SQLite-у и извршава упите са конкатенацијом стрингова, злонамерни подаци који су доспели у локалну базу података кроз API могу изазвати SQLi приликом касније обраде.
Локална SQLite база на уређају је такође рањива на SQL Injection ако апликација формира упите конкатенацијом стрингова. Content Providers на Android-у и Core Data на iOS-у користе параметризацију подразумевано, али сирови упити захтевају пажњу програмера. SQLite не подржава вишеструке упите кроз тачку-запету, што ограничава могућности нападача, али не штити од читања података кроз WHERE услове. Увек користите selectionArgs у Android-у и NSPredicate са параметрима у iOS-у.
API коме се обраћа мобилна апликација рањив је исто као и сваки веб сервер. Мобилни програмери често сматрају да је SQLi само проблем бекенда, али рањивост настаје на нивоу API ендпоинта који прима параметре од клијента. Подела одговорности не штити: ако је бекенд програмер заборавио да параметризује упит, мобилна апликација корисника постаје вектор напада. Захтевајте од бекенда коришћење ORM-а или prepared statements.
// Пример SQL Injection у локалном SQLite-у на Android-у
// РАЊИВИ КОД: директна конкатенација
fun getUserVulnerable(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = $userId", null
)
}
// БЕЗБЕДНА ВЕРЗИЈА: параметризација кроз selectionArgs
fun getUserSecure(db: SQLiteDatabase, userId: String): Cursor {
return db.rawQuery(
"SELECT * FROM users WHERE id = ?",
arrayOf(userId)
)
}
Заштита од SQL Injection се заснива на једноставном принципу: никада не верујте корисничком уносу у SQL упитима. Једини поуздани метод — параметризација упита (prepared statements), при којој се SQL код и подаци преносе одвојено. Сви остали методи — екранизација, валидација, WAF — представљају додатне слојеве заштите, али не замењују параметризацију. Према подацима OWASP (2025), параметризација спречава 100% SQL Injection напада.
При коришћењу prepared statements, SQL упит се прво компајлира на серверу базе података без података, а затим се вредности параметара преносе одвојено. База података третира параметре као податке, а не као извршиви код. Чак и ако нападач пренесе ' OR '1'='1, база података то интерпретира као стринг вредност, а не као SQL код. Prepared statements подржавају сви савремени језици и оквири: PDO у PHP-у, PreparedStatement у Java-и, cursor.execute у Python-у.
Савремени ORM (Hibernate, Entity Framework, SQLAlchemy, Room) аутоматски користе параметризацију при извршавању упита, ако програмер не пређе на сирове упите. Међутим, ORM не штити у потпуности: конструкције попут @Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true) у JPA захтевају пренос параметара кроз именоване параметре, а не конкатенацију. Query Builders (Knex, jOOQ) такође параметризују упите подразумевано, ако се не користе сирови методи.
Екранизација специјалних знакова (mysql_real_escape_string) — застарели метод који не штити од свих типова SQL Injection. Проблем: екранизација зависи од кодирања и може се заобићи коришћењем вишебајтних кодирања (нпр. GBK у азијским системима). Користите екранизацију само у legacy коду где параметризација није могућа, и увек у комбинацији са строгом валидацијом типа улазних података.
| Метод | Ефикасност | Препорука |
|---|---|---|
| Prepared Statements | 100% | Обавезно за све упите |
| ORM (правилна употреба) | 99% | Препоручује се |
| Екранизација стрингова | 70% (зависи од кодирања) | Само legacy |
| Валидација уноса (бела листа) | 50% (само за бројеве) | Додатно |
| WAF (Web Application Firewall) | 60% | Додатно |
Кориснички налог апликације треба да има минимално неопходна права: SELECT, INSERT, UPDATE, DELETE — само на табелама које су стварно потребне апликацији. Забраните коришћење DROP, TRUNCATE, CREATE за налог апликације. Ово ће ограничити штету чак и у случају успешне SQL Injection: нападач неће моћи да обрише табеле или изврши административне операције.
Редовно тестирање на SQL Injection треба да буде део pipeline-а безбедног развоја. Комбинација статичке анализе, динамичког скенирања и ручног пентеста даје најбоље резултате. Према Synopsys Cybersecurity Report (2025), аутоматски скенери проналазе до 70% SQLi рањивости, али сложени Blind напади захтевају ручно тестирање.
За мобилне апликације важна је и анализа локалног SQLite-а: проверите све rawQuery позиве, ContentProvider упите и Room упите са rawQuery. Алати: Android Studio Lint (детектује SQLi у SQLite-у), MobSF (Mobile Security Framework) за аутоматску статичку и динамичку анализу APK/IPA. Препоручује се и тестирање API ендпоинта путем sqlmap-а са прокси пресретањем саобраћаја мобилне апликације.
Често постављана питања
SQL Injection напада релационе базе података кроз SQL упите. NoSQL Injection утиче на нерелационе базе података (MongoDB, Couchbase) кроз њихове операторе упита ($gte, $ne, $where). У MongoDB-ју инјекција је могућа ако апликација формира BSON документ из JSON стринга. Механизми заштите су слични: параметризација и валидација типова.
Пронађите сва места где се SQL упити формирају кроз конкатенацију стрингова са корисничким подацима. Тражите обрасце попут "SELECT ... WHERE id = " + userId или f"UPDATE ... SET name = '{name}'". Сваки такав стринг је потенцијална SQL инјекција. Замените све параметризованим упитима или prepared statements.
ORM оквири штите аутоматски само ако користите њихове методе Query Builder-а и именоване параметре. Ако користите сирове упите (nativeQuery у JPA, rawQuery у Room-у), заштита не ради — потребно је преносити параметре кроз припремљене изразе, а не кроз конкатенацију стрингова.
Second-Order SQL Injection — напад при којем се злонамерни подаци чувају у бази података као безбедни, али се затим користе у другом упиту без екранизације. На пример, нападач се региструје са username-ом ' OR '1'='1. Подаци се чувају као стринг — при регистрацији нема напада. Али ако други упит замени username у SQL-у без параметризације, инјекција се активира.
NoSQL Injection је потенцијално опаснији због мање свести програмера. Програмери знају за SQL Injection и већина користи ORM, али за NoSQL Injection знају ретки. У MongoDB-ју неправилно формиран упит може вратити све документе колекције. Заштита — исти prepared statements (BSON параметризација) и строга валидација улазних података.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође