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: trueAuthorization
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_12Senior 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 tekshiriladiBu 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 / APIQaysi 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‘lsaKerak bo‘lmasligi mumkin:
- Oddiy bitta backend + bitta frontend bo‘lsa
- Keycloak/Auth0/Okta kabi tayyor provider ishlatilsa
- OAuth2 flow’larni to‘liq tushunmasangizSenior 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-serviceHar 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.uzBu 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 listYaxshi:
sub
tenant_id
scope
role
exp
issuer
audienceToken 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‘lsa7. 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: responseSpring config
spring:
security:
oauth2:
resourceserver:
opaque-token:
introspection-uri: https://auth.company.uz/oauth2/introspect
client-id: order-service
client-secret: secretSecurity 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‘radiShuning uchun:
- Short cache ishlatish mumkin
- Critical endpointlarda qo‘llash mumkin
- JWT bilan hybrid model qilish mumkin8. 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 → allowLekin 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 CEng 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 |
|
Subdomain |
|
Header |
|
Path |
|
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-999Agar 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 topilmadideb qaytarish ko‘pincha:
Access denieddan 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-passwordYoki undan ham yomon:
Git repository ichida production passwordSecret’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
- Certificatekabi 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 ishlatadiMisol:
spring:
cloud:
vault:
uri: http://localhost:8200
token: ${VAULT_TOKEN}
kv:
enabled: true
backend: secret
application-name: order-serviceKeyin 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‘rsin13. 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; includeSubDomainsSpring 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 |
|---|---|
| Browser MIME sniffing’ni cheklaydi |
| Clickjacking’dan himoya |
| Referrer ma’lumotini cheklaydi |
| Camera, microphone, geolocation kabi API’larni cheklaydi |
| 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 yubordiQachon 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 = 123456Yaxshi:
password_hash = BCrypt hashSpring’da:
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}Login:
if (!passwordEncoder.matches(rawPassword, user.getPasswordHash())) {
throw new BadCredentialsException("Invalid credentials");
}Password hashing uchun:
BCrypt
Argon2
PBKDF2kabi adaptive hash algoritmlar ishlatiladi.
17. Authorization model
Role-based access:
ADMIN
MANAGER
USERLekin katta system’da role yetmaydi.
Permission-based model yaxshiroq:
orders.read
orders.create
orders.cancel
payments.refund
users.inviteMisol:
@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 revokeLog namunasi:
event=PAYMENT_REFUND userId=123 tenantId=company-12 paymentId=987 amount=100000 result=SUCCESS traceId=abc123Lekin logga yozilmasin:
- Password
- Full token
- Card number
- Secret
- Private keyToken kerak bo‘lsa faqat qisqa fingerprint:
tokenHash=ab12cd3419. 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 protection2. 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 access4. Secret’larni Git’da saqlash
Bu production incident.
5. CORS *
Ayniqsa credentials bilan xavfli.
6. Token expiration juda uzun
Yomon:
Access token: 30 kunYaxshi:
Access token: 5-15 minut
Refresh token: rotation bilan20. 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_3API:
GET /api/orders/500Muammo:
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 |
|
CSRF | Auth cookie’da bo‘lsa jiddiy ko‘ring |
Audit log | Sensitive actionlar iz qoldirsin |