Problem: etibarlı istifadəçi başqasının invoice-unu oxuyur

API GET /invoices/{id} endpoint-ini açır. Login tələb olunur və security konfiqurasiyası anyRequest().authenticated() deyir. Müştəri görür ki, URL-dəki nömrəni dəyişəndə başqa müştərilərə aid invoice-lar qayıdır. Autentifikasiya mükəmməl işləyib: server çağıranın kim olduğunu bilirdi. Çatışmayan sual bu idi: "bu çağıran bu obyekti görə bilərmi?"

Bu cür qüsur geniş yayılıb, çünki heç bir tək sətirdə bug kimi görünmür. OWASP API Security Top 10 ən çox rast gəlinən API risk kateqoriyalarının açıq siyahısıdır və review üçün yaxşı yoxlama siyahısıdır. Bu yazı backend mühəndislərinin ən çox rastlaşdığı kateqoriyaları və onların qarşısını alan kod səviyyəli vərdişləri əhatə edir.

Broken object level authorization

Siyahı bunu broken object level authorization adlandırır: endpoint çağırandan identifikator alır və çağıranın məhz həmin obyektə toxunmağa icazəsi olub-olmadığını yoxlamadan obyekti qaytarır və ya dəyişir.

// Zəif: istənilən autentifikasiya olunmuş istifadəçi istənilən invoice-u oxuyur@GetMapping("/invoices/{id}")InvoiceResponse get(@PathVariable Long id) {    return invoiceService.get(id);}

İcazə qərarını məlumata girişin hissəsinə çevirərək düzəldin; identifikasiya request parametrindən yox, security context-dən götürülür:

@GetMapping("/invoices/{id}")InvoiceResponse get(@PathVariable Long id, @AuthenticationPrincipal UserPrincipal user) {    return invoiceService.getForCustomer(id, user.customerId());}// service/repositoryOptional<Invoice> findByIdAndCustomerId(Long id, Long customerId);

Heç nə uyğun gəlmirsə, 404 qaytarın (və ya açıq olmağı üstün tutursunuzsa 403), obyektin mövcudluğu özü həssasdırsa, "yoxdur" və "sizin deyil" üçün eyni cavabı verin. Spring Security-nin @PreAuthorize ilə @PostAuthorize və ya custom permission evaluator qaydanı deklarativ ifadə edə bilər; hansını seçsəniz seçin, yoxlama obyekt identifikatoru qəbul edən hər endpoint-də, o cümlədən update və delete-də tətbiq olunmalıdır.

Qeyri-şəffaf və ya təsadüfi identifikatorlar (UUID) təxmini çətinləşdirir, amma avtorizasiya deyil. Onlar ikinci qatdır, əvəzedici deyil.

Function level və property level problemlər

Adı ilə tanımağa dəyər iki qohum var: broken function level authorization, burada yalnız UI-də marşrut gizlədildiyi üçün adi istifadəçi administrativ endpoint-i çağıra bilir; və broken object property level authorization, buraya həddindən artıq məlumat ifşası və mass assignment daxildir.

Mass assignment request body-nin birbaşa entity-yə və ya geniş DTO-ya bağlanması zamanı baş verir, nəticədə client heç vaxt idarə etməməli olduğu sahələri təyin edə bilir:

// Zəif: client {"name":"Ana","role":"ADMIN"} göndərir@PutMapping("/me")User update(@RequestBody User user) { return users.save(user); }

İstifadəçinin dəyişə biləcəyi sahələri ehtiva edən ayrıca giriş tipi ilə düzəldin və onları açıq şəkildə map edin:

public record UpdateProfileRequest(@NotBlank @Size(max = 100) String name,                                   @Size(max = 500) String bio) {}@PutMapping("/me")ProfileResponse update(@Valid @RequestBody UpdateProfileRequest request,                       @AuthenticationPrincipal UserPrincipal user) {    return profileService.update(user.id(), request);}

Eyni intizam çıxışa da aiddir: entity yox, response DTO qaytarın ki, daxili sahələr heç vaxt sızmasın.

Function səviyyəli yoxlamalar serverdə olmalıdır: admin marşrutlarını hasRole("ADMIN") və ya method security ilə qoruyun və adi istifadəçinin 403 aldığını test edin.

Rate limiting və resurs istehlakı

Siyahı bunu unrestricted resource consumption adlandırır. Limit olmadıqda bir client qəsdən və ya təsadüfən CPU-nu, yaddaşı, baza connection-larını və ya ödənişli üçüncü tərəf kvotasını tükədə bilər. Nəzarətlər:

  • Gateway-də və ya tətbiqdə client və ya API key üzrə rate limit; login, parol sıfırlama və digər bahalı və ya həssas endpoint-lərdə daha sərt.
  • Maksimum request body ölçüsü və maksimum səhifə ölçüsü (size serverdə məhdudlaşdırılır, məsələn bir neçə yüzlə).
  • Hər xarici çağırışda və uzun sorğularda timeout.
  • Bahalı əməliyyatlar üçün məhdud paralellik.
  • Düzgün client-lərin geri çəkilə bilməsi üçün Retry-After header-i ilə 429 kimi cavab.

Əlaqəli kateqoriya, unrestricted access to sensitive business flows, qanuni funksiyaların kütləvi sui-istifadəsini əhatə edir, məsələn kütləvi hesab yaradılması və ya məhdud stokun toplu alınması. Rate limit kömək edir, amma bizneslə bağlı limitlər (müştəriyə bir kupon) daha vacibdir.

Giriş validasiyası

