Advanced security

01.07.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Spring Boot | 25 daqiqa o'qish

Advanced security - Spring Boot’da faqat "login va JWT" qilish emas. Senior darajada biz authentication, authorization, token lifecycle, tenant izolatsiyasi, secret management, browser security headers va production threat model’ni tushunishinishimiz kerak.

Bu blok quyidagilarni o‘z ichiga oladi: Spring Authorization Server, token introspection, opaque tokens, multi-tenancy security, row-level security, Vault + Spring Cloud Vault, CSP/HSTS.


1. Security’da asosiy farq: Authentication vs Authorization

Authentication

Kimligini aniqlash.

Masalan:

Bu user kim?
Token haqiqiymi?
Login/password to‘g‘rimi?
JWT validmi?

Misol:

User: Ali
Authenticated: true

Authorization

Nima qilishga ruxsati borligini aniqlash.

Masalan:

Ali admin panelga kira oladimi?
Ali boshqa tenant ma’lumotini ko‘ra oladimi?
Ali payment refund qila oladimi?

Misol:

User: Ali
Role: MANAGER
Permission: ORDER_READ
Tenant: company_12

Senior darajada eng katta xato:

Authentication bor, demak xavfsiz.

Yo‘q. User login qilgan bo‘lsa ham, har bir action uchun authorization alohida tekshirilishi kerak.


2. Oddiy JWT security qayerda yetmay qoladi?

Junior/Middle darajada ko‘pincha shunday qilinadi:

Login → JWT beriladi → Har request’da JWT tekshiriladi

Bu kichik loyihalarda yetarli bo‘lishi mumkin.

Lekin katta system’da savollar ko‘payadi:

Tokenni kim chiqaradi?
Tokenni qanday bekor qilamiz?
Refresh token qayerda saqlanadi?
Multiple client bormi: mobile, web, admin panel?
OAuth2 kerakmi?
Token ichida permission ko‘paysa nima bo‘ladi?
Tenant security qayerda tekshiriladi?
Token o‘g‘irlansa nima qilamiz?

Shu yerda advanced security boshlanadi.


3. Spring Authorization Server

Nima?

Spring Authorization Server - OAuth2 Authorization Server qurish uchun ishlatiladi.

Oddiy qilib:

Authorization Server token chiqaradi.
Resource Server tokenni tekshiradi.
Client token bilan API chaqiradi.

Architecture:

User
 ↓
Client App
 ↓ login
Authorization Server
 ↓ token
Client App
 ↓ Bearer token
Resource Server / API

Qaysi holatda kerak?

Spring Authorization Server kerak bo‘lishi mumkin:

- O‘zingizning OAuth2 provider’ingiz kerak bo‘lsa
- Mobile, web, admin kabi bir nechta client bo‘lsa
- Access token / refresh token lifecycle kerak bo‘lsa
- SSO kerak bo‘lsa
- Internal platform security markazlashtirilsa
- Microservice ecosystem’da token issuing alohida bo‘lishi kerak bo‘lsa

Kerak bo‘lmasligi mumkin:

- Oddiy bitta backend + bitta frontend bo‘lsa
- Keycloak/Auth0/Okta kabi tayyor provider ishlatilsa
- OAuth2 flow’larni to‘liq tushunmasangiz

Senior qaror:

Auth server yozish - katta mas’uliyat.
Imkon bo‘lsa, Keycloak/Auth0/Okta kabi battle-tested yechimlarni ham ko‘rib chiqish kerak.

Minimal tushuncha

OAuth2'da asosiy rollar:

Role

Vazifasi

Resource Owner

User

Client

Frontend/mobile/backend client

Authorization Server

Token chiqaradi

Resource Server

API, tokenni tekshiradi

Misol:

Ali mobile app’da login qiladi.
Mobile app Authorization Server’dan token oladi.
Mobile app Order API’ga token bilan request yuboradi.
Order API tokenni tekshiradi.

4. Resource Server

