API Security Best Practices in System Design
API security is the practice of protecting application programming interfaces from misuse, data theft and attacks. Modern apps are built on APIs: mobile apps, single-page web apps, partner integrations and microservices all talk through them. That makes APIs one of the most attractive targets for attackers, because a single weak endpoint can expose an entire database.
This tutorial explains the most common API threats and walks through twelve practical best practices: encryption, authentication, object-level authorization, input validation, rate limiting, minimal data exposure, safe error handling, secret management, security headers and CORS, API gateways, monitoring and API inventory. Each comes with clear examples, including Java and Spring Boot code, and the tutorial ends with a complete checklist.
1. What Is API Security?
An API is a set of endpoints that lets one program ask another to read or change data, such as GET /api/orders/1001 or POST /api/transfers. API security makes sure that:
- only known callers can use the API (authentication),
- each caller can only do what they are allowed to, on data they are allowed to touch (authorization),
- data is protected in transit and at rest (encryption),
- the API cannot be abused through malicious input or excessive traffic (validation and rate limiting),
- misuse is detected and investigated quickly (logging and monitoring).
APIs are different from traditional web pages in one important way: there is no user interface hiding things. Attackers call endpoints directly with tools like curl or scripts, change IDs and parameters freely, and read every field in a response. Every rule must therefore be enforced on the server.
2. Common API Threats
The OWASP API Security Top 10 is a widely used list of the most critical API risks. In plain language, the main threats are:
| Threat | What goes wrong | Simple example |
|---|---|---|
| Broken object-level authorization (BOLA) | The API does not check that the requested record belongs to the caller | Changing /orders/1001 to /orders/1002 shows someone else's order |
| Broken authentication | Weak login, token or session handling lets attackers impersonate users | Unlimited password guesses, tokens that never expire |
| Broken property-level authorization | The API returns or accepts fields the caller should not see or change | A response includes passwordHash, or a user sets "role": "ADMIN" in a profile update |
| Unrestricted resource consumption | No limits on request rate, size or cost | A script requests 10,000 pages per second, or uploads a 5 GB file |
| Broken function-level authorization | Admin or privileged endpoints are reachable by ordinary users | A normal user calls DELETE /api/admin/users/42 |
| Abuse of sensitive business flows | Legitimate features are automated at scale | Bots buying all concert tickets in seconds |
| Server-side request forgery (SSRF) | The API fetches a URL supplied by the user without checks | A "fetch image from URL" feature is used to reach internal servers |
| Security misconfiguration | Unsafe defaults, verbose errors, open CORS, missing patches | Stack traces returned to clients, debug endpoints left enabled |
| Improper inventory management | Forgotten old or test APIs stay online without protection | /v1/ endpoints still live after /v2/ fixed a flaw |
| Unsafe consumption of third-party APIs | Data from partner APIs is trusted blindly | A partner response containing malicious data is stored and executed |
Injection attacks such as SQL injection, and attacks on data in transit, remain important too. The best practices below address all of these.
3. Defence in Depth: Layers of API Security
No single control is enough. Strong API security stacks several layers, so if one fails, the next still protects you:
4. Best Practice 1: Encrypt Everything with HTTPS
Every API call must use HTTPS (TLS 1.2 or 1.3). Without it, tokens, passwords and personal data travel as readable text that anyone on the network path can capture or modify.
- Reject or redirect plain HTTP requests, and send the
Strict-Transport-Securityheader. - Never put secrets such as API keys, tokens or passwords in URLs; URLs end up in logs, browser history and proxy caches. Use headers instead.
- Encrypt internal service-to-service traffic too, ideally with mutual TLS (mTLS), where both sides present certificates.
- Encrypt sensitive data at rest in databases, backups and logs.
See HTTP vs HTTPS for how TLS works.
5. Best Practice 2: Authenticate Every Request
Authentication answers "who is calling?". Choose a method that fits the caller:
| Method | How it works | Good for | Watch out for |
|---|---|---|---|
| API keys | A long random string sent in a header | Identifying partner applications, simple server-to-server use | Identifies an app, not a user; easy to leak; rotate regularly |
| OAuth 2.0 access tokens | An authorization server issues short-lived tokens after login or client authentication | User-facing apps, third-party access, microservices | Use proven flows (Authorization Code with PKCE for apps); avoid deprecated flows |
| JWT (JSON Web Token) | A signed token containing claims such as user ID, scopes and expiry | Stateless validation at gateways and services | Verify signature, issuer, audience and expiry; keep lifetimes short |
| Mutual TLS | Both client and server present certificates | Service-to-service and high-security partner integrations | Certificate issuing and rotation must be automated |
| Session cookies | Server-side session identified by a cookie | Traditional browser-based web apps on the same domain | Require CSRF protection; use Secure, HttpOnly, SameSite |
A typical modern setup uses OAuth 2.0 with JWT access tokens:
- Keep access tokens short-lived (minutes, not days) and use refresh tokens to get new ones.
- Always validate the token's signature, issuer, audience and expiry. Reject tokens with the
nonealgorithm or unexpected algorithms. - Protect login endpoints with rate limits, account lockout or progressive delays, and multi-factor authentication for sensitive accounts.
- Never build your own cryptography or token format; use well-tested libraries and identity providers.
Learn more in the OAuth2 and JWT tutorials.
6. Best Practice 3: Authorize Every Object and Action
Authentication tells you who the caller is; authorization decides what they may do. Broken object-level authorization is consistently ranked the number one API risk because it is so easy to get wrong: the developer checks that the user is logged in, but not that the requested record belongs to them.
package com.javabytechie.apisecurity;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
@RestController
public class OrderController {
private final OrderRepository orders;
public OrderController(OrderRepository orders) {
this.orders = orders;
}
// VULNERABLE: any logged-in user can read any order just by changing the id
// @GetMapping("/api/orders/{id}")
// public Order getOrder(@PathVariable long id) {
// return orders.findById(id).orElseThrow();
// }
// SECURE: the order is looked up together with the caller's identity from the verified token
@GetMapping("/api/orders/{id}")
public OrderResponse getOrder(@PathVariable long id, @AuthenticationPrincipal Jwt jwt) {
String customerId = jwt.getSubject();
return orders.findByIdAndCustomerId(id, customerId)
.map(OrderResponse::from)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
}
- Take the caller's identity from the verified token, never from a request parameter like
?userId=7. - Check ownership or permission on every endpoint that accepts an ID, including updates and deletes.
- Return
404rather than403for records the caller should not know exist. - Use random, non-guessable IDs (such as UUIDs) for public identifiers as an extra layer, but never as a replacement for authorization checks.
- Protect admin functions with role or scope checks and keep them on separate paths, ideally a separate internal API. See Role Based Access Control.
- Deny by default: an endpoint without an explicit rule should be inaccessible.
7. Best Practice 4: Validate All Input
Treat every value coming into your API as untrusted: path variables, query parameters, headers, JSON bodies and uploaded files. Validation stops injection attacks, crashes and corrupted data.
package com.javabytechie.apisecurity;
import java.math.BigDecimal;
import jakarta.validation.Valid;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TransferController {
public record CreateTransferRequest(
@NotBlank @Pattern(regexp = "^[A-Z0-9]{10,18}$") String toAccount,
@NotNull @DecimalMin("1.00") @DecimalMax("200000.00") BigDecimal amount,
@Size(max = 140) String note) { }
public record TransferResponse(String reference, String status) { }
@PostMapping("/api/transfers")
@ResponseStatus(HttpStatus.CREATED)
public TransferResponse create(@Valid @RequestBody CreateTransferRequest request) {
// Only reached when every constraint passes; otherwise Spring returns 400 Bad Request
return new TransferResponse("TRF-20261019-0001", "PENDING");
}
}
- Allow-list what is valid (types, lengths, ranges, formats, enum values) rather than trying to block bad patterns.
- Use parameterised queries or an ORM for all database access; never build SQL by concatenating strings.
- Reject unknown JSON fields for sensitive operations, so attackers cannot slip in extra properties such as
roleorbalance(mass assignment). - Limit request body size, array lengths, nesting depth and file upload size and type.
- When the API fetches URLs supplied by users, allow only approved domains and block internal IP ranges to prevent SSRF.
- Encode output correctly for its destination (HTML, SQL, shell) if data is ever rendered or executed.
8. Best Practice 5: Rate Limiting and Throttling
Without limits, a single client, whether a buggy script, a scraper or an attacker, can exhaust your servers, run up your cloud bill or brute-force passwords. Rate limiting caps how many requests each client may make in a time window and answers excess requests with 429 Too Many Requests.
The token bucket algorithm is a popular choice because it allows short bursts while enforcing a steady average rate:
package com.javabytechie.apisecurity;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** One token bucket per client: each request takes a token, tokens refill at a steady rate. */
public class TokenBucketRateLimiter {
private static final class Bucket {
double tokens;
long lastRefillNanos;
Bucket(double tokens, long now) {
this.tokens = tokens;
this.lastRefillNanos = now;
}
}
private final int capacity;
private final double tokensPerSecond;
private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
public TokenBucketRateLimiter(int capacity, double tokensPerSecond) {
this.capacity = capacity;
this.tokensPerSecond = tokensPerSecond;
}
public boolean allow(String clientId) {
long now = System.nanoTime();
Bucket bucket = buckets.computeIfAbsent(clientId, id -> new Bucket(capacity, now));
synchronized (bucket) {
double elapsedSeconds = (now - bucket.lastRefillNanos) / 1_000_000_000.0;
bucket.tokens = Math.min(capacity, bucket.tokens + elapsedSeconds * tokensPerSecond);
bucket.lastRefillNanos = now;
if (bucket.tokens >= 1) {
bucket.tokens -= 1;
return true;
}
return false;
}
}
public static void main(String[] args) throws InterruptedException {
TokenBucketRateLimiter limiter = new TokenBucketRateLimiter(5, 1.0); // burst of 5, then 1 request/second
for (int i = 1; i <= 7; i++) {
System.out.println("Request " + i + " from api-key-42: " + (limiter.allow("api-key-42") ? "200 OK" : "429 Too Many Requests"));
}
System.out.println("Request 1 from api-key-77: " + (limiter.allow("api-key-77") ? "200 OK" : "429 Too Many Requests"));
Thread.sleep(1_100); // wait a little over a second: one token is refilled
System.out.println("After 1.1 s, api-key-42: " + (limiter.allow("api-key-42") ? "200 OK" : "429 Too Many Requests"));
}
}
Request 1 from api-key-42: 200 OK
Request 2 from api-key-42: 200 OK
Request 3 from api-key-42: 200 OK
Request 4 from api-key-42: 200 OK
Request 5 from api-key-42: 200 OK
Request 6 from api-key-42: 429 Too Many Requests
Request 7 from api-key-42: 429 Too Many Requests
Request 1 from api-key-77: 200 OK
After 1.1 s, api-key-42: 200 OK
Each client has its own bucket, so one noisy client does not affect others. In production with several API instances, keep the buckets in a shared store such as Redis, or enforce limits at the API gateway, so the limit applies across all servers. Also:
- Set stricter limits on expensive or sensitive endpoints, such as login, password reset, search and exports.
- Return a
Retry-Afterheader with 429 responses. - Limit by API key, user and IP address, and add timeouts and maximum page sizes so a single request cannot be too expensive.
Read more in the Rate Limiting tutorial.
9. Best Practice 6: Return Only the Data You Need
A common mistake is returning whole database entities and expecting the front end to hide sensitive fields. Attackers read raw API responses, so anything returned is exposed.
- Use dedicated response DTOs (data transfer objects) that contain only the fields the client needs.
- Never return secrets such as password hashes, internal flags, other users' data or full card numbers. Mask values like
**** **** **** 4821. - Use separate request DTOs that contain only the fields a caller may change, which prevents mass assignment.
- Paginate large collections and cap the page size.
- For GraphQL APIs, limit query depth and complexity, and disable introspection in production if it is not needed.
10. Best Practice 7: Handle Errors Without Leaking Details
Stack traces, SQL errors and framework versions in error responses give attackers a map of your system. Return consistent, minimal errors to clients and keep the details in your logs:
package com.javabytechie.apisecurity;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.server.ResponseStatusException;
@RestControllerAdvice
public class ApiExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(ApiExceptionHandler.class);
@ExceptionHandler(MethodArgumentNotValidException.class)
public ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
ProblemDetail problem = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Request validation failed");
problem.setProperty("errors", ex.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.toList());
return problem;
}
@ExceptionHandler(ResponseStatusException.class)
public ProblemDetail handleStatus(ResponseStatusException ex) {
return ProblemDetail.forStatus(ex.getStatusCode());
}
@ExceptionHandler(AccessDeniedException.class)
public ProblemDetail handleDenied(AccessDeniedException ex) {
return ProblemDetail.forStatusAndDetail(HttpStatus.FORBIDDEN, "Access denied");
}
@ExceptionHandler(Exception.class)
public ProblemDetail handleUnexpected(Exception ex) {
String errorId = UUID.randomUUID().toString();
log.error("Unexpected error, reference {}", errorId, ex); // full details stay in the server log
return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR,
"Something went wrong. Quote reference " + errorId + " when contacting support.");
}
}
{
"type": "about:blank",
"title": "Internal Server Error",
"status": 500,
"detail": "Something went wrong. Quote reference 9b3f1e2a-6c41-4d8e-a0f7-52e1c9d4b8aa when contacting support.",
"instance": "/api/transfers"
}
Use correct status codes (400, 401, 403, 404, 429, 500), and give login failures the same generic message whether the username or the password was wrong, so attackers cannot discover valid accounts.
11. Best Practice 8: Protect Secrets and Keys
- Never hard-code API keys, database passwords or signing keys in source code, and never commit them to Git.
- Store secrets in a secrets manager (such as HashiCorp Vault or a cloud secrets service) or inject them as environment variables at runtime.
- Rotate keys and credentials regularly, and immediately after any suspected leak.
- Give each client its own key so a leaked key can be revoked without affecting others.
- Store only hashes of API keys on the server, just like passwords.
- Scan repositories and build pipelines for accidentally committed secrets.
12. Best Practice 9: Security Headers and CORS
Browsers enforce useful protections, but only if your API tells them to:
Strict-Transport-Security: always use HTTPS for this domain.X-Content-Type-Options: nosniff: stop browsers guessing content types.Cache-Control: no-store: for responses containing personal or sensitive data.Content-Security-Policyandframe-ancestors: mainly for any HTML the API serves, such as documentation pages.
CORS (Cross-Origin Resource Sharing) controls which websites may call your API from a browser. Allow only the exact origins you trust, for example https://app.example.com. Never combine Access-Control-Allow-Origin: * with credentials, and do not reflect whatever origin the request sends. Remember that CORS only restricts browsers; it does not replace authentication, because scripts and servers ignore it.
13. Best Practice 10: Use an API Gateway
An API gateway is a single entry point in front of your services. It is the natural place to enforce common protections consistently, instead of re-implementing them in every service:
- TLS termination and HTTPS redirects.
- Token validation and API key checks.
- Rate limiting, quotas and request size limits.
- IP allow and block lists, and integration with a Web Application Firewall.
- Request logging, metrics and tracing headers.
- Hiding internal services so only the gateway is reachable from the internet.
Services behind the gateway should still check authorization for their own data. The gateway answers "is this a valid caller?"; the service answers "may this caller touch this record?". See the API Gateway tutorial.
14. Best Practice 11: Log, Monitor and Audit
- Log security events: logins, failed authentication, permission denials, rate-limit hits, token errors and administrative actions.
- Include who, what, when and from where, plus a correlation ID that follows the request across services.
- Never log passwords, tokens, full card numbers or other secrets; mask sensitive values.
- Alert on unusual patterns, such as a spike in
401/403responses, one client walking through sequential IDs, or traffic from unexpected countries. - Keep tamper-resistant audit logs for sensitive operations, and retain them as regulations require.
15. Best Practice 12: Keep an API Inventory and Lifecycle
- Maintain an up-to-date list of every API, version, environment and owner. Forgotten "shadow" or "zombie" APIs are a common entry point.
- Describe APIs with OpenAPI specifications, and use them to generate documentation, tests and gateway configuration.
- Retire old versions on a published schedule instead of leaving them running indefinitely.
- Keep test and staging APIs off the public internet, and never let them use real customer data without equal protection.
- Validate and sanitise data received from third-party APIs just as carefully as user input, and set timeouts on outbound calls.
- Include security testing in the delivery pipeline: dependency scanning, static analysis, and automated tests that try to access other users' data.
16. Putting It Together: A Secured Spring Boot API
Spring Boot with Spring Security's OAuth2 resource server support validates JWT access tokens with very little code. Add the spring-boot-starter-oauth2-resource-server dependency and point it at your identity provider:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.example.com/realms/shop # keys are discovered and cached automatically
server:
error:
include-stacktrace: never
package com.javabytechie.apisecurity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableMethodSecurity
public class ApiSecurityConfig {
@Bean
SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
// Stateless API with bearer tokens: no server session, no cookies, so CSRF tokens are not needed
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers(HttpMethod.GET, "/api/products/**").hasAuthority("SCOPE_products:read")
.requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
.anyRequest().authenticated()) // deny anonymous access by default
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
With this configuration, every request needs a valid, unexpired token signed by your identity provider; scopes inside the token decide access to product and admin endpoints; and the controller-level ownership check shown earlier protects individual records. Add the validation, error handling and rate limiting from the previous sections, and the API covers the most important risks.
17. API Security Checklist
| Area | Checklist item |
|---|---|
| Transport | HTTPS only, TLS 1.2+, HSTS, mTLS between services where possible |
| Authentication | OAuth 2.0 / OIDC or scoped API keys; short-lived tokens; full JWT validation; protected login |
| Authorization | Ownership check on every object; role/scope check on every function; deny by default |
| Input | Allow-list validation, size limits, parameterised queries, no mass assignment, SSRF protection |
| Output | Response DTOs, masked sensitive fields, pagination, no internal details |
| Abuse protection | Rate limits per client, quotas, timeouts, bot protection for sensitive flows |
| Errors | Generic messages, correct status codes, no stack traces, reference IDs |
| Secrets | Secrets manager, rotation, no secrets in code, URLs or logs |
| Browser | Strict CORS allow list, security headers, no-store for sensitive data |
| Operations | API gateway, WAF, security logging, alerting, audit trails |
| Lifecycle | API inventory, OpenAPI specs, version retirement, security testing in CI/CD, dependency updates |
18. Summary
- API security protects the endpoints that modern applications depend on, using several layers from the edge to the database.
- The biggest risks are broken authorization (especially object-level), weak authentication, excessive data exposure, missing rate limits and misconfiguration.
- Use HTTPS everywhere, OAuth 2.0 and JWT or scoped keys for authentication, and check ownership and permissions on every request.
- Validate input, limit request rates, return only needed data and keep errors generic.
- Protect secrets, configure CORS and headers carefully, use an API gateway, monitor everything and keep an inventory of every API you run.
Security is not a one-time feature but a habit built into every endpoint, review and deployment. Open the FAQ tab for answers to common questions about API security.
Frequently Asked Questions (FAQ)
Q1. What is API security?
API security is the set of practices and tools that protect APIs from unauthorised access, data leaks and abuse. It covers encryption, authentication, authorization, input validation, rate limiting, safe error handling, monitoring and managing the full API lifecycle.
Q2. Why is API security important?
APIs give direct access to an application's data and business logic, and attackers can call them with simple tools. A single unprotected endpoint can expose millions of records, allow fraud or take a service offline, so APIs are a prime target and must be secured carefully.
Q3. What is the OWASP API Security Top 10?
It is a list published by the OWASP Foundation of the most critical security risks specific to APIs, such as broken object-level authorization, broken authentication, unrestricted resource consumption and security misconfiguration. Teams use it as a checklist when designing and testing APIs.
Q4. What is broken object level authorization (BOLA)?
BOLA happens when an API returns or changes a record based only on an ID from the request, without checking that the caller is allowed to access that specific record. An attacker simply changes the ID to read or modify other users' data. The fix is an ownership or permission check on every object access.
Q5. What is the difference between authentication and authorization in APIs?
Authentication verifies who the caller is, for example by validating a token or API key. Authorization decides what that caller may do, such as which endpoints and which records they can access. Both are required on every request.
Q6. Are API keys secure enough?
API keys identify an application, not a user, and can leak easily if embedded in apps or URLs. They are acceptable for server-to-server use and partner identification when sent over HTTPS, scoped, rate-limited and rotated. For user data and sensitive operations, OAuth 2.0 tokens are the better choice.
Q7. What is the difference between OAuth 2.0 and JWT?
OAuth 2.0 is an authorization framework that defines how clients obtain access tokens from an authorization server. JWT is a token format: a signed JSON object containing claims. OAuth 2.0 access tokens are often JWTs, but the two are separate concepts.
Q8. How should JWTs be validated in an API?
Verify the signature with the issuer's trusted public key, check that the algorithm is the expected one, and validate the issuer, audience, expiry and not-before claims. Then check that the token's scopes or roles permit the requested action. Libraries such as Spring Security's resource server do this for you.
Q9. What is rate limiting and why does an API need it?
Rate limiting restricts how many requests a client can make in a given period, rejecting the excess with 429 Too Many Requests. It protects against brute-force attacks, scraping, denial of service and runaway costs, and keeps the service fair for all clients.
Q10. How does the token bucket algorithm work?
Each client has a bucket that holds a maximum number of tokens and refills at a fixed rate. Every request removes one token; if the bucket is empty, the request is rejected. This allows short bursts up to the bucket size while enforcing a steady average rate.
Q11. Should I disable CSRF protection for REST APIs?
Only for stateless APIs that authenticate with bearer tokens in the Authorization header and do not use cookies. If your API relies on session cookies, browsers send them automatically, so CSRF protection must stay enabled.
Q12. What is CORS and does it secure my API?
CORS is a browser mechanism that controls which websites may call your API from JavaScript. Configure it with an exact allow list of trusted origins. It does not secure the API by itself, because non-browser clients ignore it; authentication and authorization are still required.
Q13. How do I prevent SQL injection in APIs?
Use parameterised queries, prepared statements or an ORM for all database access, never concatenating user input into SQL. Add input validation with strict types and formats, and give the database account only the permissions it needs.
Q14. What is mass assignment?
Mass assignment happens when an API automatically copies all fields from a request into an object, letting attackers set fields they should not control, such as role, isVerified or balance. Prevent it with dedicated request DTOs containing only editable fields.
Q15. How should APIs handle errors securely?
Return consistent, generic error messages with correct status codes, never stack traces, SQL errors or internal details. Log the full error on the server with a reference ID and include only that reference in the response so support teams can trace it.
Q16. What role does an API gateway play in security?
An API gateway is a single entry point that enforces shared protections: TLS, token validation, API keys, rate limiting, request size limits, IP filtering and logging. It also hides internal services. Services behind it must still perform their own data-level authorization.
Q17. What is mutual TLS (mTLS)?
Mutual TLS is a TLS connection where both the client and the server present certificates and verify each other. It is commonly used for service-to-service communication and high-security partner integrations, and is often provided automatically by a service mesh.
Q18. How do I secure secrets like API keys and database passwords?
Keep them out of source code and Git, store them in a secrets manager or inject them as environment variables, give each client its own key, rotate them regularly, store only hashes of API keys on the server and scan code repositories for leaked secrets.
Q19. How do I secure a Spring Boot REST API?
Add Spring Security with the OAuth2 resource server starter, configure the token issuer, make the API stateless, require authentication by default, restrict endpoints by scope or role, check record ownership in controllers or with method security, validate input with Jakarta Validation, return safe errors and add rate limiting.
Q20. How often should API security be tested?
Continuously. Run automated dependency scans and security tests in every build, include authorization tests that try to access other users' data, review new endpoints during code review, and run periodic penetration tests, especially before major releases or after significant changes.