Bütün girişə etibarsız kimi yanaşın: body, query, path, header-lər və çağırdığınız digər sistemlərdən gələn məlumat.

  • Tipi, uzunluğu, diapazonu və formatı validasiya yazısında təsvir olunduğu kimi Bean Validation ilə yoxlayın.
  • Parametrləşdirilmiş sorğular və ya JPA parametrləri işlədin. SQL və ya JPQL-i string birləşdirməsi ilə heç vaxt qurmayın.
  • İstifadəçi mətnini ORDER BY-a ötürmək əvəzinə sort sahələri və filter adları üçün allow-list işlədin.
  • Serverinizin istifadəçinin verdiyi URL-i gətirdiyi hər funksiyaya ehtiyatlı olun. Bu, server-side request forgery-dir: sxemi və host-u allow-list ilə yoxlayın və daxili ünvan aralıqlarını bloklayın.
private static final Set<String> SORTABLE = Set.of("createdAt", "total");Sort sort(String field) {    if (!SORTABLE.contains(field)) throw new BadRequestException("Unsupported sort field");    return Sort.by(field);}

Autentifikasiya və sirlər

Broken authentication zəif login axınlarını, brute-force qorumasının olmamasını, uzunömürlü token-ləri və zəif token yoxlamasını əhatə edir; JWT və session yazısına baxın. Sirlər üçün:

  • Açarları, parolları və token-ləri mənbə koduna və image-lərə qoymayın. Onları mühitdən və ya secret manager-dən inject edin.
  • Hər mühit üçün fərqli sirlər işlədin və onları rotate edin; tətbiqi elə dizayn edin ki, rotation yenidən build tələb etməsin.
  • Sirləri heç vaxt API cavablarında qaytarmayın və proxy-lərin və log-ların qeyd etdiyi URL-lərə qoymayın.
  • Parolları Spring Security-nin təqdim etdiyi yavaş, duzlu alqoritmlə saxlayın (BCryptPasswordEncoder, Argon2), heç vaxt sadə sürətli hash ilə yox.

Şəxsi məlumat olmadan logging

Log-lar çox yerə kopyalanır və uzun müddət saxlanır. İdentifikatorları (request id, daxili user id), hadisə adlarını və nəticələri log-layın. Parolları, token-ləri, tam kart nömrələrini, tam request body-lərini və lazımsız şəxsi məlumatı log-lamayın. Saxlamalı olduğunuzu maskalayın. Xəta cavablarında stack trace və ya SQL olmadığına əmin olun, xəta idarəsi yazısında göstərildiyi kimi. Hücumlar görünsün deyə təhlükəsizlik üçün əhəmiyyətli hadisələri (uğursuz login-lər, icazə rədləri, rate limit-ə dəymələr) log-layın.

Konfiqurasiya və inventar

Security misconfiguration uzun quyruqdur: açıq qalmış debug endpoint-lər, icazəli CORS (credential ilə *), default credential-lar, çatışmayan security header-lər, çox ətraflı xətalar, lazımsız HTTP metodları. Yalnız lazım olan actuator endpoint-lərini açın və onları qorunan port və ya path-də saxlayın. Improper inventory management unudulmuş köhnə API versiyalarının və sənədləşməmiş endpoint-lərin hələ də işləməsi deməkdir; nəyin deploy olunduğunun siyahısını saxlayın və istifadə olunmayanı bağlayın. Unsafe consumption of APIs xatırladır ki, üçüncü tərəf servislərin cavabları da yoxlanmalı girişdir.

Review siyahısı

  1. İdentifikator qəbul edən hər endpoint üçün: sahiblik və ya icazə yoxlaması haradadır?
  2. Hər yazı endpoint-i üçün: client dəqiq hansı sahələri təyin edə bilər?
  3. Hər cavab üçün: daxili sahə sızmaq ehtimalı varmı?
  4. Yalnız admin funksiyaları serverdə qorunur və testlə əhatə olunur?
  5. Rate, body ölçüsü, səhifə ölçüsü və vaxt üçün hansı limitlər var?
  6. Hər sorğu parametrləşdirilib və hər sort və ya filter sahəsi allow-list-dədir?
  7. Sirlər haradan gəlir və rotate etmək mümkündür?
  8. Log-lar və xəta body-ləri şəxsi məlumatdan, token-lərdən və stack trace-dən təmizdir?
  9. Açıq endpoint-lərin və versiyaların siyahısı var?
  10. Test hücumu sınayır: A istifadəçisi B-nin obyektini istəyir?

Tez-tez edilən səhvlər

  1. UI-də düymələri gizlədib bunu avtorizasiya saymaq.
  2. Client-in göndərdiyi identifikasiya dəyərlərinə (user id, rol) etibar etmək.
  3. Sahiblik yoxlamasını bəzi endpoint-lərdə controller-də edib digərlərini unutmaq. Onu ortaq service və ya repository metoduna çəkin.
  4. Gateway-in tətbiqi təhlükəsiz etdiyinə inanmaq. Gateway 42 nömrəli invoice-un kimə aid olduğunu bilə bilməz.
  5. Siyahıya düşməyən URL və ya çətin təxmin olunan id kimi gizlilik üzərində qurmaq.
  6. Təhlükəsizliyə funksiya ilə birlikdə yazılan testlər əvəzinə son review kimi baxmaq.

Trade-off-lar

Sərt yoxlamalar kod, bəzən də performans tələb edir; scope-lanmış sorğular (findByIdAndCustomerId) əlavə xərc tələb etmir və ən ucuz güclü düzəlişdir. Aqressiv rate limit-lər sistemi qoruyur, amma qanuni partlayışları bloklaya bilər; müşahidə olunan istifadədən başlayın və tənzimləyin. Ətraflı xəta mesajları inteqrasiya edənlərə kömək edir, hücumçulara da; təfərrüatı autentifikasiya olunmamış çağıranlara göstərilən mətndə yox, kodlarda və log-larda saxlayın.

Sonra nə oxumalı