Ko‘p Spring Boot service’lar Resource Server sifatida ishlaydi.

Ya’ni ular token chiqarmaydi, faqat tokenni tekshiradi.

Gateway → order-service → payment-service

Har bir service:

Authorization: Bearer <token>

header’ni tekshiradi.


JWT Resource Server config

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/actuator/health").permitAll()
                        .requestMatchers("/api/admin/**").hasRole("ADMIN")
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(Customizer.withDefaults())
                )
                .build();
    }
}

application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.company.uz

Bu service tokenni issuer-uri orqali tekshiradi.


5. JWT token

JWT nima?

JWT - self-contained token.

Ichida claim’lar bo‘ladi:

{
  "sub": "user-123",
  "tenant_id": "company-12",
  "roles": ["ADMIN"],
  "scope": "orders.read orders.write",
  "iat": 1710000000,
  "exp": 1710003600
}

JWT’ning afzalligi:

Resource Server tokenni local tekshirishi mumkin.
Har request’da auth serverga borish shart emas.

Kamchiligi:

Token chiqarilgandan keyin uni bekor qilish qiyinroq.
Token ichidagi ma’lumot token tugaguncha yashaydi.

JWT ichiga nima qo‘ymaslik kerak?

Yomon:

Password
Card number
Passport data
Sensitive personal info
Juda katta permission list

Yaxshi:

sub
tenant_id
scope
role
exp
issuer
audience

Token minimal bo‘lishi kerak.


6. Opaque token

Nima?

Opaque token - ichida ma’lumot ko‘rinmaydigan token.

Masalan:

7f8a9c3d91b44e0a9c...

Resource Server bu tokenni o‘zi decode qilolmaydi. Auth Server’dan so‘raydi:

Bu token validmi?
Kimga tegishli?
Scope nima?

JWT vs Opaque token

Mezoni

JWT

Opaque token

Ichida claim bor

Ha

Yo‘q

Local verify

Ha

Yo‘q

Auth serverga so‘rov

Shart emas

Kerak

Revoke qilish

Qiyinroq

Osonroq

Performance

Tezroq

Sekinroq

Security control

O‘rtacha

Kuchliroq

Token hajmi

Kattaroq

Kichikroq


Qachon opaque token yaxshi?

- Tokenni tez revoke qilish kerak
- Security control markazlashtirilgan bo‘lsa
- Token ichida data ko‘rinmasligi kerak bo‘lsa
- High-risk API bo‘lsa
- Session-like control kerak bo‘lsa

7. Token introspection

Nima?

Token introspection - Resource Server tokenni Authorization Server’dan tekshirtiradi.

Flow:

Client → Resource Server: Bearer opaque-token
Resource Server → Auth Server: token validmi?
Auth Server → Resource Server: active=true, sub=user-123, scope=orders.read
Resource Server → Client: response

Spring config

spring:
  security:
    oauth2:
      resourceserver:
        opaque-token:
          introspection-uri: https://auth.company.uz/oauth2/introspect
          client-id: order-service
          client-secret: secret

Security config:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/actuator/health").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                    .opaqueToken(Customizer.withDefaults())
            )
            .build();
}

Introspection xavfi

Har request’da auth serverga borilsa:

Latency oshadi
Auth server bottleneck bo‘ladi
Auth server down bo‘lsa API ham muammo ko‘radi

Shuning uchun:

- Short cache ishlatish mumkin
- Critical endpointlarda qo‘llash mumkin
- JWT bilan hybrid model qilish mumkin

8. Token revocation

Muammo

User logout qildi yoki token o‘g‘irlandi.

JWT bo‘lsa:

Token exp tugamaguncha valid bo‘lishi mumkin.

Yechimlar:

Yechim

Tushuntirish

Short-lived access token

Access token 5-15 minut yashaydi

Refresh token rotation

Har refresh’da yangi refresh token

Token blacklist

Revoke qilingan token ID saqlanadi

Opaque token

