Secure coding is essential for building robust and secure applications. This guide covers fundamental security practices and common vulnerabilities to avoid.
Key areas covered:
public class InputValidator {
public boolean validateInput(String input) {
// Whitelist validation
return input != null &&
input.matches("^[a-zA-Z0-9\\s-_]{1,50}$");
}
public String sanitizeHtml(String input) {
// Use a library like OWASP Java Encoder
return Encode.forHtml(input);
}
public boolean validateEmail(String email) {
if (email == null || email.isEmpty()) {
return false;
}
// Email validation pattern
String pattern = "^[A-Za-z0-9+_.-]+@(.+)$";
return email.matches(pattern);
}
public String sanitizeSql(String input) {
// Use PreparedStatement instead
throw new IllegalStateException(
"Use PreparedStatement for SQL");
}
}
public class SecureDao {
public User findUser(String username) {
String sql = "SELECT * FROM users WHERE username = ?";
try (PreparedStatement stmt =
connection.prepareStatement(sql)) {
stmt.setString(1, username);
ResultSet rs = stmt.executeQuery();
return mapResultToUser(rs);
}
}
public void updateUser(User user) {
String sql = "UPDATE users SET email = ? " +
"WHERE id = ?";
try (PreparedStatement stmt =
connection.prepareStatement(sql)) {
stmt.setString(1, user.getEmail());
stmt.setLong(2, user.getId());
stmt.executeUpdate();
}
}
}
public class PasswordService {
private static final int SALT_LENGTH = 16;
private static final int ITERATIONS = 10000;
private static final int KEY_LENGTH = 256;
public String hashPassword(String password) {
byte[] salt = generateSalt();
byte[] hash = hashWithPbkdf2(password, salt);
return Base64.encode(salt) + ":" +
Base64.encode(hash);
}
private byte[] hashWithPbkdf2(
String password, byte[] salt) {
KeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
ITERATIONS,
KEY_LENGTH
);
try {
SecretKeyFactory factory =
SecretKeyFactory.getInstance(
"PBKDF2WithHmacSHA256");
return factory.generateSecret(spec)
.getEncoded();
} catch (Exception e) {
throw new RuntimeException(
"Error hashing password", e);
}
}
private byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_LENGTH];
random.nextBytes(salt);
return salt;
}
}
public class JwtService {
private static final String SECRET_KEY =
System.getenv("JWT_SECRET");
private static final long EXPIRATION = 86400000; // 24h
public String generateToken(User user) {
Date now = new Date();
Date expiry = new Date(
now.getTime() + EXPIRATION);
return Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(now)
.setExpiration(expiry)
.signWith(SignatureAlgorithm.HS512,
SECRET_KEY)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token);
return true;
} catch (JwtException e) {
return false;
}
}
}
public class EncryptionService {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int TAG_LENGTH = 128;
public byte[] encrypt(byte[] data, SecretKey key) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
byte[] iv = generateIv();
GCMParameterSpec spec =
new GCMParameterSpec(TAG_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] encrypted = cipher.doFinal(data);
return combineIvAndData(iv, encrypted);
} catch (Exception e) {
throw new RuntimeException(
"Encryption failed", e);
}
}
public byte[] decrypt(byte[] data, SecretKey key) {
try {
byte[] iv = extractIv(data);
byte[] encrypted = extractData(data);
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec spec =
new GCMParameterSpec(TAG_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
return cipher.doFinal(encrypted);
} catch (Exception e) {
throw new RuntimeException(
"Decryption failed", e);
}
}
private byte[] generateIv() {
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
return iv;
}
}
public class SecureErrorHandler {
private static final Logger log =
LoggerFactory.getLogger(
SecureErrorHandler.class);
public ApiResponse handleError(Exception e) {
// Log full error for debugging
log.error("Error occurred", e);
// Return safe error message to client
if (e instanceof ValidationException) {
return new ApiResponse(400,
"Invalid input provided");
} else if (e instanceof AuthenticationException) {
return new ApiResponse(401,
"Authentication required");
} else {
// Generic error for unknown issues
return new ApiResponse(500,
"An internal error occurred");
}
}
public void logSecurityEvent(
String event, String username) {
// Structured logging for security events
log.info("Security event: {} for user: {}",
event, username);
}
}
Implementing secure coding practices is crucial for building robust and secure applications. By following the principles and practices outlined in this guide, you can significantly improve your application's security posture.
Remember to stay updated with security best practices and regularly review and update your security measures.