Security Best Practices in Coding
What Are Security Best Practices in Coding?
Security best practices in coding are techniques used to prevent security
vulnerabilities (like data leaks, injections, or breaches) before they happen — by
writing safe, defensive, and robust code.
Just like unit testing ensures correctness, secure coding ensures safety from hackers
and misuse.
Why Security Matters for Developers
1 small bug in your code can lead to huge damage — like leaking user passwords,
exposing payment info, or allowing full server access.
• Protects users’ data and privacy
• Prevents legal/compliance issues (e.g., GDPR, HIPAA)
• Avoids breaches that ruin company reputation
• Saves time vs fixing after attacks
Core Principles of Secure Coding
Principle Explanation
Never trust user input. Always check and sanitize
Validate Inputs
it.
Escape Outputs Prevent injection attacks by encoding output.
Use Authentication &
Verify who the user is and what they can do.
Authorization Properly
Use hashing (e.g., bcrypt, Argon2). Never store
Store Passwords Securely
plain text passwords.
Encrypt communication between client and
Use HTTPS
server.
Principle Explanation
Handle Errors Safely Don’t expose stack traces or internal info in errors.
Keep Dependencies Updated Old libraries can have known exploits.
Give only the minimum access needed — to users
Least Privilege Principle
and services.
🛠 Examples by Language
Python / FastAPI Example
# Dangerous: vulnerable to SQL Injection
query = f"SELECT * FROM users WHERE email = '{user_email}'"
cursor.execute(query)
# Secure: use parameterized queries
cursor.execute("SELECT * FROM users WHERE email = %s", (user_email,))
Java / Spring Boot Example
// Don't do this
String query = "SELECT * FROM users WHERE id = " + userInput;
// Do this instead
PreparedStatement ps = connection.prepareStatement("SELECT * FROM users WHERE
id = ?");
ps.setInt(1, userId);
Best Practices in Code
• Always validate and sanitize inputs (pydantic in FastAPI helps!)
• Use prepared statements (avoid raw SQL)
• Use HTTPS and environment variables (never hard-code secrets)
• Don’t log sensitive info (passwords, tokens)
• Limit file uploads: size, type, and path
• Set proper HTTP headers (X-Content-Type-Options, Strict-Transport-Security)
Secure Coding Tools
Tool Use
Bandit (Python) Scans for insecure code patterns
SonarQube Finds bugs, vulnerabilities, and code smells
OWASP Dependency-Check Detects vulnerable packages
Black Duck / Snyk Commercial tools to scan for security issues
JWT.io Debugger To validate JSON Web Tokens (JWTs)
Security Mindset for Developers
Think like an attacker, code like a defender.
• Never assume input is safe
• Add security to your definition of done
• Review and test security like any other feature
OWASP Top 10 Security Vulnerabilities (2021 Edition)
Here’s a simplified table with each item, explanation, and example:
# Category Explanation Example
Users can access data or A normal user accessing
1 Broken Access Control
actions they shouldn’t. /admin routes
Weak or missing
Sending passwords over HTTP
2 Cryptographic Failures encryption leads to data
instead of HTTPS
exposure.
Attacker injects code via
3 Injection user inputs (e.g., SQL, ' OR 1=1-- in a login form
NoSQL, LDAP).
Poor security design or No rate-limiting or abuse
4 Insecure Design
missing threat modeling. checks
Security Wrong server, framework, Leaving debug mode on in
5
Misconfiguration or cloud configs. production
Vulnerable & Outdated Using libraries with known Using Log4j with the Log4Shell
6
Components security flaws. exploit
Identification &
Session IDs not rotating after
7 Authentication Weak login mechanisms.
login
Failures
Software & Data Trusting unverified code or Using unsigned third-party
8
Integrity Failures CI/CD artifacts. packages
Security Logging & Missing or weak Breach goes undetected for
9
Monitoring Failures monitoring, alerts, or logs. months
Server-Side Request Server makes unintended Attacker gets server to call
10
Forgery (SSRF) external requests. http://localhost/admin
What Developers Should Do
Vulnerability How to Protect
Injection Use parameterized queries (no string concat in SQL)
Access Control Use role-based permissions (RBAC)
Cryptographic Always use HTTPS, hash passwords (bcrypt), avoid custom
Failures encryption
Security Misconfig Disable debug logs, restrict CORS, review cloud configs
Logging Failures Enable logs, but avoid logging sensitive info (tokens, passwords)
SSRF Whitelist URLs, validate all outbound requests
🛡 OWASP Top 10 – Explained in Easy Language
1. Broken Access Control
What it means: Users can access things they shouldn’t.
Imagine: A student logging in to a school portal can access the teacher’s
dashboard. Oops!
How to fix:
• Always check roles and permissions on the server.
• Never rely on frontend (UI) to hide or disable buttons.
• Use role-based access control (RBAC).
# FastAPI example
@app.get("/admin")
def admin_data(current_user: User = Depends(get_user)):
if not current_user.is_admin:
raise HTTPException(status_code=403)
2. Cryptographic Failures
(Previously called: Sensitive Data Exposure)
What it means: Not protecting data in transit or at rest (bad encryption or none at
all).
Imagine: Sending a credit card number over HTTP – anyone can steal it!
How to fix:
• Always use HTTPS
• Hash passwords (bcrypt, argon2)
• Don’t log sensitive data
• Use proper SSL/TLS certificates
# Python: storing passwords securely
import bcrypt
hashed = bcrypt.hashpw(b"password123", bcrypt.gensalt())
3. Injection (SQL, NoSQL, etc.)
What it means: Attackers put malicious code into inputs, which gets executed by
the server.
Imagine: A login form where the user enters: ' OR 1=1 --, and it logs them in without
a password.
How to fix:
• Never build SQL with string concatenation
• Use parameterized queries
# Python (safe query)
cursor.execute("SELECT * FROM users WHERE email = %s", (user_email,))
4. Insecure Design
What it means: Your app design is flawed — even before coding.
🏗 Imagine: You built a house with no locks on the doors.
How to fix:
• Design for security: add rate-limiting, logging, verification steps
• Do threat modeling
• Add security into sprint planning and system design
5. Security Misconfiguration
What it means: Your server or app has the wrong settings.
Example:
• Default passwords still enabled
• CORS open to all (*)
• Dev/debug mode enabled in production
How to fix:
• Turn off debug mode in prod
• Review cloud security rules
• Use tools like Docker Bench, AWS Trusted Advisor
6. Vulnerable and Outdated Components
What it means: You use old libraries or dependencies with known security issues.
Imagine: You still use a version of Log4j that hackers already know how to break.
How to fix:
• Keep dependencies updated
• Use tools like pip-audit, OWASP Dependency-Check, Snyk
# Check Python package vulnerabilities
pip install pip-audit
pip-audit
7. Identification & Authentication Failures
What it means: Authentication (login) is broken or weak.
Example:
• Users can reuse old session tokens
• No account lockout after failed login attempts
How to fix:
• Use multi-factor authentication (MFA)
• Rotate session tokens after login
• Use libraries like FastAPI Users or Spring Security
8. Software & Data Integrity Failures
What it means: You’re trusting things that might have been tampered with.
Example:
• Using plugins from unknown sources
• Auto-deploying from GitHub without verification
How to fix:
• Use signed packages (PyPI and Maven support this)
• Validate file hashes
• Don’t blindly run scripts from the internet
9. Security Logging & Monitoring Failures
What it means: You’re blind to attacks — no logs, alerts, or tracking.
Imagine: An attacker is inside your system, but you have no logs, so you can’t tell.
How to fix:
• Log important actions (logins, errors, failed logins)
• Use a central logging system (like ELK, CloudWatch, Splunk)
• Set up alerts for suspicious behavior
10. Server-Side Request Forgery (SSRF)
What it means: An attacker tricks your server into making requests to internal or
sensitive services.
Example: Your API lets users enter a URL to fetch. They give http://localhost/admin,
and your server calls it!
How to fix:
• Validate URLs before making any requests
• Block internal IP ranges (127.0.0.1, 169.254.x.x, etc.)
• Use firewalls and access controls
Summary Table
OWASP # Risk Fix Strategy
1 Broken Access Control Enforce strict backend checks
2 Cryptographic Failures Use HTTPS, hash passwords
3 Injection Parameterized queries
4 Insecure Design Think security in design phase
5 Misconfiguration Review server & app settings
6 Outdated Components Keep libraries up-to-date
7 Auth Failures Secure login, sessions
8 Integrity Failures Use signed packages, verify
9 Logging Failures Log & monitor key actions
10 SSRF Block server-side fetch abuse
Secure Coding Guidelines for Python (FastAPI)
1. Input Validation & Sanitization
Why? To protect against injection attacks and malformed input.
Do:
• Use Pydantic models for type validation.
• Use regex or enums to restrict accepted values.
• Strip unwanted fields using extra=forbid.
from pydantic import BaseModel, Field, EmailStr
class UserCreate(BaseModel):
name: str = Field(..., max_length=50)
email: EmailStr
age: int = Field(..., ge=18, le=100)
class Config:
extra = "forbid" # Disallow unexpected fields
2. Authentication & Authorization
Why? Prevent unauthorized access to resources.
Do:
• Use OAuth2, JWT tokens, or fastapi-users.
• Protect sensitive routes with Depends(get_current_user).
• Check roles and permissions inside route handlers.
from fastapi import Depends, HTTPException
from .dependencies import get_current_user
@app.get("/admin/dashboard")
def read_admin_dashboard(current_user: User = Depends(get_current_user)):
if not current_user.is_admin:
raise HTTPException(status_code=403, detail="Not authorized")
3. Secure Password Handling
Why? Passwords must never be stored in plaintext.
Do:
• Use bcrypt or passlib for hashing passwords.
• Never log or return passwords in any response.
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str):
return pwd_context.hash(password)
def verify_password(plain, hashed):
return pwd_context.verify(plain, hashed)
4. Dependency Management
Why? Vulnerable libraries can compromise your app.
Do:
• Pin dependency versions (requirements.txt)
• Run security audits:
pip install pip-audit
pip-audit
• Avoid unknown or unofficial packages.
5. Use HTTPS in Production
Why? HTTP exposes passwords, tokens, and data.
Do:
• Use HTTPS (via reverse proxy like NGINX).
• Enforce HTTPS-only cookies (Secure=True).
6. Avoid Leaking Sensitive Info
Do:
• Don’t print or log secrets, passwords, tokens.
• Use .env files and environment variables.
# .env
DATABASE_URL=postgresql://user:pass@localhost/db
from dotenv import load_dotenv
import os
load_dotenv()
db_url = os.getenv("DATABASE_URL")
7. Prevent Path Traversal and File Upload Abuse
Do:
• Validate file extensions and names.
• Sanitize filenames using secure_filename.
from werkzeug.utils import secure_filename
filename = secure_filename(uploaded_file.filename)
8. CSRF & CORS Protections
Do:
• Use CORSMiddleware to allow only trusted origins.
• Protect against CSRF if you use session-based auth.
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourfrontend.com"],
allow_credentials=True,
allow_methods=["GET", "POST"],
allow_headers=["*"],
9. Set Secure HTTP Headers
Use a reverse proxy (like NGINX) or middleware like Starlette’s
HTTPSRedirectMiddleware and security headers middleware.
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
from starlette.middleware.trustedhost import TrustedHostMiddleware
app.add_middleware(HTTPSRedirectMiddleware)
app.add_middleware(TrustedHostMiddleware, allowed_hosts=["yourdomain.com"])
10. Logging and Monitoring
Why? Detect and respond to breaches or suspicious activity.
Do:
• Log logins, password changes, failed logins, admin actions.
• Use structured logging (loguru, structlog).
• Avoid logging sensitive data like tokens or PII.
11. Common Things to Avoid
Bad Practice Better Practice
Storing plain passwords Hash with bcrypt or argon2
Using eval() or exec() Avoid entirely
Accepting any user input as-is Use Pydantic + regex + enums
Trusting frontend auth Always recheck user permissions on backend
Hardcoding secrets in code Use environment variables + .env files
Exposing stack traces in prod Catch exceptions, return generic error messages
Summary Checklist for FastAPI Security
Area Best Practice
Input Validation Use Pydantic, regex, enums
JWT/OAuth2, password
Authentication
hashing
Authorization Backend role checks, RBAC
Secrets Use .env, don’t log secrets
Dependencies Run pip-audit, pin versions
Sanitize filenames, restrict
File Uploads
types
Area Best Practice
CORS Allow only trusted origins
Log safely, use structured
Logging
loggers
Set strict headers, use HTTPS
HTTP Headers
in prod
Secure Coding Guidelines for Java (Spring Boot)
1. Input Validation & Sanitization
Why? Prevent malicious input (e.g., SQL Injection, XSS).
Do:
• Use Bean Validation (javax.validation.constraints)
• Sanitize input strings using libraries like ESAPI if needed
public class UserDTO {
@NotNull
@Size(max = 50)
private String name;
@Email
private String email;
@Min(18)
@Max(100)
private int age;
And apply it:
@PostMapping("/register")
public ResponseEntity<?> registerUser(@Valid @RequestBody UserDTO user) {
// Safe handling
}
2. Authentication & Authorization
Why? To restrict access to sensitive resources.
Do:
• Use Spring Security
• Secure endpoints with roles and permissions
• Enable JWT or OAuth2 for stateless APIs
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/dashboard")
public String adminDashboard() {
return "Welcome Admin!";
Use @EnableGlobalMethodSecurity(prePostEnabled = true) to activate.
3. Secure Password Storage
Why? Storing raw passwords is a critical risk.
Do:
• Use BCryptPasswordEncoder for hashing
• Never log or send passwords in responses
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
4. Dependency Management
Why? Vulnerabilities often come from third-party libraries.
Do:
• Use dependency-check plugin (OWASP)
• Regularly check dependencies with:
mvn dependency-check:check
• Use only trusted libraries.
5. Prevent SQL Injection
Do:
• Use Spring Data JPA or Hibernate (prepared statements)
• Never concatenate user input into SQL
@Query("SELECT u FROM User u WHERE u.email = :email")
User findByEmail(@Param("email") String email);
6. Cross-Site Scripting (XSS) Prevention
Do:
• Escape outputs in HTML using frameworks like Thymeleaf
• Sanitize input on both frontend and backend
Thymeleaf escapes HTML by default:
<p th:text="${user.name}"></p>
7. Cross-Site Request Forgery (CSRF)
Do:
• CSRF is enabled by default in Spring Security
• For stateless APIs (JWT), you can disable CSRF:
http.csrf().disable();
8. Use HTTPS in Production
Do:
• Always serve your app over HTTPS
• Set secure cookies:
server.servlet.session.cookie.secure=true
9. Avoid Exposing Sensitive Information
Do:
• Never return stack traces in production
• Customize exception handlers using @ControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleException(Exception ex) {
return ResponseEntity.status(500).body("Something went wrong");
10. Security Headers
Set common headers using Spring Security config:
http
.headers()
.contentSecurityPolicy("script-src 'self'")
.xssProtection()
.frameOptions().deny();
11. Avoid Hardcoding Secrets
Do:
• Store secrets in environment variables or .properties files.
• Use tools like Spring Cloud Vault or AWS Secrets Manager.
# application.properties
db.password=${DB_PASSWORD}
Summary Table: Secure Spring Boot Practices
Area Best Practice
Input Validation Use @Valid, @NotNull, @Size, etc.
Authentication Use Spring Security, JWT, OAuth2
Passwords Store hashed using BCrypt
SQL Injection Use JPQL / prepared queries
XSS Escape output in views (Thymeleaf)
CSRF Enabled by default (disable for stateless JWT)
HTTPS Enforce HTTPS, Secure cookies
Exception Leaks Use @ControllerAdvice, avoid stack traces
Dependency Safety Run OWASP dependency-check
Secrets Handling Use environment variables, not hardcoding
Tools to Integrate
Tool Purpose
OWASP Zap Penetration testing
SonarQube Static analysis, vulnerability scan
Spring Security Core security framework
Maven plugin – OWASP Dependency vulnerability check
Snyk Real-time vuln scan for dependencies
✅ Input Validation & Data Sanitization in FastAPI (In
Depth)
Why It Matters
Input validation and sanitization protect your application from:
• Invalid or malicious input (e.g., SQL Injection, XSS, security attacks)
• Crashes due to wrong data types
• Unexpected behavior from missing or bad input
Key Concepts
Concept Purpose
Validation Ensures data is correct and expected
Sanitization Cleans/removes unwanted characters
Schema (Pydantic) Defines data types and rules
FastAPI Uses Pydantic for Input Validation
Pydantic models power FastAPI's request parsing. You define what data is expected,
and FastAPI automatically:
• Parses JSON
• Validates types and constraints
• Returns 422 Unprocessable Entity if input is invalid
Example 1: Basic Input Validation with Pydantic
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
app = FastAPI()
class User(BaseModel):
name: str = Field(..., max_length=50)
email: EmailStr
age: int = Field(..., ge=18, le=100)
@app.post("/register")
def register_user(user: User):
return {"message": f"Welcome {user.name}"}
What’s Validated?
• name: required, string, max 50 chars
• email: must be a valid email
• age: must be 18 to 100
Example 2: Custom Validation with Pydantic
from pydantic import validator
class User(BaseModel):
username: str
password: str
@validator("password")
def strong_password(cls, v):
if len(v) < 8:
raise ValueError("Password must be at least 8 characters")
return v
Input Sanitization
Validation checks correctness, sanitization removes or escapes bad input.
While Pydantic does not auto-sanitize (like removing HTML tags), you can manually
sanitize:
Example 3: Manual Input Sanitization
import re
def sanitize_text(text: str) -> str:
# Remove HTML tags
return re.sub(r'<.*?>', '', text)
@app.post("/submit-comment")
def submit(comment: str):
clean = sanitize_text(comment)
return {"sanitized": clean}
Security-Focused Validation & Sanitization Tips
Attack Type Prevention Strategy
SQL Injection Use ORMs like SQLAlchemy (avoid raw SQL with user input)
XSS Sanitize strings before rendering in frontend (e.g., remove <script>)
Path Traversal Use Path() from FastAPI to validate paths, avoid file injections
Invalid Input Use Pydantic for schema-level validation
Optional: Use Third-Party Libraries for Sanitization
• bleach – for HTML/XSS sanitization
• python-slugify – for clean URLs
Example with bleach:
import bleach
@app.post("/safe-message")
def safe_msg(msg: str):
safe = bleach.clean(msg)
return {"safe": safe}
Best Practices Checklist
Define input schemas with Pydantic
Use Field() for type constraints
Validate data length, formats, enums
Sanitize user-generated strings before storing or displaying
Never trust query parameters or JSON bodies as-is
Log validation failures (in production logs, not to users)
Return helpful but safe error messages (no stack traces)
Test Invalid Input
Test your API by sending:
"name": "Hacker<script>alert(1)</script>",
"email": "bademail.com",
"age": -5
FastAPI will reject it with detailed error messages!
Input validation and data sanitization in Spring Boot
Why Input Validation and Sanitization Matter
In Spring Boot applications, user input is typically accepted via:
• @RequestBody (JSON from frontend)
• @RequestParam (query params)
• @PathVariable (URL path parts)
• HTML Forms (for web apps)
You must never trust user input. Attackers often inject invalid or malicious data (e.g.,
SQL, script tags, long inputs).
Core Concepts
Concept What it Does
Validation Checks input format, value range, etc.
Sanitization Cleans or escapes unwanted characters
Spring Boot Tools for Input Validation
Tool Purpose
Built-in annotations like @NotNull,
javax.validation (Jakarta Bean Validation)
@Email, etc.
Used to trigger validation on request
@Valid / @Validated
body or params
BindingResult or
Used to handle validation errors
MethodArgumentNotValidException
Write your own rules beyond built-in
Custom Validators
ones
Step-by-Step: Input Validation with Example
1. Maven Dependencies (if using Spring Boot < 3)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2. DTO with Validation Annotations
import jakarta.validation.constraints.*;
public class UserDTO {
@NotBlank(message = "Name is required")
@Size(max = 50)
private String name;
@Email(message = "Invalid email")
private String email;
@Min(18)
@Max(100)
private int age;
// Getters and setters
}
3. Controller Using @Valid
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping("/register")
public String register(@RequestBody @Valid UserDTO user) {
return "User registered: " + user.getName();
4. Error Handling (Optional)
Create a @ControllerAdvice to return custom validation error responses:
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<?>
handleValidationErrors(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors()
.forEach(err -> errors.put(err.getField(), err.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}
Input Sanitization in Spring Boot
Spring doesn't auto-sanitize inputs like stripping HTML — you must manually sanitize
fields that may hold text input from users.
Common Techniques
1. Escape HTML Using Apache Commons Text
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
import org.apache.commons.text.StringEscapeUtils;
public String sanitize(String input) {
return StringEscapeUtils.escapeHtml4(input); // Remove <script> etc.
2. Prevent SQL Injection
Use JPA or Spring Data Repositories
Avoid building raw SQL strings from user input
Best Practices Table
Input Type Validation Approach Sanitization Needed?
JSON body @Valid, DTO classes Yes (for text input)
Query params @RequestParam + validation annotations Optional
Path variables Use @PathVariable + enum/type checks Optional
Form data Use Spring Forms with HTML escaping Yes
What to Avoid
Mistake Why it's Dangerous
Trusting raw request values Opens door for XSS, injection
Not validating enums/IDs Attackers can tamper inputs
Echoing user input in HTML directly XSS vulnerability
Bonus: Custom Validation Example
@Constraint(validatedBy = NoSpecialCharsValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NoSpecialChars {
String message() default "Special characters not allowed";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
public class NoSpecialCharsValidator implements
ConstraintValidator<NoSpecialChars, String> {
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && value.matches("^[a-zA-Z0-9 ]*$");
Use it like:
@NoSpecialChars
private String username;
Summary Checklist
• Use @Valid in controllers
• Use DTO classes with @NotBlank, @Size, @Email, etc.
• Manually sanitize input that could be rendered (e.g., names, comments)
• Avoid raw SQL — use ORM (Hibernate/JPA)
• Escape or sanitize text fields before storage/display
• Always handle invalid input with safe error responses
Authentication and authorization best practices
Authentication vs Authorization
Term Meaning
Authentication Verifies who the user is (e.g., login with token, OAuth, etc.)
Authorization Verifies what the authenticated user is allowed to do (e.g., roles)
Common Authentication Methods in FastAPI
Method Description Use When...
Username/password in header Rarely used, mostly for internal
Basic Auth
(insecure) APIs
OAuth2 / JWT Token-based authentication Most secure and scalable
API Key Simple token passed via header/query For machine-to-machine APIs
Session
For browser-based login systems If building full-stack web apps
Cookies
Authentication with OAuth2 + JWT (Recommended)
FastAPI provides first-class support for OAuth2 with JWT (JSON Web Tokens).
1. Install Required Libraries
pip install fastapi[all] python-jose[cryptography] passlib[bcrypt]
2. Create Token Utilities
# auth_utils.py
from jose import JWTError, jwt
from datetime import datetime, timedelta
SECRET_KEY = "your_secret"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
3. Set Up Dependency to Verify Token
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def get_current_user(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
if username is None:
raise HTTPException(status_code=401, detail="Invalid credentials")
return {"username": username}
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
4. Protect Routes
@app.get("/users/me")
def read_users_me(current_user: dict = Depends(get_current_user)):
return current_user
Authorization with Roles/Scopes
Example: Require Admin Role
def get_current_admin(current_user=Depends(get_current_user)):
if current_user["username"] != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
return current_user
Use it in a route:
@app.get("/admin")
def admin_dashboard(current_user=Depends(get_current_admin)):
return {"msg": "Welcome Admin"}
🛡 Best Practices for Authentication & Authorization in FastAPI
1. Always use HTTPS in production
• Never transmit tokens or passwords over plain HTTP.
• Use https:// and secure cookies if needed.
2. Use JWT tokens securely
• Set expiration (exp) claim
• Use secure secret keys (SECRET_KEY)
• Rotate keys periodically
3. Store hashed passwords
Use passlib to hash passwords (e.g., bcrypt):
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password):
return pwd_context.hash(password)
def verify_password(plain, hashed):
return pwd_context.verify(plain, hashed)
4. Implement Role-Based Access Control (RBAC)
Store roles in your user model and check roles before actions.
Example:
if current_user["role"] != "admin":
raise HTTPException(status_code=403)
5. Prevent JWT replay attacks
• Set short expiration (15–30 minutes)
• Store token identifiers in DB if needed
• Use refresh tokens for longer sessions
6. Log security-related events
• Login attempts
• Token refresh
• Unauthorized access attempts
7. Use OAuth2 for third-party login (optional)
Use fastapi-login, authlib, or python-social-auth to integrate Google/GitHub login.
Secure Cookie-based Sessions (Alternative to JWT)
Use if you're building full-stack apps with FastAPI + Jinja.
• Use response.set_cookie() with HttpOnly, Secure, SameSite flags.
• Avoid storing sensitive data directly in cookies.
Libraries & Tools to Explore
• python-jose – JWT encoding/decoding
• passlib – Secure password hashing
• fastapi-users – Full user auth system
• authlib – For advanced OAuth2/OpenID support
Summary
Checklist Status
Use JWT + OAuth2 securely
Protect sensitive routes with roles
Hash passwords with bcrypt
Implement token expiration
Avoid exposing sensitive info
Log auth events and errors
Authentication and authorization best practices in Spring Boot
What’s the Difference?
Term Description
Authentication Verifies who you are (e.g., via login credentials)
Authorization Verifies what you are allowed to do (e.g., roles, scopes)
Common Authentication Methods in Spring Boot
Method Use Case
Basic Auth Simple login for internal tools or testing
Form-based login Web applications with UI
JWT (Stateless) Modern REST APIs
OAuth2 Third-party login via Google/GitHub/etc.
Session-based Traditional web apps
Spring Security Basics
Maven Dependency (Spring Boot Starter)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Basic Flow with JWT
1. User sends login credentials to /login
2. Spring authenticates and returns a JWT
3. User includes JWT in Authorization: Bearer <token> header in future requests
4. Spring validates token, extracts identity & roles
Best Practices: Authentication in Spring Boot
1. Use BCrypt for Password Hashing
Hash and store passwords safely.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
Use passwordEncoder.encode(plainPassword) to hash passwords.
2. JWT Token Generation & Validation
• Use libraries like jjwt or auth0/java-jwt
• Add expiration to tokens
• Never store sensitive info in JWT payload
Sample filter:
// Custom filter to validate JWT
public class JwtRequestFilter extends OncePerRequestFilter {
// Extract token and validate
3. Secure APIs with Role-based Access
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
4. Protect Sensitive Endpoints
Use annotations:
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/data")
public ResponseEntity<?> adminOnly() { ... }
Enable via:
@EnableGlobalMethodSecurity(prePostEnabled = true)
5. Don’t Hardcode Secrets
Use application.yml or Spring Config Server for secrets.
jwt:
secret: ${JWT_SECRET}
6. Enable HTTPS in Production
• Never allow credentials or tokens over plain HTTP.
• Use reverse proxy (e.g., Nginx) to enforce SSL.
Best Practices: Authorization in Spring Boot
Principle Best Practice Example
Least privilege Give minimum necessary roles to users
Role hierarchy Define structured access: ADMIN > MANAGER > USER
Centralized role check Avoid hardcoding if (user.role == ...) all over
Principle Best Practice Example
Use annotations @PreAuthorize, @Secured, etc.
Session vs JWT
Feature Session-Based JWT-Based (Stateless)
Storage Server memory Client-side
Scalability Limited (server-bound) Highly scalable
Recommended Full web apps REST APIs, microservices
🛡 Tips for Secure Spring Boot Apps
1. Set security headers
http
.headers()
.xssProtection()
.and()
.contentSecurityPolicy("script-src 'self'");
2. Limit login attempts
Prevent brute-force attacks.
3. Enable CSRF only for web apps
Disable for APIs:
http.csrf().disable();
4. Use HTTPS everywhere
5. Log security events
Log authentication, errors, and denied access for auditing.
Recommended Libraries/Tools
Tool Purpose
Spring Security AuthN + AuthZ framework
jjwt JWT creation/validation
OAuth2 client Google, GitHub login integration
BCrypt Secure password hashing
Summary Checklist
Practice Done?
Use Spring Security
Hash passwords with BCrypt
Secure API routes with roles
Use JWT for stateless auth (REST)
Never hardcode credentials
Enable HTTPS in production
Log security events
Security Code Review Checklist
1. Authentication
• Are strong password policies enforced?
• Are passwords hashed using a strong algorithm (e.g., BCrypt, Argon2)?
• Is multi-factor authentication (MFA) supported for sensitive areas?
• Is the authentication mechanism stateless (JWT) or session-based, and is it
appropriate for the use case?
• Is the login endpoint protected against brute-force attacks (e.g., rate limiting)?
2. Authorization
• Are users authorized based on roles/permissions?
• Is access control enforced at both API layer and business logic layer?
• Are endpoints annotated with role restrictions (@PreAuthorize,
Depends(get_current_user) in FastAPI)?
• Are sensitive actions (like user deletion) double-checked for correct roles?
3. Input Validation & Output Encoding
• Is all user input validated on both client and server side?
• Are data types, formats, and lengths strictly checked (e.g., using Pydantic in
FastAPI or Bean Validation in Java)?
• Is untrusted input never inserted directly into HTML, SQL, or command lines?
• Are HTML/JS outputs properly escaped to avoid XSS?
4. Data Protection
• Is HTTPS enforced on all environments (not just production)?
• Are sensitive data fields encrypted at rest (e.g., credit cards)?
• Is JWT signed using a secure algorithm (e.g., RS256, HS256) and properly
validated?
• Are secrets, tokens, and keys stored in secure configs or environment variables?
5. Error Handling & Logging
• Are detailed error messages hidden from users (e.g., no stack traces)?
• Are security events logged (logins, failed attempts, permission denials)?
• Are logs scrubbed to avoid storing sensitive data (e.g., passwords, tokens)?
• Are logs rotated and access-controlled?
6. Dependency and Package Security
• Are you using a vulnerability scanner (e.g., Dependabot, Snyk, OWASP
Dependency-Check)?
• Are dependencies locked using version pinning (requirements.txt, pom.xml)?
• Are unused libraries or services removed?
• Are third-party libraries vetted and well-maintained?
7. Session Management
• Are sessions invalidated on logout?
• Is there an expiration mechanism for JWT or sessions?
• Is session ID stored securely (e.g., HttpOnly, Secure cookies)?
• Is session fixation prevented (regenerate session IDs after login)?
8. Secure Configuration
• Is debug mode disabled in production (DEBUG = False,
spring.profiles.active=prod)?
• Are security headers used? (CSP, X-Frame-Options, etc.)
• Are CORS policies properly configured (avoid '*' in production)?
• Are sensitive files (.env, settings.py, application.properties) excluded from
version control?
9. File Uploads
• Are file types validated on upload?
• Are uploaded files stored outside the web root or scanned for malware?
• Are filename/path sanitization and size limits applied?
10. API Security
• Is rate limiting enabled for public endpoints?
• Are APIs protected using tokens (JWT, API Keys, OAuth2)?
• Are GraphQL/REST endpoints checked for excessive data exposure?
• Are OPTIONS/TRACE methods disabled unless required?
11. Code-Specific Checks
• Are SQL queries parameterized? (to avoid SQL Injection)
• Are command line executions sanitized? (subprocess, Runtime.getRuntime)
• Are temporary files and user data cleaned up securely?
• Are background jobs (cron, async tasks) validated and authenticated?
Optional: Security Review Tags for PRs
Include tags or comments like:
#SECURITY-INPUT-VALIDATION
#SECURITY-AUTH
#SECURITY-FILE-UPLOAD
To help security engineers during PR reviews.
Recommended Tools
Tool Purpose
SonarQube Code quality + security detection
Tool Purpose
Bandit (Python) Static analysis for security in Python
OWASP ZAP Dynamic app security testing
Snyk Dependency scanning
Dependabot GitHub-based auto-patching
Code encryption and obfuscation
Code Encryption vs Obfuscation
Feature Encryption Obfuscation
Transforms code into ciphertext Transforms code into difficult-to-
What it is
using encryption algorithms understand versions
To protect code/data at rest or in To prevent easy reverse engineering
Purpose
transit or understanding of the source
Mostly for storing/deploying code Mostly for client-side apps or
When used
or secrets securely distributed binaries
Who The system or application itself No decryption — runs as-is, but is
decrypts it (during runtime or build) hard to understand
Encrypting Python .pyc files; Renaming variables to a, b, c;
Examples
encrypting config files making control flow unreadable
1. Why Use These Techniques?
• Prevent IP theft (especially in frontend or distributed apps)
• Make reverse engineering harder (useful in Java/.NET which are easy to
decompile)
• Hide business logic or license checks
• Protect API keys or credentials (though this should be avoided—best stored
securely)
2. Obfuscation in Practice
Java Obfuscation
Java bytecode is easy to reverse engineer with tools like JD-GUI, so Java apps often use:
Tools:
• ProGuard: Popular tool for Android & Java apps.
• DexGuard: Commercial alternative for Android.
• yGuard, Zelix KlassMaster
What it does:
• Renames classes/methods/variables (UserManager → a)
• Strips out debug symbols and unused code
• Encrypts string literals
• Flattens control flow
Example:
Before:
public class UserService {
public boolean isUserLoggedIn(String username) { ... }
After ProGuard:
public class a {
public boolean a(String a) { ... }
Python Obfuscation
Python is interpreted and open, making it easy to inspect source. You can obfuscate it
using:
Tools:
• Pyarmor
• Nuitka (compiles to C)
• Cython (convert to C extensions)
• pyminifier
These tools:
• Encrypt .py files
• Convert Python code to bytecode or C code
• Obfuscate function and variable names
3. Code Encryption Techniques
Use Cases:
• Encrypting Python .pyc files before deployment
• Storing encrypted config files or secrets
• Java class files encrypted and loaded at runtime
Tools:
• PyArmor: Encrypts .py files and adds a bootloader to decrypt at runtime
• Jasypt (Java): Encrypts sensitive configs
• AES/RSA based loaders: Custom loaders that decrypt encrypted modules on-
the-fly
4. What NOT to Do
• Don’t hide secrets in obfuscated code — they can still be extracted
• Don’t rely solely on obfuscation for security
• Don’t skip proper authorization and validation thinking obfuscation is
enough
Obfuscation is security through obscurity — it should complement, not replace,
security best practices.
5. Best Practices
Goal Best Practice
Prevent
Use obfuscation tools on your Java/.NET builds
decompilation
Secure Python code Use PyArmor, Cython, or freeze to executable with PyInstaller
Encrypt with AES/GPG and use vault tools (e.g., HashiCorp
Secure config/secrets
Vault)
CI/CD protection Run obfuscation/encryption as part of your build pipeline
Great! Let’s do a hands-on exercise where you:
• Identify security vulnerabilities in sample Python (FastAPI) and Java (Spring
Boot) code
• Understand why each issue is dangerous
• Learn how to fix them with secure coding best practices
Hands-On: FastAPI (Python)
Insecure Sample Code
# app.py
from fastapi import FastAPI, Request
import sqlite3
app = FastAPI()
@app.get("/users")
def get_user(name: str):
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
# SQL Injection risk!
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")
user = cursor.fetchone()
return {"user": user}
Issues:
1. SQL Injection – direct string interpolation in SQL query.
2. No input validation – name is unchecked.
3. No error handling/logging – if DB fails, it crashes.
4. Connection not closed properly – resource leak.
Fixed Secure Version
from fastapi import FastAPI, Query, HTTPException
import sqlite3
app = FastAPI()
@app.get("/users")
def get_user(name: str = Query(..., min_length=1, max_length=50)):
try:
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
# Use parameterized queries
cursor.execute("SELECT * FROM users WHERE name = ?", (name,))
user = cursor.fetchone()
conn.close()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return {"user": user}
except Exception as e:
raise HTTPException(status_code=500, detail="Internal Server Error")
Fixes Applied:
• Parameterized SQL query → prevents SQL injection
• Input validation with Query()
• Exception handling with proper HTTP status
• Closing DB connection
Hands-On: Java (Spring Boot)
Insecure Sample Code
@RestController
public class LoginController {
@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String
password) {
// Storing plain text password in logs
System.out.println("Login attempt: " + username + " / " + password);
if(username.equals("admin") && password.equals("admin123")) {
return "Welcome!";
} else {
return "Invalid login";
Issues:
1. Logging passwords – major credential leakage risk.
2. Hardcoded credentials – should never be in code.
3. No input validation
4. No authentication mechanism (JWT, session, etc.)
Fixed Secure Version
@RestController
public class LoginController {
@Autowired
private AuthenticationManager authManager;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody AuthRequest request) {
try {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(request.getUsername(),
request.getPassword());
Authentication auth = authManager.authenticate(token);
// Generate JWT token here (mocked)
String jwt = JwtUtil.generateToken(request.getUsername());
return ResponseEntity.ok(new AuthResponse(jwt));
} catch (AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid
credentials");
// AuthRequest.java
public class AuthRequest {
private String username;
private String password;
// getters/setters
Fixes Applied:
• No sensitive data in logs
• Authentication done using Spring Security
• No hardcoded secrets
• Secure, extendable architecture using JWT or OAuth
Great! Let’s walk through one more FastAPI security hands-on example — this time
focusing on:
• Missing authentication
• Sensitive data exposure
• Lack of rate limiting / protection against brute-force
Insecure FastAPI Code: Login Endpoint
from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login")
def login(username: str = Form(...), password: str = Form(...)):
# NEVER log credentials!
print(f"Login attempt - username: {username}, password: {password}")
# Hardcoded login logic
if username == "admin" and password == "secret123":
return {"message": "Login successful"}
return {"message": "Invalid credentials"}
Security Problems
Problem Why it’s dangerous
No Authentication Anyone can call this endpoint freely
Logging credentials Exposes sensitive user data in logs
Hardcoded secrets Easily leaked or reused
Problem Why it’s dangerous
No rate limiting Vulnerable to brute-force attacks
No hashing of passwords Passwords compared in plain text
Secure & Improved Version
Here’s a more realistic and secure login flow:
from fastapi import FastAPI, Form, HTTPException, Depends
from passlib.context import CryptContext
from pydantic import BaseModel
from fastapi.security import OAuth2PasswordRequestForm
app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Mock user "database"
users_db = {
"admin": {
"username": "admin",
"hashed_password": pwd_context.hash("secret123")
# Dependency
def authenticate_user(username: str, password: str):
user = users_db.get(username)
if not user:
return False
if not pwd_context.verify(password, user["hashed_password"]):
return False
return user
@app.post("/login")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=401, detail="Invalid username or password")
return {"message": "Login successful", "user": user["username"]}
What Did We Fix?
Fix Explanation
Password hashing Passwords are stored & compared securely
No logging sensitive data No print() of credentials
OAuth2PasswordRequestForm Standard format for login
Proper error handling Uses HTTPException with 401
Authentication logic separated Clean, reusable function