Introspection orqali markaziy nazorat

Session version

User session version claim bilan tekshiriladi


JWT uchun jti

JWT ichida jti - token ID saqlash mumkin.

{
  "sub": "user-123",
  "jti": "token-abc-123",
  "exp": 1710003600
}

Logout bo‘lsa:

jti blacklist’ga yoziladi.

Har request’da:

jti blacklist’da bormi?
ha → reject
yo‘q → allow

Lekin bu JWT’ning "stateless" foydasini kamaytiradi.


9. Multi-tenancy security

Nima?

Multi-tenant system’da bir application bir nechta company/client data’sini saqlaydi.

tenant_1: Company A
tenant_2: Company B
tenant_3: Company C

Eng katta xavf:

Company A user’i Company B ma’lumotini ko‘rib qo‘yishi.

Bu critical security bug.


Tenant ID qayerdan olinadi?

Variantlar:

Joy

Misol

JWT claim

tenant_id=company-12

Subdomain

company12.app.uz

Header

X-Tenant-Id: company-12

Path

/tenant/company-12/orders

Senior tavsiya:

Tenant ID’ni faqat client yuborgan header’dan ko‘r-ko‘rona olmang.
Token claim yoki server-side mapping bilan tasdiqlang.

Yomon:

X-Tenant-Id: company-999

Agar user aslida company-12ga tegishli bo‘lsa, bu request reject qilinishi kerak.


TenantContext

public final class TenantContext {

    private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();

    public static void setTenantId(String tenantId) {
        CURRENT_TENANT.set(tenantId);
    }

    public static String getTenantId() {
        return CURRENT_TENANT.get();
    }

    public static void clear() {
        CURRENT_TENANT.remove();
    }
}

Filter:

@Component
public class TenantFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
    ) throws ServletException, IOException {

        try {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

            if (authentication instanceof JwtAuthenticationToken jwtAuth) {
                String tenantId = jwtAuth.getToken().getClaimAsString("tenant_id");
                TenantContext.setTenantId(tenantId);
            }

            filterChain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }
}

Muhim:

finally {
    TenantContext.clear();
}

Aks holda thread pool sabab boshqa request eski tenant context’ni ko‘rib qolishi mumkin.


10. Row-level security

Nima?

Row-level security - database darajasida row’larni tenant yoki user bo‘yicha cheklash.

Application-level filter:

SELECT * FROM orders WHERE tenant_id = ?;

DB-level security:

Database o‘zi tenant_id bo‘yicha ruxsatni nazorat qiladi.

Application-level tenant filtering

Repository:

@Query("""
    select o from Order o
    where o.tenantId = :tenantId
      and o.id = :orderId
""")
Optional<Order> findByIdAndTenantId(
        @Param("orderId") Long orderId,
        @Param("tenantId") String tenantId
);

Service:

public OrderResponse getOrder(Long orderId) {
    String tenantId = TenantContext.getTenantId();

    Order order = orderRepository.findByIdAndTenantId(orderId, tenantId)
            .orElseThrow(() -> new AccessDeniedException("Order not found"));

    return mapper.toResponse(order);
}

E’tibor bering:

Order topilmadi

deb qaytarish ko‘pincha:

Access denied

dan xavfsizroq. Chunki boshqa tenant’da shunaqa order borligini oshkor qilmaydi.


Global filter yondashuvi

Hibernate’da tenant filter qilish mumkin:

@FilterDef(name = "tenantFilter", parameters = @ParamDef(name = "tenantId", type = String.class))
@Filter(name = "tenantFilter", condition = "tenant_id = :tenantId")
@Entity
public class Order {
    @Id
    private Long id;

    private String tenantId;
}

Lekin Senior darajada ehtiyot bo‘lish kerak:

- Har session’da filter yoqilganmi?
- Native query’larda ishlaydimi?
- Batch joblarda tenant context bormi?
- Admin cross-tenant query’lari qanday ishlaydi?

