DEV Community

Cover image for How Do Backend Developers Secure APIs?
Tanu Priya
Tanu Priya

Posted on

How Do Backend Developers Secure APIs?

Building an API is only half the job.

Once an API is exposed to real users, you have to think about what happens when someone sends unexpected input, calls an endpoint they shouldn't, sends thousands of requests, or gets access to a secret.

That's why API security isn't a single feature. It's a collection of layers that protect different parts of the application.

A typical request might go through:

Request
   ↓
Authentication
   ↓
Authorization
   ↓
Validation
   ↓
Rate Limiting
   ↓
Business Logic
   ↓
Database
   ↓
Response
Enter fullscreen mode Exit fullscreen mode

Let's look at the important parts.

Authentication

The first question is:

Who is making this request?

For example:

GET /api/profile
Authorization: Bearer <token>
Enter fullscreen mode Exit fullscreen mode

The backend verifies the credentials and identifies the user.

If authentication fails:

Invalid / missing credentials
        ↓
401 Unauthorized
Enter fullscreen mode Exit fullscreen mode

Authentication establishes identity, but it doesn't decide what that user can access.

That's authorization's job.

Authorization

Suppose two users are logged in:

Alex → user
Sarah → admin
Enter fullscreen mode Exit fullscreen mode

Alex sends:

DELETE /api/users/100
Enter fullscreen mode Exit fullscreen mode

The backend shouldn't simply check whether Alex is logged in.

It should also check whether Alex has permission to delete that user.

Authenticated user
       ↓
Permission check
       ↓
Allowed?
   ├── No → 403 Forbidden
   └── Yes → Continue
Enter fullscreen mode Exit fullscreen mode

This check must happen on the backend.

Hiding a button in the frontend is not authorization.

A user can always send the API request directly.

Validate Client Input

Never assume that data from the frontend is trustworthy.

A client might send:

{
  "name": 123,
  "age": -50,
  "role": "admin"
}
Enter fullscreen mode Exit fullscreen mode

even if your frontend never allows those values.

The backend should validate:

Required fields
Data types
Formats
Lengths
Allowed values
Business rules
Enter fullscreen mode Exit fullscreen mode

A useful rule is:

Frontend validation improves the user experience. Backend validation protects the application.

SQL Injection

One classic API vulnerability is SQL injection.

This is dangerous:

const query =
  `SELECT * FROM users WHERE email = '${email}'`;
Enter fullscreen mode Exit fullscreen mode

User input is being inserted directly into the SQL statement.

Instead, use parameterized queries:

const result = await db.query(
  "SELECT * FROM users WHERE email = $1",
  [email]
);
Enter fullscreen mode Exit fullscreen mode

The database can then treat the supplied value as data rather than SQL syntax.

The same principle applies when using ORMs: use their parameterized query mechanisms rather than constructing raw SQL from untrusted input.

XSS

XSS stands for Cross-Site Scripting.

Consider a comment system:

User input
   ↓
API
   ↓
Database
   ↓
Browser
Enter fullscreen mode Exit fullscreen mode

If the application later renders that content as executable HTML or JavaScript, an attacker could potentially run code in another user's browser.

Common defenses include:

Safe rendering
Output encoding
Avoiding unsafe HTML injection
Content Security Policy
Enter fullscreen mode Exit fullscreen mode

The important lesson is that data stored in your database isn't automatically safe.

It may have originally come from an untrusted user.

CSRF

CSRF stands for Cross-Site Request Forgery.

It is particularly relevant when authentication uses cookies because browsers can automatically send cookies with requests.

A simplified attack looks like:

User is logged in
      ↓
Authentication cookie
      ↓
Malicious website
      ↓
Forged request
      ↓
Your API
Enter fullscreen mode Exit fullscreen mode

Depending on the application, protections can include:

SameSite cookies
CSRF tokens
Origin validation
Enter fullscreen mode Exit fullscreen mode

The right defense depends on how authentication and browser requests are designed.

Rate Limiting

An attacker can automate requests to endpoints such as:

Login
OTP verification
Password reset
Search
Expensive API operations
Enter fullscreen mode Exit fullscreen mode

Without limits:

Attacker
   ↓
Thousands of requests
   ↓
Backend
   ↓
Database
Enter fullscreen mode Exit fullscreen mode

Rate limiting can stop excessive traffic:

Too many requests
       ↓
429 Too Many Requests
Enter fullscreen mode Exit fullscreen mode

Rate limiting isn't only about preventing brute-force attacks. It can also protect backend resources from accidental or malicious traffic spikes.

Protect Your Secrets

Backend applications often use sensitive credentials:

Database passwords
JWT signing keys
API keys
Cloud credentials
Payment provider secrets
OAuth secrets
Enter fullscreen mode Exit fullscreen mode

Don't put them directly into source code:

const secret = "my-secret-key";
Enter fullscreen mode Exit fullscreen mode

Use environment configuration or a dedicated secret-management system instead:

const secret = process.env.JWT_SECRET;
Enter fullscreen mode Exit fullscreen mode

Also remember that secrets can leak through more than source code.

Watch out for:

Git repositories
Logs
Error messages
Frontend bundles
Debug endpoints
Screenshots
Enter fullscreen mode Exit fullscreen mode

A secret is only useful if it stays secret.

HTTPS and Encryption

API traffic should use HTTPS.

Without HTTPS:

Client
   ↓
Network
   ↓
Server
Enter fullscreen mode Exit fullscreen mode

With HTTPS:

Client
   ↓
