Best practices for Java web application security in webforJ

Java web app security best practices start with one choice: keep the interface logic on the server. webforJ does exactly that. Your views run in Java, the browser only renders them, and whole classes of attack never get a foothold. What remains is a short list of habits: gate every route, treat input as untrusted, cap uploads and requests, keep secrets out of the repo, and run over HTTPS. This post covers each one for a webforJ app.
What does webforJ protect by default?
Because a webforJ app executes in Java on the server (see Client/Server Interaction), the browser holds no business logic to tamper with. Two protections follow from that design.
- Cross-site scripting (XSS). When you set a component's text, the value shows as literal characters. A string like
<b>hi</b>appears as typed and never runs. Markup renders only when you opt in withsetHtml. - Cross-site request forgery (CSRF). The framework only trusts requests that belong to the current user's session, so a page on another origin can't drive your app for the user.
That covers the attacks that usually need the most framework plumbing. The rest of this post is about the parts that are still yours, which is where most real incidents come from.
How do I secure a Java web app UI?
Start with who can reach each view. A secure Java web app checklist begins with route access, because a hidden button isn't a lock. webforJ route security uses annotations such as @AnonymousAccess, @PermitAll, @RolesAllowed and @DenyAll, and a secure-by-default mode makes an unmarked route require sign-in. The route security overview covers the setup with Spring Security.
Then repeat the rule in the handler. setEnabled(false) and setVisible(false) are interface cues, and a manipulated client can re-enable a control and trigger its action. The server-side code must check permission on its own:
@Route("/admin/orders")
@RolesAllowed("ADMIN")
public class OrdersView extends Composite<Div> {
private final Button delete = new Button("Delete order", ButtonTheme.DANGER);
public OrdersView() {
getBoundComponent().add(delete);
delete.onClick(e -> deleteOrder(42L));
}
private void deleteOrder(long id) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
boolean admin = auth != null && auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
if (!admin) {
Toast.show("Not allowed", Theme.DANGER);
return;
}
// delete the order
}
}
The route annotation keeps non-admins out of the view, and the check in the handler keeps the action safe even if something reaches it another way.
Which OWASP risks still apply?
The OWASP Top 10 is a useful lens for any OWASP Java web UI review. In a webforJ app, four items need your attention.
Injection through JavaScript and SQL
executeJs runs the string you give it as a program. If you concatenate an untrusted value into it, the value becomes code. Send the value as data and run a fixed script that reads it back through the component keyword:
// Shown as literal characters, never as markup
para.setText(name);
// The value travels as data, the script stays fixed
el.setProperty("greetName", name);
el.executeJs("greet(component.greetName)");
SQL works the same way. webforJ sits outside your data layer, so use prepared statements and bound parameters, never string concatenation.
Markup you opt into
The framework doesn't scrub the markup you wrap in <html>. If any part of it comes from a person, a stored record or a query string, clean it first with a maintained library such as jsoup or the OWASP Java HTML Sanitizer and an allowlist of tags.
Broken access control
This is the route and handler pairing from the previous section. Grant the narrowest access that lets people work.