Exposure of Private Personal Information to an Unauthorized Actor
Description
Exposure of Private Personal Information to an Unauthorized Actor occurs when a product does not properly prevent a person's private, personal information from being accessed by actors who either are not explicitly authorized to access the information or do not have the implicit consent of the person about whom the information is collected. Private personal information (PPI) includes passwords, phone numbers, geographic locations, personal messages, credit card numbers, social security numbers, medical records, and other data that individuals have a reasonable expectation of privacy about. This differs from general information exposure in that it specifically involves personal, identifiable information about individuals.
Risk
PPI exposure has severe consequences for affected individuals and organizations. CVE-2025-41685 in SMA's Sunny Portal allowed attackers to obtain usernames by submitting email addresses. CVE-2025-62362 in the Dutch government's citizen portal exposed employee names and emails through browser developer tools, enabling targeted phishing campaigns. CVE-2025-13008 in M-Files Server allowed session token capture of other users (CVSS 8.6). These exposures violate GDPR, HIPAA, and other privacy regulations, leading to significant fines. Exposed personal data enables identity theft, fraud, social engineering, and harassment of affected individuals.
Solution
Implement strict access controls on all personal data. Apply the principle of least privilege—only expose data necessary for the current operation. Validate user authorization before returning any personal information. Remove sensitive data from API responses, logs, and error messages. Implement data minimization—don't collect or store more personal data than necessary. Use proper session isolation to prevent cross-user data access. Conduct privacy impact assessments. Implement audit logging for access to personal data. Apply data masking for display purposes.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Privacy Violation Personal information is exposed to unauthorized parties, violating individual privacy. |
| Compliance | Scope: Regulatory Penalties GDPR, HIPAA, CCPA and other regulations mandate protection of personal data with significant penalties for violations. |
| Reputation | Scope: Trust Damage Organizations lose user trust when personal data is exposed, leading to user attrition and brand damage. |
Example Code + Solution Code
Vulnerable Code
# VULNERABLE: Exposing user data without authorization check
@app.route('/api/user/<user_id>')
def get_user(user_id):
user = User.query.get(user_id)
# Returns ALL user data to ANY requester!
return jsonify({
'id': user.id,
'email': user.email,
'phone': user.phone_number,
'ssn': user.social_security_number, # Extremely sensitive!
'address': user.home_address,
'credit_card': user.credit_card_number
})
# VULNERABLE: User enumeration through error messages
@app.route('/forgot-password', methods=['POST'])
def forgot_password():
email = request.form['email']
user = User.query.filter_by(email=email).first()
if user:
send_reset_email(user)
return "Reset email sent"
else:
return "Email not found" # Reveals if email exists!
// VULNERABLE: Logging personal information
import org.slf4j.Logger;
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public void processUserRegistration(User user) {
// Logging PII!
logger.info("Registering user: " + user.getEmail() +
", SSN: " + user.getSsn() +
", DOB: " + user.getDateOfBirth());
}
// VULNERABLE: Returning other users' data
public List<User> searchUsers(String query) {
// Returns full user objects including PII to any authenticated user
return userRepository.findByNameContaining(query);
}
}
// VULNERABLE: Client-side exposure of sensitive data
app.get('/api/search/users', (req, res) => {
const users = db.searchUsers(req.query.q);
// Sending full user objects to client
res.json(users.map(u => ({
id: u.id,
name: u.name,
email: u.email, // PII exposed
phone: u.phone, // PII exposed
ssn: u.ssn, // Extremely sensitive!
salary: u.salary // Private information
})));
});
// VULNERABLE: PII in URL parameters
<a href="/profile?ssn=${user.ssn}&email=${user.email}">
View Profile
</a>
Fixed Code
# SAFE: Authorization check and data minimization
from functools import wraps
def require_owner_or_admin(f):
@wraps(f)
def decorated(user_id, *args, **kwargs):
current_user = get_current_user()
if current_user.id != user_id and not current_user.is_admin:
abort(403)
return f(user_id, *args, **kwargs)
return decorated
@app.route('/api/user/<user_id>')
@require_owner_or_admin
def get_user_safe(user_id):
user = User.query.get(user_id)
current_user = get_current_user()
# Return different data based on authorization level
if current_user.id == user_id:
# User viewing own profile - full access
return jsonify({
'id': user.id,
'email': user.email,
'phone': mask_phone(user.phone_number), # Mask sensitive data
'address': user.home_address
# Never return SSN, credit card in API responses
})
else:
# Others see limited public info
return jsonify({
'id': user.id,
'name': user.display_name
})
# SAFE: Prevent user enumeration
@app.route('/forgot-password', methods=['POST'])
def forgot_password_safe():
email = request.form['email']
user = User.query.filter_by(email=email).first()
if user:
send_reset_email(user)
# Same message regardless of whether user exists
return "If an account exists with this email, a reset link has been sent"
def mask_phone(phone):
if not phone:
return None
return '***-***-' + phone[-4:] # Only show last 4 digits
// SAFE: No PII in logs, data access controls
import org.slf4j.Logger;
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public void processUserRegistration(User user) {
// Log only non-sensitive identifiers
logger.info("Registering user with ID: {}", user.getId());
}
// SAFE: Return DTOs with limited data
public List<UserSearchResultDTO> searchUsers(String query, User requestingUser) {
List<User> users = userRepository.findByNameContaining(query);
return users.stream()
.map(user -> new UserSearchResultDTO(
user.getId(),
user.getDisplayName(),
user.getPublicBio()
// No email, phone, SSN, or other PPI
))
.collect(Collectors.toList());
}
// SAFE: Audit logging for PII access
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public UserPrivateDTO getUserPrivateInfo(Long userId) {
auditLogger.logPIIAccess(getCurrentUser(), userId);
User user = userRepository.findById(userId).orElseThrow();
return new UserPrivateDTO(user);
}
}
// SAFE: Data minimization and access control
app.get('/api/search/users', authenticateUser, (req, res) => {
const users = db.searchUsers(req.query.q);
// Return only public information
res.json(users.map(u => ({
id: u.id,
name: u.name,
avatar: u.avatarUrl
// No email, phone, SSN, salary
})));
});
// SAFE: POST requests for sensitive operations, no PII in URLs
app.post('/api/profile/view', authenticateUser, (req, res) => {
const { userId } = req.body; // Not in URL
// Check authorization
if (req.user.id !== userId && !req.user.isAdmin) {
return res.status(403).json({ error: 'Unauthorized' });
}
// Audit log
auditLog.record({
action: 'VIEW_PROFILE',
actor: req.user.id,
target: userId,
timestamp: new Date()
});
const profile = db.getUserProfile(userId);
res.json(sanitizeForClient(profile));
});
function sanitizeForClient(profile) {
// Remove all fields that shouldn't go to client
const { ssn, creditCard, internalNotes, ...safe } = profile;
return {
...safe,
phone: maskPhone(safe.phone),
email: maskEmail(safe.email)
};
}
Exploited in the Wild
SMA Sunny Portal Username Exposure (SMA, 2025)
CVE-2025-41685 in SMA's ennexos.sunnyportal.com allowed attackers to obtain usernames by submitting email addresses, enabling targeted attacks on solar energy system users.
Dutch Government Citizen Portal (GPP, 2025)
CVE-2025-62362 exposed employee names and email addresses in network responses visible through browser developer tools, enabling targeted phishing and social engineering campaigns against government employees.
M-Files Server Session Token Capture (M-Files, 2025)
CVE-2025-13008 allowed authenticated attackers to capture session tokens of other active users through the M-Files Web interface, rated CVSS 8.6 high severity, potentially exposing confidential documents.
Tools to test/exploit
-
Burp Suite — analyze API responses for PII exposure.
-
OWASP ZAP — automated scanning for information disclosure.
-
PII Detection Tools — identify PII in data.
CVE Examples
-
CVE-2025-41685 — SMA Sunny Portal username exposure.
-
CVE-2025-62362 — Dutch government portal employee data exposure.
-
CVE-2025-13008 — M-Files Server session token capture.
References
-
MITRE. "CWE-359: Exposure of Private Personal Information to an Unauthorized Actor." https://cwe.mitre.org/data/definitions/359.html
-
OWASP. "OWASP Top 10 Privacy Risks." https://owasp.org/www-project-top-10-privacy-risks/