11. Method-level security

Endpoint’da security borligi yetmaydi.

@GetMapping("/orders/{id}")
public OrderResponse getOrder(@PathVariable Long id) {
    return orderService.getOrder(id);
}

Service layer’da ham tekshirish kerak bo‘lishi mumkin.

@PreAuthorize("hasAuthority('SCOPE_orders.read')")
public OrderResponse getOrder(Long id) {
    return ...
}

Yoki custom permission:

@PreAuthorize("@orderSecurity.canReadOrder(authentication, #id)")
public OrderResponse getOrder(Long id) {
    return orderService.getOrder(id);
}

Custom bean:

@Component
@RequiredArgsConstructor
public class OrderSecurity {

    private final OrderRepository orderRepository;

    public boolean canReadOrder(Authentication authentication, Long orderId) {
        String tenantId = extractTenantId(authentication);

        return orderRepository.existsByIdAndTenantId(orderId, tenantId);
    }

    private String extractTenantId(Authentication authentication) {
        JwtAuthenticationToken token = (JwtAuthenticationToken) authentication;
        return token.getToken().getClaimAsString("tenant_id");
    }
}

Bu ayniqsa object-level authorization uchun kerak.


12. Secrets injection: Vault + Spring Cloud Vault

Muammo

Yomon amaliyot:

spring:
  datasource:
    username: postgres
    password: super-secret-password

Yoki undan ham yomon:

Git repository ichida production password

Secret’lar source code’da turmasligi kerak.


Vault nima beradi?

Vault yoki shunga o‘xshash secret manager:

- DB password
- API key
- JWT signing key
- OAuth client secret
- Certificate

kabi maxfiy ma’lumotlarni markaziy va nazoratli saqlaydi.


Spring Cloud Vault flow

Spring Boot app start bo‘ladi
↓
Vault’ga authenticate qiladi
↓
Secret’larni oladi
↓
Environment property sifatida ishlatadi

Misol:

spring:
  cloud:
    vault:
      uri: http://localhost:8200
      token: ${VAULT_TOKEN}
      kv:
        enabled: true
        backend: secret
        application-name: order-service

Keyin app quyidagicha property olishi mumkin:

spring:
  datasource:
    password: ${db.password}

Secret management qoidalari

- Secret’ni Git’ga yozmang
- Secret’ni log qilmang
- Environment variables ham ehtiyotkorlik bilan ishlatilsin
- Rotation strategiyasi bo‘lsin
- Least privilege access bering
- Service faqat o‘ziga kerak secret’ni ko‘rsin

13. Security headers

HTTP response header’lari browser security uchun juda muhim.

Spring Security default ko‘p header’larni qo‘shadi, lekin production’da aniq policy kerak.


HSTS

HSTS - HTTP Strict Transport Security.

Bu browserga shuni aytadi:

Bu domain bilan faqat HTTPS orqali ishlagin.

Header:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Spring config:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .headers(headers -> headers
                    .httpStrictTransportSecurity(hsts -> hsts
                            .includeSubDomains(true)
                            .maxAgeInSeconds(31536000)
                    )
            )
            .build();
}

CSP

CSP - Content Security Policy.

Bu XSS xavfini kamaytiradi.

Misol:

Content-Security-Policy: default-src 'self'; script-src 'self'

Spring config:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .headers(headers -> headers
                    .contentSecurityPolicy(csp -> csp
                            .policyDirectives("default-src 'self'; script-src 'self'; object-src 'none'")
                    )
            )
            .build();
}

Boshqa foydali headers

Header

Vazifasi

X-Content-Type-Options: nosniff

Browser MIME sniffing’ni cheklaydi

X-Frame-Options: DENY

Clickjacking’dan himoya

Referrer-Policy

Referrer ma’lumotini cheklaydi

Permissions-Policy

Camera, microphone, geolocation kabi API’larni cheklaydi

Cache-Control

Sensitive response cache bo‘lib qolmasin


14. CSRF

