Jakarta EE applications might be vulnerable to serious security risks, but you can protect them by addressing these five common issues:
- Authentication Flaws: Weak passwords, poor session management, and insecure credential storage are common. Use the Jakarta Security API to enforce strong authentication mechanisms like OpenID Connect and secure identity stores.
- Access Control Gaps: Misconfigured roles, unprotected endpoints, and token mismanagement can lead to unauthorized access. Implement Role-Based Access Control (RBAC) using annotations like
@RolesAllowed
and monitor access violations. - Cross-Site Scripting (XSS): XSS attacks exploit unvalidated inputs to inject malicious scripts. Use input validation, output encoding, and Content Security Policies (CSP) to block harmful code.
- Data Storage and Transfer Security: Risks like unencrypted data and hardcoded keys can be mitigated by enforcing HTTPS, using secure storage tools, and applying encryption for sensitive data.
- Injection Attacks: SQL, XML, and LDAP injection attacks exploit unvalidated inputs. Prevent them with parameterized queries, input validation, and the Jakarta Criteria API for safe database access.
Quick Tip: Regularly update GlassFish or your Jakarta EE server or runtime of choice, keep your application dependencies updated, monitor vulnerabilities, and use Jakarta EE’s built-in security features to stay ahead of threats.
These steps ensure your applications remain secure while safeguarding sensitive data and user trust.
1. Fixing Authentication Issues
Common Authentication Weaknesses
Jakarta EE applications often face authentication vulnerabilities due to poor security practices. Some typical issues include:
- Weak password policies that make credentials easy to guess
- Poor session management, which can lead to session hijacking
- Reliance on single-factor authentication, offering minimal protection
- Storing credentials insecurely with outdated hashing methods
- Misconfigured or missing identity stores
Setting Up Strong Authentication
The Jakarta Security API provides built-in authentication features that work seamlessly with Jakarta CDI, eliminating the need for vendor-specific configurations [1].
Here’s how to set up authentication:
- Define Security Constraints. Use the following XML snippet to protect specific HTTP resources:
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/secure/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>ADMIN</role-name>
</auth-constraint>
</security-constraint>
- Choose an Authentication Mechanism. Options include:
- Basic Authentication
- Form-based Authentication
- Custom Form Authentication
- OpenID Connect (OIDC)
- Set Up an Identity Store. Configure an identity store to validate user credentials against a database or LDAP directory.
Example: Jakarta Security Implementation
Here’s an example of how to implement authentication using Jakarta Security. This example defines getting user’s credentials using a HTML form and validating them using hashed passwords and roles stored in the database:
@DatabaseIdentityStoreDefinition(
dataSourceLookup = "jdbc/UserDS",
callerQuery = "SELECT password FROM users WHERE username = ?",
groupsQuery = "SELECT role FROM user_roles WHERE username = ?"
)
@FormAuthenticationMechanismDefinition(
loginToContinue = @LoginToContinue(
loginPage = "/login.html",
errorPage = "/error.html"
)
)
@ApplicationScoped
public class SecurityConfig {
@Inject
private SecurityContext securityContext;
public boolean isUserInRole(String role) {
return securityContext.isCallerInRole(role);
}
}
This setup provides several advantages:
- Passwords are stored securely with modern hashing techniques (See
DatabaseIdentityStoreDefinition
.hashAlgorithm) - Role-based access is enforced with fine-grained control
- Sessions are managed securely, including timeouts
- Authentication attempts are logged for auditing purposes (See auditing configuration in GlassFish, where the default audit module, Audit, logs all successful or failed authentication attempts if enabled)
If multiple identity stores are configured, the Jakarta Security API prioritizes them automatically, offering flexible yet secure authentication options [1].