Encrypted TLS connection
   ↓
Server
Enter fullscreen mode Exit fullscreen mode

HTTPS protects data while it travels between the client and server.

This is especially important for:

Passwords
Cookies
Tokens
Personal data
Payment information
Enter fullscreen mode Exit fullscreen mode

Encryption protects the connection, but it doesn't decide whether the authenticated user has permission to access a resource.

Security Headers

HTTP security headers can provide additional browser protections.

Common examples include:

Content-Security-Policy
Strict-Transport-Security
X-Content-Type-Options
Referrer-Policy
Enter fullscreen mode Exit fullscreen mode

For example, Content Security Policy can restrict where a browser is allowed to load scripts and other resources.

These headers don't replace secure application code, but they add another layer of defense.

Don't Return More Data Than Necessary

Suppose your database contains:

id
name
email
password_hash
internal_notes
created_at
Enter fullscreen mode Exit fullscreen mode

Your API probably doesn't need to return all of it.

Instead of:

SELECT * FROM users;
Enter fullscreen mode Exit fullscreen mode

select only what the client needs:

SELECT id, name, email
FROM users;
Enter fullscreen mode Exit fullscreen mode

The same applies to error messages.

Avoid exposing:

Database credentials
SQL queries
Internal file paths
Stack traces
Private service information
Enter fullscreen mode Exit fullscreen mode

The client usually needs a simple error, while detailed information can remain in protected server logs.

Don't Trust IDs From the Client

Consider:

GET /api/orders/123
Enter fullscreen mode Exit fullscreen mode

The user is authenticated.

That's not enough.

The backend should check whether that particular order belongs to that user.

Otherwise, someone might change:

/api/orders/123
Enter fullscreen mode Exit fullscreen mode

to:

/api/orders/124
Enter fullscreen mode Exit fullscreen mode

and access another user's data.

The check should be:

Authenticated user
       ↓
Requested resource
       ↓
Does the user have access?
       ↓
Allow / Reject
Enter fullscreen mode Exit fullscreen mode

This is a common authorization problem in APIs.

Logging and Monitoring

Security also means being able to notice suspicious activity.

Useful events to track include:

Failed logins
Permission failures
Password reset attempts
Rate-limit violations
Administrative actions
Unusual request patterns
Enter fullscreen mode Exit fullscreen mode

For example:

User 42
DELETE /api/users/100
Authorization denied
Enter fullscreen mode Exit fullscreen mode

But don't casually log sensitive credentials.

Avoid storing things such as:

Passwords
Access tokens
Refresh tokens
API keys
Enter fullscreen mode Exit fullscreen mode

Logs can become a security risk themselves if they contain secrets.

Defense in Depth

At this point, the bigger picture becomes clear.

API security isn't about finding one perfect security mechanism.

It's about having several layers:

                    Request
                       ↓
                 Rate Limiting
                       ↓
                Authentication
                       ↓
                 Authorization
                       ↓
                  Validation
                       ↓
                 Business Logic
                       ↓
                    Database
                       ↓
                   Response
Enter fullscreen mode Exit fullscreen mode

And around the application:

HTTPS
Security Headers
Secret Management
Logging
Monitoring
Enter fullscreen mode Exit fullscreen mode

Each layer deals with a different type of problem.

For example:

SQL Injection
→ Parameterized queries

XSS
→ Safe rendering + output handling

CSRF
→ Appropriate CSRF protections

Brute Force
→ Rate limiting

Broken Authorization
→ Server-side permission checks

Exposed Secrets
→ Secret management
Enter fullscreen mode Exit fullscreen mode

If one layer fails, another layer can still limit the damage.

That's the basic idea behind defense in depth.

A Practical Checklist

Before putting an API into production, ask:

Authentication
[ ] Are protected endpoints authenticated?

Authorization
[ ] Are permissions checked on the server?
[ ] Can users access other users' resources?

Validation
[ ] Are request bodies validated?
[ ] Are query parameters validated?

Database
[ ] Are queries parameterized?

Rate Limiting
[ ] Are login and expensive endpoints protected?

Secrets
[ ] Are credentials outside the source code?
[ ] Are secrets excluded from logs?

Transport
[ ] Is HTTPS enabled?

Browser Security
[ ] Are cookies configured appropriately?
[ ] Are CSRF protections considered?

Monitoring
[ ] Are suspicious activities detectable?
Enter fullscreen mode Exit fullscreen mode

This isn't a complete security audit, but it's a good starting point.

The Mental Model

When securing an API, think about the request one question at a time:

Who is making the request?
        ↓
Authentication

What can they access?
        ↓
Authorization

Is the input valid?
        ↓
Validation

Are they sending too many requests?
        ↓
Rate Limiting

Can the connection be intercepted?
        ↓
HTTPS

Can browser behavior be abused?
        ↓
CSRF / Security Headers

Are credentials protected?
        ↓
Secret Management

Can suspicious behavior be detected?
        ↓
Logging / Monitoring
Enter fullscreen mode Exit fullscreen mode

The main lesson is simple:

API security isn't a feature you add once. It's a collection of decisions made throughout the backend.

Authentication tells you who the user is.

Authorization controls what they can access.

Validation keeps unexpected input out of your application logic.

Rate limiting controls abusive traffic.

HTTPS protects data in transit.

And careful secret management prevents credentials from becoming an easy attack path.

The most useful question isn't:

"Is my API secure?"

It's:

"What could go wrong at each stage of this request, and what prevents it?"

That's the mindset that helps turn a working API into a secure one.

Top comments (0)