CSRF nima?

Agar user browser’da login bo‘lgan bo‘lsa, yomon sayt uning nomidan request yuborishi mumkin.

User bank.uz’da login
↓
Yomon sayt ochildi
↓
Yomon sayt bank.uz’ga POST request yubordi

Qachon CSRF kerak?

Architecture

CSRF

Cookie-based session auth

Kerak

Server-side rendered app

Kerak

Browser cookie bilan auth

Kerak

Bearer token Authorization header

Ko‘pincha shart emas

Mobile app API

Odatda shart emas

JWT ishlatyapmiz deb CSRF’ni avtomatik o‘chirish har doim to‘g‘ri emas.

Savol:

Token qayerda saqlanyapti?
Cookie’dami yoki Authorization header’dami?

Agar JWT HttpOnly cookieda bo‘lsa, CSRF yana muhim bo‘lishi mumkin.


15. CORS

CORS security emas, browser policy.

Yomon:

configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(List.of("*"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);

Bu xavfli kombinatsiya.

Yaxshi:

@Configuration
public class CorsConfig {

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();

        config.setAllowedOrigins(List.of("https://app.company.uz"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
        config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
        config.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/api/**", config);

        return source;
    }
}

16. Password hashing

Password hech qachon plain text saqlanmaydi.

Yomon:

password = 123456

Yaxshi:

password_hash = BCrypt hash

Spring’da:

@Bean
PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

Login:

if (!passwordEncoder.matches(rawPassword, user.getPasswordHash())) {
    throw new BadCredentialsException("Invalid credentials");
}

Password hashing uchun:

BCrypt
Argon2
PBKDF2

kabi adaptive hash algoritmlar ishlatiladi.


17. Authorization model

Role-based access:

ADMIN
MANAGER
USER

Lekin katta system’da role yetmaydi.

Permission-based model yaxshiroq:

orders.read
orders.create
orders.cancel
payments.refund
users.invite

Misol:

@PreAuthorize("hasAuthority('SCOPE_orders.read')")
@GetMapping("/orders")
public List<OrderResponse> getOrders() {
    return orderService.getOrders();
}

Yoki:

@PreAuthorize("hasAuthority('payments.refund')")
@PostMapping("/payments/{id}/refund")
public RefundResponse refund(@PathVariable Long id) {
    return paymentService.refund(id);
}

18. Security audit logging

Sensitive actionlar log qilinishi kerak:

- Login success/failure
- Password change
- Role change
- Permission change
- Payment refund
- Admin action
- Tenant switch
- Token revoke

Log namunasi:

event=PAYMENT_REFUND userId=123 tenantId=company-12 paymentId=987 amount=100000 result=SUCCESS traceId=abc123

Lekin logga yozilmasin:

- Password
- Full token
- Card number
- Secret
- Private key

Token kerak bo‘lsa faqat qisqa fingerprint:

tokenHash=ab12cd34

19. Common security mistakes

1. JWT’ni localStorage’da saqlash

XSS bo‘lsa token o‘g‘irlanishi mumkin.

Yaxshiroq variantlar:

- Short-lived access token
- Refresh token HttpOnly Secure cookie’da
- CSP kuchli
- XSS protection

2. Tenant check faqat frontend’da

Yomon:

Frontend tenant dropdown’ni yashirgan.
Backend esa tekshirmaydi.

Backend har doim tenant authorization qilishi kerak.


3. hasRole("ADMIN") hamma joyga qo‘yish

Katta system’da bu qo‘pol model.

Yaxshiroq:

Permission-based access
Object-level access
Tenant-level access

4. Secret’larni Git’da saqlash

Bu production incident.


5. CORS *

Ayniqsa credentials bilan xavfli.


6. Token expiration juda uzun

Yomon:

Access token: 30 kun

Yaxshi:

Access token: 5-15 minut
Refresh token: rotation bilan

20. Advanced security checklist

[ ] Authentication va authorization ajratilganmi?
[ ] Resource Server tokenni to‘g‘ri tekshiradimi?
[ ] JWT issuer/audience validate qilinadimi?
[ ] Access token muddati qisqami?
[ ] Refresh token rotation bormi?
[ ] Token revoke strategiyasi bormi?
[ ] Tenant ID token claim bilan tekshiriladimi?
[ ] Har query tenant bo‘yicha filter qilinadimi?
[ ] Object-level authorization bormi?
[ ] Method-level security ishlatiladimi?
[ ] Secret’lar Git’da emasmi?
[ ] Vault yoki secret manager bormi?
[ ] CSP/HSTS headerlari sozlanganmi?
[ ] CSRF arxitekturaga qarab to‘g‘ri sozlanganmi?
[ ] CORS faqat kerakli originlarga ochilganmi?
[ ] Password BCrypt/Argon2 bilan hash qilinganmi?
[ ] Audit log sensitive actionlarni yozadimi?
[ ] Loglarda token/password/secret yo‘qmi?

21. Real production scenario

Vaziyat

SaaS app:

company_1
company_2
company_3

API:

GET /api/orders/500

Muammo:

company_1 user’i company_2 orderini ko‘rmasligi kerak.

Yomon service:

public OrderResponse getOrder(Long id) {
    Order order = orderRepository.findById(id)
            .orElseThrow();

    return mapper.toResponse(order);
}

Bu xavfli. Chunki id=500 boshqa tenantniki bo‘lishi mumkin.

To‘g‘ri:

public OrderResponse getOrder(Long id) {
    String tenantId = TenantContext.getTenantId();

    Order order = orderRepository.findByIdAndTenantId(id, tenantId)
            .orElseThrow(() -> new NotFoundException("Order not found"));

    return mapper.toResponse(order);
}

Repository:

Optional<Order> findByIdAndTenantId(Long id, String tenantId);

Bu advanced security’ning real mohiyati:

Authentication yetmaydi.
Data access ham tenant-aware bo‘lishi kerak.

22. Senior interview javob

Suhbatda so‘rasa:

Advanced security’da nimalarga e’tibor berasiz?

Javob:

Men authentication va authorization’ni alohida ko‘raman.
Microservice’da service’lar Resource Server bo‘ladi, tokenni issuer, audience, expiry va scope bo‘yicha tekshiradi.

Agar OAuth2 lifecycle kerak bo‘lsa, Spring Authorization Server yoki Keycloak kabi provider ishlatiladi.
JWT self-contained va tez, lekin revoke qilish qiyinroq. Opaque token introspection orqali kuchliroq nazorat beradi, lekin latency va auth server dependency oshadi.

Multi-tenant system’da tenant_id faqat client header’dan olinmaydi, token claim yoki server-side mapping bilan tekshiriladi.
Har query tenant-aware bo‘lishi kerak, object-level authorization ham service layer’da tekshiriladi.

Secret’lar Git’da saqlanmaydi, Vault yoki secret manager’dan olinadi.
Production’da CSP, HSTS, CORS, CSRF, audit logging, short-lived tokens va refresh token rotation muhim.

23. Qisqa xulosa

Advanced security’ni bitta gap bilan:

Senior security - faqat login qilish emas, balki token, tenant, permission, secret va browser-level himoyani to‘liq boshqarish.

Eng muhim fikrlar:

Mavzu

Esda qoladigan qoida

JWT

Minimal claim, qisqa muddat

Opaque token

Revoke va markaziy control kuchli

Introspection

Security kuchli, latency ko‘proq

Multi-tenancy

Tenant check backend’da majburiy

Row-level security

Query tenant-aware bo‘lsin

Vault

Secret Git’da bo‘lmasin

CSP/HSTS

Browser security production’da kerak

CORS

* bilan ochib tashlamang

CSRF

Auth cookie’da bo‘lsa jiddiy ko‘ring

Audit log

Sensitive actionlar iz qoldirsin