Eclipse GlassFish is a Fast, Updated and Secure OpenSource Jakarta EE Server supported by OmniFish.
Find out more about its progress and current state in 2024 in this article: GlassFish is rolling forward. What’s New?
Next, we’ll discuss how to address access control issues to further enhance the security of Jakarta EE applications.
2. Fixing Access Control Gaps
Access Control Weaknesses
Jakarta EE applications often face issues with access control [2]. Some common problems include:
- Improper Role Management: Roles are misconfigured, granting more permissions than necessary.
- Unprotected Endpoints: REST endpoints lack adequate security measures.
- Insufficient Access Verification: Applications fail to confirm if users have ownership of the resources they’re accessing.
- Missing Rate Limiting: Automated attacks aren’t mitigated due to the absence of rate limits.
- Token Mismanagement: Tokens, such as JWTs, aren’t invalidated after logout.
For instance, an attacker can exploit a poorly verified SQL parameter to access restricted data:
// Vulnerable code
pstmt.setString(1, request.getParameter("acct"));
ResultSet results = pstmt.executeQuery();
By altering the URL parameter like this: http://example.com/app/accountInfo?acct=notmyacct
, the attacker can gain unauthorized access [2].
To address such vulnerabilities, you can leverage Jakarta EE’s built-in access control features.
Setting Up Access Controls
Strong access control, when paired with robust authentication, is key to securing a Jakarta EE application. Jakarta EE provides both declarative and programmatic Role-Based Access Control (RBAC) [3].
Here’s an example of declarative access control:
@DeclareRoles({"ADMIN", "USER"})
@WebServlet("/secure/data")
public class SecureServlet extends HttpServlet {
@Inject
private SecurityContext securityContext;
@RolesAllowed("ADMIN")
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// Accessible to ADMINs only
if (securityContext.isCallerInRole("ADMIN")) {
// Process admin request
}
}
}
Access Control Setup Steps
To secure your application, follow these steps:
- Define Security Constraints
Use XML to define constraints for specific resources:
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin Resources</web-resource-name>
<url-pattern>/admin/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>ADMIN</role-name>
</auth-constraint>
</security-constraint>
- Implement Role-Based Security
Control access to methods using annotations:
@Stateless
public class DataService {
@RolesAllowed("USER")
public List<Data> getUserData() {
// Accessible to USERs only
return dataRepository.findUserData();
}
@DenyAll
public void restrictedOperation() {
// No access permitted
}
}
- Configure Access Control Monitoring
Monitor and log access violations with a filter:
@WebFilter("/*")
public class SecurityAuditFilter implements Filter {
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) {
try {
chain.doFilter(request, response);
} catch (AccessControlException e) {
logger.warning("Access control violation: " + e.getMessage());
// Notify administrators
}
}
}
These steps help ensure your application is protected against unauthorized access while maintaining proper role-based permissions.
3. Stopping XSS Attacks
How XSS Affects Jakarta EE
XSS poses a serious threat to all web applications by embedding harmful client-side code into web pages. This makes having strong defense mechanisms a must.
Here are the main types of XSS attacks that can impact Jakarta EE applications:
Attack Type | Target | Impact |
---|---|---|
Stored XSS | Server-side storage | Malicious code gets saved on the server (e.g., in a database) and affects all users accessing the infected content. |
Reflected XSS | Server response | Harmful code is instantly reflected off the server when a user clicks on a manipulated link. |
DOM-based XSS | Client browser | Exploits client-side JavaScript to alter the DOM without involving the server. |
Mutated XSS | Browser parsing | Code initially appearing safe is modified during browser parsing, making it dangerous. |
XSS Protection Methods
Defending against XSS requires a multi-layered approach, starting with strict input validation and using output encoding tailored to the context [4].
Some key strategies include:
- Enforcing server-side input validation
- Implementing context-aware output encoding
- Enabling a Content Security Policy (CSP)
- Using secure template engines that escape output automatically
- Leveraging established validation frameworks
The OWASP ESAPI framework provides a range of tools for security, while Jakarta Validation (formerly JSR 303) simplifies server-side input validation with annotations [4].
“Whitelisting is the preferred and recommended approach because it is impossible to know all the dangerous input values as newer attack vectors are developed” [4]
Here’s an example of these techniques in action.
Input Validation Example
Below is a practical example of protecting against XSS in a Jakarta EE application:
@WebServlet("/secure/comment")
public class SecureCommentServlet extends HttpServlet {
@Inject
private ValidationService validator;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException {
String userInput = request.getParameter("comment");
// Step 1: Input Validation, e.g. using Jakarta Validation annotations
if (!validator.isValidInput("Comment", userInput, "SafeText", 1000, false)) {
response.sendError(400, "Invalid input detected");
return;
}
// Step 2: Canonicalization using OWASP ESAPI component
String canonicalInput = ESAPI.encoder().canonicalize(userInput);
// Step 3: Output Encoding using OWASP ESAPI component
String safeOutput = ESAPI.encoder().encodeForHTML(canonicalInput);
response.getWriter().write(safeOutput);
}
}
When working with template engines like Thymeleaf or Jakarta Faces (formerly JSF), always use secure attributes that escape output automatically.
Example with Jakarta Faces (JSF):
<!-- Safe: Escapes output automatically -->
<h:outputText value="#{userComment}"/>
<!-- Unsafe: Allows unescaped HTML -->
<span>${userComment}</span>
<!-- Unsafe: Allows unescaped HTML -->
<h:outputText value="#{userComment}" escape="false" />
Example with Thymeleaf:
<!-- Safe: Escapes output automatically -->
<div th:text="${userComment}"></div>
<!-- Unsafe: Allows unescaped HTML -->
<div th:utext="${userComment}"></div>
To further enhance security, include the following headers in your Jakarta EE application, for example using a servlet filter:
response.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'");
response.setHeader("X-XSS-Protection", "1; mode=block");
response.setHeader("X-Content-Type-Options", "nosniff");
4. Securing Data Storage and Transfer
Data Security Risks
Jakarta EE applications face several vulnerabilities when it comes to storing and transferring data. Here are some common risks to be aware of:
Risk Type | Impact | Common Scenarios |
---|---|---|
Unencrypted Data Transit | Data interception | Using plain HTTP connections for sensitive data |
Insecure Storage | Data breach | Storing passwords or keys in plaintext files |
Weak Transport Layer | Man-in-the-middle attacks | Misconfigured or missing SSL/TLS |
Key Management Issues | Unauthorized access | Hardcoding encryption keys in application code |
Addressing these risks is crucial to ensure the safety of your application and user data.
Data Protection Methods
To secure data storage and transfer, focus on encryption and secure protocols:
- Enforce HTTPS: Use the transport guarantee in your deployment descriptor to ensure data confidentiality [5].
- Secrets Management: Utilize tools like Java KeyStore, HashiCorp Vault, or specialized MicroProfile Config Sources like SmallRye KeyStore Config Source, to manage encrypted secrets across different storage locations.
- Add Security Headers: Use filters to strengthen HTTP responses. For example:
@WebFilter("/*")
public class SecurityHeadersFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
chain.doFilter(request, response);
}
}
Using GlassFish server can further enhance security with its built-in security features and regular updates. Combined with proactive server maintenance and expert support from OmniFish, these practices help protect sensitive data across your Jakarta EE applications.
5. Preventing Injection Attacks
Types of Injection Attacks
Injection attacks are a major threat to Jakarta EE applications, exploiting non-validated user inputs to manipulate systems. Here’s an overview of the most common types:
Attack Type | Description | Impact |
---|---|---|
SQL Injection | Alters database queries with malicious SQL statements | Data theft, corruption, or deletion |
Command Injection | Runs unauthorized OS commands | System compromise |
XML Injection | Modifies XML processing | Data exposure, service disruption |
LDAP Injection | Abuses dynamic LDAP queries | Directory service compromise |
NoSQL Injection | Targets non-relational databases | Unauthorized data access |
For instance, during the Accellion FTA breach, attackers combined SQL injection with command execution to access encryption keys, compromise the system, and install a web shell [6].
Injection Prevention Steps
Protecting your application from these attacks requires careful implementation of key practices:
- Use Parameterized Queries: Always rely on parameterized queries with
PreparedStatement
to bind inputs securely. Here’s an example:
// INCORRECT - vulnerable to injection
String query = "SELECT * FROM users WHERE username='" + username + "'";
// CORRECT - using parameterized query
String query = "SELECT * FROM users WHERE username = ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, username);
- Validate Inputs: Enforce strict input validation to ensure only expected data is processed. For example:
@Entity
public class UserInput {
@Pattern(regexp = "^[a-zA-Z0-9_]{3,20}$")
private String username;
@Size(min = 8, max = 50)
private String comment;
}
- Leverage Framework Security Features: Use built-in protections like the Jakarta Security API to control access and enforce security policies.
@Inject
private SecurityContext securityContext;
@GET
@Path("/secure")
public Response secureEndpoint() {
if (!securityContext.isUserInRole("admin")) {
return Response.status(Status.FORBIDDEN).build();
}
// Process secure operation
}
Safe Database Query Example
Here’s how you can securely construct dynamic queries using the JPA Criteria API:
@Stateless
public class SecureDataAccess {
@PersistenceContext
private EntityManager em;
public List<User> findUsersByDepartment(String department) {
// Using Criteria API for dynamic queries
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
// Safe parameter handling
Predicate departmentPredicate = cb.equal(
user.get("department"),
cb.parameter(String.class, "dept")
);
query.where(departmentPredicate);
TypedQuery<User> typedQuery = em.createQuery(query);
typedQuery.setParameter("dept", department);
return typedQuery.getResultList();
}
}
“SQL Injection happens when a rogue attacker can manipulate the query building process so that he can execute a different SQL statement than what the application developer has originally intended.” – vladmihalcea [7]
Conclusion
Summary of Solutions
Ensuring strong Jakarta EE security requires a layered approach. The table below highlights key security measures and the tools to implement them effectively:
Security Measure | Example Implementation Tool | Key Advantage |
---|---|---|
Authentication | Eclipse Epicyro (available in GlassFish) | Provides standardized authentication |
Authorization | Eclipse Exousia (available in GlassFish) | Enables detailed access control |
Dependency Management | Azul Intelligence Cloud, Snyk Open Source | Delivers automated security updates and alerts about vulnerable dependencies |
Input Validation | Jakarta Security API (available in GlassFish) | Protects against XSS and injection risks |
Vulnerability Detection | Contrast ADR | Helps detect vulnerabilities early and protect from them |
Applying these measures consistently is essential, as past breaches have shown the risks of neglecting security. Regular reviews further strengthen your system’s defenses.
Regular Security Checks
Security measures are only effective when maintained and updated. Here are key practices to ensure ongoing protection:
- Dependency Management: Regularly review dependencies to eliminate unnecessary ones and update critical libraries and frameworks. This minimizes vulnerabilities and simplifies maintenance [8].
- Security Testing: Use automated tools to scan for vulnerabilities in known databases, such as Contrast AST. This proactive approach helps identify and address risks before they are exploited.
- Framework Integration: Take advantage of Jakarta Security‘s built-in features, which work seamlessly with Jakarta CDI. This ensures a consistent and reliable security setup across your application [9]. On top of that, check what other security features is offered by your app server, for example GlassFish.
- Continuous Monitoring: Adopt a layered monitoring approach to quickly identify and address vulnerabilities through frequent assessments and updates [10].