CWE-308 in APIs
- CWE ID
- CWE-308
- Category
- Authentication
- Severity
- MEDIUM
- Short Name
- Single Factor
What is Cwe 308?
CWE-308: Use of Single-factor Authentication is a weakness where a system relies solely on one authentication factor—typically a password—without requiring additional verification methods. This creates a significant security vulnerability because if an attacker compromises the single factor, they gain full access to the system.
The weakness manifests when systems authenticate users based on only one of these factors: something you know (password), something you have (security token), or something you are (biometric). By using just one factor, the system lacks the layered defense that multi-factor authentication (MFA) provides. An attacker who obtains a user's password through phishing, credential stuffing, or database breaches can immediately impersonate that user without any additional barriers.
Cwe 308 in API Contexts
In API environments, CWE-308 appears when APIs accept only password-based authentication without additional verification. This is particularly dangerous because APIs often handle sensitive operations like financial transactions, data access, and system administration.
Common API manifestations include:
- APIs that accept only API keys or basic authentication without requiring second factors
- OAuth implementations that skip step-up authentication for high-risk operations
- Session-based APIs that rely solely on cookies without additional verification
- Administrative APIs accessible with single-factor credentials
- Database APIs exposed with only password protection
The risk escalates when APIs handle privileged operations. For example, an API endpoint that allows password changes or fund transfers should require additional verification beyond just the user's primary credentials. Without this, attackers who steal credentials can immediately perform destructive actions.
Detection
Detecting CWE-308 requires examining authentication mechanisms and testing for single-factor dependencies. Manual code review can identify authentication patterns, but automated scanning provides comprehensive coverage.
middleBrick's authentication scanning specifically tests for single-factor authentication weaknesses by analyzing API endpoints and their authentication requirements. The scanner examines:
- Authentication method diversity (checking for only password-based systems)
- Step-up authentication requirements for sensitive operations
- Session management and token validation patterns
- Administrative endpoint protection levels
During scanning, middleBrick tests whether APIs require multiple authentication factors for different operations. For instance, it checks if password changes or account modifications require additional verification beyond the initial login. The scanner also analyzes OpenAPI specifications to identify endpoints that should implement MFA but don't.
Security teams should also look for indicators like APIs that accept only basic auth, endpoints without rate limiting on authentication attempts, and administrative interfaces accessible with single credentials.
Remediation
Fixing CWE-308 involves implementing multi-factor authentication and applying the principle of least privilege. Here's how to remediate this weakness in API implementations:
1. Implement Multi-Factor Authentication
// Before: Single-factor authentication only
app.post('/login', (req, res) => {
const { username, password } = req.body;
authenticateUser(username, password)
.then(user => res.json({ token: generateToken(user) }))
.catch(err => res.status(401).json({ error: 'Invalid credentials' }));
});
// After: Multi-factor authentication
app.post('/login', (req, res) => {
const { username, password } = req.body;
authenticateUser(username, password)
.then(user => {
// Send MFA code via SMS, email, or authenticator app
const mfaCode = generateMFACode();
sendMFACode(user.email, mfaCode);
res.json({ mfaRequired: true, userId: user.id });
})
.catch(err => res.status(401).json({ error: 'Invalid credentials' }));
});
app.post('/verify-mfa', (req, res) => {
const { userId, mfaCode } = req.body;
verifyMFACode(userId, mfaCode)
.then(() => res.json({ token: generateToken(user) }))
.catch(err => res.status(401).json({ error: 'Invalid MFA code' }));
});2. Apply Step-up Authentication
# Before: Single authentication for all operations
@app.route('/api/transfer', methods=['POST'])
def transfer_funds():
user = get_authenticated_user()
# No additional verification for high-risk operation
return execute_transfer(user, request.json)
# After: Step-up authentication for sensitive operations
@app.route('/api/transfer', methods=['POST'])
def transfer_funds():
user = get_authenticated_user()
# Require additional verification for high-risk operations
if not verify_step_up_auth(user):
return jsonify({
'error': 'Additional verification required',
'step_up_required': True
}), 403
return execute_transfer(user, request.json)
def verify_step_up_auth(user):
# Check for recent MFA, biometric, or security question verification
if user.last_mfa_time > datetime.now() - timedelta(minutes=5):
return True
# Or require fresh MFA code
return verify_mfa_code(user.id, request.headers.get('X-MFA-Code'))3. Implement API Key Rotation and Time-based Tokens
// Before: Static API keys
func authenticateAPIKey(key string) (*User, error) {
user, err := db.GetUserByAPIKey(key)
if err != nil { return nil, err }
return user, nil
}
// After: Time-based, rotating tokens
func authenticateBearerToken(token string) (*User, error) {
claims, err := jwt.ParseWithClaims(token, &jwt.StandardClaims{},
if err != nil || claims.ExpiresAt < time.Now().Unix() {
return nil, errors.New("invalid or expired token")
}
return db.GetUserByID(claims.Subject)
}
// Require re-authentication periodically
func requireReauth(user *User) bool {
return user.last_auth < time.Now().Add(-24 * time.Hour)
}4. Use Adaptive Authentication
@Service
public class AdaptiveAuthService {
public AuthenticationResult authenticate(UserCredentials creds) {
// Basic authentication
User user = userRepository.findByUsername(creds.getUsername());
if (!passwordEncoder.matches(creds.getPassword(), user.getPassword())) {
return AuthenticationResult.failure("Invalid credentials");
}
// Risk assessment
RiskLevel risk = assessRisk(user, creds);
if (risk == RiskLevel.HIGH) {
// High-risk operations require additional factors
if (!verifySecondFactor(user, creds.getSecondFactor())) {
return AuthenticationResult.failure("Additional verification required");
}
} else if (risk == RiskLevel.CRITICAL) {
// Critical operations require step-up authentication
return requireStepUpAuthentication(user);
}
return AuthenticationResult.success(generateToken(user));
}
private RiskLevel assessRisk(User user, UserCredentials creds) {
if (isHighRiskOperation(creds.getEndpoint())) {
return RiskLevel.CRITICAL;
}
return RiskLevel.HIGH;
}
}
}