Identify security vulnerabilities and suggest secure coding practices
When reviewing code for security issues, systematically check for common vulnerabilities and suggest secure alternatives.
Look for:
Vulnerable:
// SQL Injection
String query = "SELECT * FROM users WHERE username = '" + username + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
// Command Injection
Runtime.getRuntime().exec("ping " + userInput);
Secure:
// Use Prepared Statements
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, username);
ResultSet rs = pstmt.executeQuery();
// Avoid direct command execution; use APIs instead
// If unavoidable, validate and sanitize input
List<String> allowedHosts = Arrays.asList("localhost", "example.com");
if (allowedHosts.contains(userInput)) {
// proceed
}
Look for:
Vulnerable:
// Plain text password
user.setPassword(password);
// Weak session ID
String sessionId = user.getId() + System.currentTimeMillis();
Secure:
// Hash passwords with bcrypt
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String hashedPassword = encoder.encode(password);
user.setPassword(hashedPassword);
// Use cryptographically secure random session IDs
String sessionId = UUID.randomUUID().toString();
Look for:
Vulnerable:
// Logging sensitive data
logger.info("User password: " + password);
// Hardcoded secrets
String apiKey = "sk_live_1234567890abcdef";
// Detailed error messages
catch (Exception e) {
return "Database error: " + e.getMessage();
}
Secure:
// Mask sensitive data in logs
logger.info("User authenticated: " + username);
// Use environment variables
String apiKey = System.getenv("API_KEY");
// Generic error messages for users
catch (Exception e) {
logger.error("Database error", e); // Log details internally
return "An error occurred. Please try again later.";
}
Look for:
Vulnerable:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(userProvidedXml);
Secure:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(userProvidedXml);
Look for:
Vulnerable:
// No authorization check
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id);
}
// Path traversal
File file = new File("/uploads/" + filename);
Secure:
// Check authorization
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id, Principal principal) {
User currentUser = getCurrentUser(principal);
if (!currentUser.canAccess(id)) {
throw new AccessDeniedException("Unauthorized");
}
return userRepository.findById(id);
}
// Validate and sanitize file paths
Path basePath = Paths.get("/uploads").toAbsolutePath().normalize();
Path filePath = basePath.resolve(filename).normalize();
if (!filePath.startsWith(basePath)) {
throw new SecurityException("Invalid file path");
}
Look for:
Vulnerable:
# application.yml
spring:
profiles:
active: dev
debug: true
Secure:
# application-prod.yml
spring:
profiles:
active: prod
debug: false
# Add security headers
server:
servlet:
session:
cookie:
secure: true
http-only: true
Look for:
Vulnerable:
// Reflected XSS
document.getElementById('greeting').innerHTML =
"Hello " + userInput;
// DOM-based XSS
element.innerHTML = location.hash.substring(1);
Secure:
// Escape user input
document.getElementById('greeting').textContent =
"Hello " + userInput;
// Use safe methods
const div = document.createElement('div');
div.textContent = userInput;
element.appendChild(div);
Look for:
Vulnerable:
ObjectInputStream ois = new ObjectInputStream(userInputStream);
Object obj = ois.readObject(); // Dangerous!
Secure:
// Use safe alternatives like JSON
ObjectMapper mapper = new ObjectMapper();
MyObject obj = mapper.readValue(jsonString, MyObject.class);
// If ObjectInputStream required, validate class types
ObjectInputStream ois = new ObjectInputStream(userInputStream) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
if (!desc.getName().equals("com.example.SafeClass")) {
throw new InvalidClassException("Unauthorized deserialization");
}
return super.resolveClass(desc);
}
};
Look for:
Check:
# Maven
mvn versions:display-dependency-updates
mvn dependency-check:check
# npm
npm audit
# Python
pip-audit
Recommendation:
Look for:
Vulnerable:
@PostMapping("/login")
public void login(String username, String password) {
if (authenticate(username, password)) {
// Login successful
}
// No logging
}
Secure:
@PostMapping("/login")
public void login(String username, String password, HttpServletRequest request) {
boolean success = authenticate(username, password);
if (success) {
auditLog.info("Successful login: user={}, ip={}",
username, request.getRemoteAddr());
} else {
auditLog.warn("Failed login attempt: user={}, ip={}",
username, request.getRemoteAddr());
failedLoginTracker.record(username, request.getRemoteAddr());
}
}
Look for:
Vulnerable:
MessageDigest md = MessageDigest.getInstance("MD5");
Random random = new Random();
byte[] key = "hardcodedkey1234".getBytes();
Secure:
MessageDigest md = MessageDigest.getInstance("SHA-256");
SecureRandom random = new SecureRandom();
byte[] key = loadKeyFromSecureStorage();
Look for:
Secure Practices:
@RestController
@RequestMapping("/api/v1")
public class UserController {
// Rate limiting
@RateLimiter(name = "userApi")
@GetMapping("/users")
public Page<UserDto> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
Principal principal) {
// Validate input
if (size > 100) {
throw new IllegalArgumentException("Page size too large");
}
// Return only necessary fields
return userService.findAll(page, size)
.map(this::toDto);
}
}
When reviewing code, check:
When documenting security findings:
### [CRITICAL] SQL Injection in User Search
**Location**: UserController.java:45
**Issue**: User input is concatenated directly into SQL query, allowing SQL injection attacks.
**Vulnerable Code**:
```java
String query = "SELECT * FROM users WHERE name LIKE '%" + searchTerm + "%'";
Impact: Attackers can execute arbitrary SQL, potentially accessing all database data or modifying records.
Recommendation: Use parameterized queries with PreparedStatement.
Fixed Code:
String query = "SELECT * FROM users WHERE name LIKE ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, "%" + searchTerm + "%");
Severity: Critical CVSS Score: 9.8 (Critical) Remediation Priority: Immediate
## When This Skill Activates
This skill automatically activates when:
- Reviewing code for security vulnerabilities
- Performing security audits
- Analyzing authentication/authorization code
- Checking for OWASP Top 10 vulnerabilities
- Questions about secure coding practices
- Reviewing API security
- Analyzing cryptographic implementations