-
Notifications
You must be signed in to change notification settings - Fork 134
SE 12 Security Principles
Part of the Software Engineering Principles series
Software that handles personal health information, financial transactions, or clinical decisions carries significant responsibilities. A security failure is not just a technical problem — it can result in:
- Exposure of confidential patient data
- Financial fraud through billing manipulation
- Regulatory penalties (GDPR, HIPAA, national data protection laws)
- Loss of patient and public trust
Security is not a feature added at the end. It is a quality attribute designed in from the beginning.
The Open Web Application Security Project (OWASP) publishes the most widely referenced list of web application security risks. Understanding these is the foundation of secure web development.
The most common and impactful vulnerability. Users access functionality or data they should not be permitted to see.
Examples:
- A patient can view another patient's records by changing the URL ID parameter
- A cashier can access the admin configuration panel
- A cancelled bill is still queryable via the API after cancellation
Prevention:
- Enforce authorisation checks server-side on every request — never rely on UI hiding alone
- Use role-based or attribute-based access control
- Deny by default: only permit what is explicitly allowed
Sensitive data is transmitted or stored without adequate protection.
Examples:
- Passwords stored as plain text or with weak hashing (MD5, SHA-1)
- Patient data transmitted over HTTP instead of HTTPS
- Encryption keys stored in source code
Prevention:
- Use TLS for all data in transit
- Hash passwords with bcrypt, scrypt, or Argon2 (never MD5 or SHA-1 alone)
- Store secrets in environment variables or secret management systems, never in source code
- Encrypt sensitive data at rest
Untrusted data is sent to an interpreter (SQL, OS command, LDAP) as part of a command, causing unintended execution.
SQL Injection example:
// Vulnerable — user input inserted directly into query string
String query = "SELECT * FROM patient WHERE name = '" + userInput + "'";
// If userInput = "' OR '1'='1", returns ALL patients
// Safe — parameterised query
TypedQuery<Patient> q = em.createQuery(
"SELECT p FROM Patient p WHERE p.name = :name", Patient.class);
q.setParameter("name", userInput);Prevention:
- Always use parameterised queries or prepared statements — never string concatenation
- Use JPQL and JPA, which parameterise by default
- Validate and sanitise all input at system boundaries
Security flaws built into the architecture — not bugs in the code, but gaps in the design itself.
Examples:
- No rate limiting on login attempts (enables brute-force attacks)
- Password reset that relies on security questions (easily guessed)
- Sensitive data emailed in plain text
Prevention:
- Threat model during design: ask "what could go wrong?" systematically
- Apply security design patterns from the beginning
- Use established authentication libraries rather than building your own
Insecure default configurations, unnecessary features enabled, or error messages that reveal internal details.
Examples:
- Stack traces exposed to end users (reveals technology stack and internal paths)
- Default admin credentials not changed
- Debug mode enabled in production
- Directory listing enabled on web server
Prevention:
- Disable all unnecessary features, ports, and services
- Return generic error messages to users; log detail internally
- Keep software updated and patched
- Review configuration before deployment to each environment
Using libraries or frameworks with known security vulnerabilities.
Prevention:
- Keep dependencies updated
- Use tools like OWASP Dependency Check to scan for known vulnerabilities in project dependencies
- Subscribe to security advisories for major dependencies
- Remove unused dependencies
Weaknesses in how users are identified and sessions are managed.
Examples:
- Allowing weak passwords ("123456", "password")
- Sessions that never expire
- Session tokens exposed in URLs
- Not invalidating sessions on logout
Prevention:
- Enforce password complexity and length requirements
- Set session expiry appropriate to the risk level
- Use secure, HttpOnly cookies for session tokens
- Invalidate server-side sessions on logout
- Implement multi-factor authentication for sensitive operations
Code or data that is trusted without verification of its integrity.
Examples:
- Application updates pulled from untrusted sources without signature verification
- Deserialising untrusted data without validation
Prevention:
- Use digital signatures to verify software integrity
- Never deserialise data from untrusted sources
- Verify integrity of data at system boundaries
Insufficient logging means breaches go undetected or undetectable.
What should be logged:
- All authentication events (success and failure)
- Access to sensitive resources (patient records, billing data)
- Data modifications (creates, updates, deletes on clinical data)
- Administrative actions
- All failed access control checks
Log format should capture:
- Timestamp
- User identity
- Action performed
- Resource affected
- IP address
- Success or failure
// Example audit log entry
auditService.log(AuditEvent.builder()
.timestamp(LocalDateTime.now())
.userId(currentUser.getId())
.action("BILL_FINALIZED")
.resourceId(bill.getId())
.ipAddress(request.getRemoteAddr())
.build());The server is manipulated into making requests to unintended destinations — including internal systems not exposed to the internet.
Prevention:
- Validate and sanitise all URLs provided by users before making requests
- Block requests to internal network ranges
- Do not expose raw server responses to users
All input from outside the system boundary must be validated before use.
@POST
@Path("/patients")
public Response createPatient(PatientRequest request) {
// Validate before any processing
if (request.getName() == null || request.getName().isBlank()) {
return Response.status(400).entity("Name is required").build();
}
if (request.getDateOfBirth() != null
&& request.getDateOfBirth().isAfter(LocalDate.now())) {
return Response.status(400).entity("Date of birth cannot be in the future").build();
}
// Now safe to process
}Every user, process, and service should have only the minimum access necessary to perform its function.
- A reporting user should not have write access to clinical data
- A background job should not run as the database administrator
- A service that reads from one table should not have access to all tables
XSS occurs when an attacker injects malicious scripts into content that is displayed to other users.
Prevention in JSF:
- JSF escapes output by default in
h:outputText— do not useescape="false"unless you have a specific, controlled need - Never render user input as raw HTML without sanitisation
- Use Content Security Policy headers
- Never log sensitive data: patient names, billing amounts, clinical details, passwords, or tokens must not appear in application logs
- Never store credentials in source code: API keys, database passwords, and connection strings belong in environment configuration, not in committed files
- Mask data in UIs: national ID numbers and financial account numbers should be partially masked in display
Previous: SE-11: API Design Principles
Next: SE-13: Database Design Principles