Common Backend Vulnerabilities
Authentication & Authorization
Spring Security JWT authentication
In the previous section, we discussed developing a security mindset—learning to think like an attacker. But how do we apply this systematically? How do we ensure we're not missing critical threats? This is where threat modeling comes in. Threat modeling is a structured approach to identifying security threats, understanding their potential impact, and deciding how to address them. Think of it as a security brainstorming session with a framework to guide your thinking.
Before jumping into Threat Modeling, watch the following video about the most fundamental concept in cybersecurity — CIA.
https://www.youtube.com/watch?v=EqNe55IzjAw
At its core, threat modeling answers four key questions:
<aside> ⚠️
You don't need to be a security expert to do threat modeling. If you can describe how your system works and ask "what if?" questions, you can threat model.
</aside>
Without Threat Modeling Developers often discover security issues through:
With Threat Modeling, you proactively identify threats before writing code or during design:
<aside> 💡
Key insight: Threat modeling shifts security from reactive "fix this vulnerability" to proactive "prevent this class of vulnerabilities".
</aside>
https://www.youtube.com/watch?v=J3V4x5QtFus
STRIDE is a mnemonic developed by Microsoft to help categorize threats. Each letter represents a different type of threat:
| Threat | Description | Security Property Violated |
|---|---|---|
| Spoofing | Pretending to be someone or something else | Authentication |
| Tampering | Modifying data or code without authorization | Integrity |
| Repudiation | Denying having performed an action | Non-repudiation |
| Information Disclosure | Exposing data to unauthorized parties | Confidentiality |
| Denial of Service | Making a system unavailable | Availability |
| Elevation of Privilege | Gaining permissions you shouldn't have | Authorization |
Let's see how STRIDE applies to a simple login feature:

| Threat | Example | Possible Mitigation |
|---|---|---|
| Spoofing | Attacker uses stolen credentials to impersonate a user | Multi-factor authentication, account lockout |
| Tampering | Attacker modifies login request in transit | Use HTTPS, validate input server-side |
| Repudiation | User denies they logged in and performed actions | Audit logging with timestamps and IP addresses |
| Information Disclosure | Error messages reveal if username exists | Generic error messages ("Invalid credentials") |
| Denial of Service | Attacker floods login endpoint with requests | Rate limiting, CAPTCHA after failed attempts |
| Elevation of Privilege | Attacker manipulates response to gain admin role | Server-side role assignment, signed tokens |
Here's a practical 5-step process you can apply to any feature or system:
Create a simple diagram showing:

What are you protecting? List the valuable things in your system:
| Asset | Why It's Valuable |
|---|---|
| User credentials | Access to accounts |
| Personal data (PII) | Privacy, regulatory compliance |
| Payment information | Financial fraud risk |
| Session tokens | Account takeover |
| Business data | Competitive advantage |
| System availability | Revenue, reputation |
For each component and data flow, systematically ask STRIDE questions.
Not all threats are equal. Use a simple risk matrix to prioritize:

Factors affecting likelihood:
Factors affecting impact:
For each prioritized threat, decide on a response:
| --- | --- | --- |
Let's walk through a complete example. Consider this endpoint for updating user profiles:
@PutMapping("/api/users/{userId}/profile")
@PreAuthorize("isAuthenticated()")
public UserProfile updateProfile(
@PathVariable Long userId,
@RequestBody ProfileUpdateRequest request
) {
return userService.updateProfile(userId, request);
}

STRIDE Analysis
| --- | --- | --- | --- |
Resulting Secure Implementation
@PutMapping("/api/users/{userId}/profile")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<ProfileResponse> updateProfile(
@PathVariable Long userId,
@Valid @RequestBody ProfileUpdateRequest request,
@AuthenticationPrincipal UserDetails currentUser
) {
// TAMPERING: Verify user can only update their own profile
if (!currentUser.getId().equals(userId)) {
throw new AccessDeniedException("Cannot update another user's profile");
}
// TAMPERING: Use DTO to only accepts allowed fields (name, bio, avatar)
UserProfile updated = userService.updateProfile(userId, request);
// REPUDIATION: Audit log the change
auditService.log("PROFILE_UPDATE", userId, request);
// INFO DISCLOSURE: Return only safe fields via response DTO
return ResponseEntity.ok(ProfileResponse.from(updated));
}