Safeguard
Security

What Is a DTO (Data Transfer Object)? Security Notes for Java and Beyond

A DTO is a plain object that carries data across a boundary. Used well it is also one of your best defenses against mass assignment and data over-exposure.

Yukti Singhal
Security Analyst
5 min read

A DTO, or Data Transfer Object, is a simple object whose only job is to carry data across a boundary, such as between your API and its clients, and using DTOs instead of exposing your database entities directly is one of the cheapest and most effective API security patterns available. The DTO full form is the whole idea in three words: it transfers data, nothing more, and that narrowness is exactly what makes it a security tool.

Developers often meet DTOs as a "best practice" without being told why they matter beyond tidiness. The why is that they let you control precisely what enters and leaves your system.

What a DTO Is, and Is Not

A DTO has no business logic, no persistence behavior, and no clever methods. It is a bag of fields with getters, or in modern languages an immutable record. In DTO Java terms, before records you would write a class with private fields and accessors; since Java 16 a record does it in one line:

public record UserResponse(Long id, String displayName, String email) {}

Contrast that with a JPA entity, which is mapped to a database table and often carries a password hash, internal flags, audit columns, and lazy-loaded relationships:

@Entity
public class User {
    @Id private Long id;
    private String displayName;
    private String email;
    private String passwordHash;   // must never leave the server
    private boolean isAdmin;       // must never be client-settable
    private Instant deletedAt;
    // ... relationships, audit fields
}

The DTO is the deliberately smaller shape you expose. The entity is the full internal model. Keeping them separate is the point.

The Over-Exposure Problem DTOs Solve

If your controller returns the User entity directly, serialization will happily include every field, and now passwordHash and isAdmin are in the JSON response. This is not hypothetical; "excessive data exposure" was a named item in the OWASP API Security Top 10 precisely because returning entities verbatim is so common.

A response DTO fixes it structurally. You map the entity to a UserResponse that contains only the three fields a client should see, and there is no code path that can accidentally serialize the password hash, because the DTO does not have that field at all. Whitelisting by construction beats blacklisting with annotations, because a new sensitive field added to the entity later does not silently leak.

Request DTOs Stop Mass Assignment

The other direction is just as important. Mass assignment (sometimes called autobinding) is when a framework maps an incoming request body straight onto an object, and an attacker adds a field you did not expect. If you bind a request directly onto the User entity, an attacker sends:

{ "displayName": "Alice", "isAdmin": true }

and if isAdmin is bindable, they just promoted themselves. GitHub had a well-known mass assignment incident in 2012 rooted in exactly this pattern in Rails.

A request DTO that only declares the fields a client is allowed to set closes the hole:

public record UpdateProfileRequest(
    @NotBlank String displayName,
    @Email String email
) {}

There is no isAdmin field to bind, so the extra JSON key is simply ignored. The DTO doubles as the natural place to hang validation annotations, so shape and content checks live together at the boundary.

Mapping Between DTOs and Entities

The one honest cost of DTOs is the mapping code. You need something to convert entity to DTO and back. Options range from writing it by hand (explicit, verbose, no surprises) to using a compile-time mapper like MapStruct (fast, generated at build time) to reflection-based mappers (convenient, slower, and easier to misconfigure into copying fields you did not intend).

My advice: prefer explicit or compile-time mapping for anything security-relevant. A reflection mapper that copies "all matching fields" can quietly reintroduce the over-exposure problem the DTO was meant to prevent, because it will happily copy a field you forgot was sensitive. If you do use one, configure it to fail on unmapped fields rather than copy them silently.

DTOs Are Not a Complete Defense

Be clear-eyed about scope. A DTO controls the shape of data crossing a boundary. It does not authenticate the caller, it does not authorize the action, and it does not protect against injection deeper in your stack. A response DTO stops you leaking a password hash, but it does nothing about a broken access-control check that lets user A fetch user B's DTO. Layer DTOs with real authorization, and remember that the libraries doing your serialization and mapping are themselves dependencies; a vulnerable JSON library can undo careful DTO design, which is the kind of transitive risk an SCA tool is built to surface. The Safeguard Academy covers where these boundary defenses fit in the broader appsec picture.

FAQ

What does DTO stand for?

DTO stands for Data Transfer Object. It is a plain object that carries data between layers or across a network boundary, deliberately without business logic or persistence behavior.

Why use a DTO instead of returning the entity directly?

Returning an entity directly serializes every field, which is how password hashes, internal flags, and audit columns end up in API responses. A DTO exposes only the fields a client should see, so sensitive data cannot leak by accident, even when the entity gains new fields later.

How does a DTO prevent mass assignment?

A request DTO declares only the fields a client is permitted to set. When an attacker adds an unexpected field like isAdmin to the request body, there is no matching field on the DTO to bind to, so the value is ignored rather than applied to your entity.

Are DTOs only a Java thing?

No. The DTO pattern predates Java and appears in every stack: serializers in Django REST Framework, schemas in FastAPI or Zod, view models in .NET. The name and syntax change, but the security value, controlling exactly what crosses the boundary, is identical.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.