Configure Javalin (Jetty) max request size (414 URI too long) - jetty

How do you configure Javalin to change the max request size, specifically the config to increase the max size of the query parameters on the request (avoiding 414 URI too long)?
I get 414 URI is too long when I exceed what looks like a default size of 8KB so would like to configure my Javalin server to increase that slightly.
I think it uses Jetty under the hood which has a HttpConfiguration.requestHeaderSize variable that may control it. Or there's a HttpParser._maxHeaderBytes which is checked before throwing the URI_TOO_LONG_414 exception.
I can't see how it can all be wired up...

Bearing in mind all the advice and warnings listed here... Assuming you have Javalin defined as follows in your main method:
Javalin app = Javalin.create(config -> {
config.jetty.server(MyJetty::create);
}).start();
Then you can create MyJetty to customize this and any other settings you may want to use.
A very basic example:
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
public class MyJetty {
public static Server create() {
Server server = new Server();
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.setRequestHeaderSize(8192); // use your value here
HttpConnectionFactory httpCF = new HttpConnectionFactory(httpConfiguration);
ServerConnector httpConnector = new ServerConnector(server, httpCF);
httpConnector.setPort(8080); // use your port here
server.addConnector(httpConnector);
return server;
}
}
This only sets up a simple insecure HTTP connection - but shows one way to change the HttpConfiguration value for Javalin. You can use the same approach for other connectors you may want to configure, including ones using SSL/TLS.
I am assuming the latest version of Javalin (version 5) since there were some syntax changes from Javalin 4 to 5 - and also the version of Jetty changed from 9 to 11.
If you are using Javalin 4, the config syntax is a bit different:
config.server(MyJetty::create);
But I don't think the Jetty code changes (for this specific setting, at least).

Only change the HttpConfiguration, it will inform the HttpParser.
You should be leery of doing this because ...
many browsers do not support that large of a query.
3rd party internet security software on laptops will reject that exchange.
you open yourself to various old school DoS (Denial of Service) attacks related to hashmap/hashcode abuse. (to minimize this issue, use Java 17 or newer)
If you move to HTTP/2 (or HTTP/3) many servers will reject the extension of the maximum request headers (at the HTTP level) that would be needed to support this massive request path.
Also, depending on what technology Javalin is using, you might need to also increase the ContextHandler maxFormContentSize and/or maxFormKeys.
If you have reached this need, it screams of abuse of the HTTP spec, you should investigate moving to a traditional HTTP POST with Content-Type: application/x-www-form-urlencoded instead.

Related

400 Bad Request Request Header Or Cookie Too Large using Sustainsys.Saml2

I'm getting a browser error when using SustainSys.Saml2 library with my app:
400 Bad Request
Request Header Or Cookie Too Large
nginx/1.14.0
I think that reducing my cookie size might help and I only really need the email from the claim data, so I thought that if I could just save the email claim and remove the other claims, that it might reduce my cookie size and fix this error.
I read the response to a similar question (SustainSys.Saml2 Request length header too long) and looked for some information on how to implement AcsCommandResultCreated to remove unused claims (and hopefully reduce cookie size). I didn't find a lot of documentation, but did piece together some ideas and code to try and take a stab at it.
I've tried this code in my global.asax as well as in a controller action (that I made the "returnUrl" after Saml2/Acs). It doesn't look like my FedAuth cookie (set by Saml2/Acs) is any smaller. Any comments or suggestions? Thank you.
// Check if email claim exists
var principal = ClaimsPrincipal.Current;
var userEmail = principal.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.Email)?.Value;
// Create new command result that only contains the email claim
if (userEmail != null)
{
var emailClaim = principal.Claims.FirstOrDefault(claim => claim.Type == ClaimTypes.Email);
Sustainsys.Saml2.Configuration.Options.FromConfiguration.Notifications.AcsCommandResultCreated =
(commandResult, response) =>
{
var newCommandResult = new Sustainsys.Saml2.WebSso.CommandResult();
newCommandResult.Principal.Claims.Append(emailClaim);
commandResult = newCommandResult;
};
}
UPDATE:
It turned out that the test environment that I was using (which used nginx) needed to increase the request header buffer size. Adding these cookies increased the size to around 9500 bytes and nginx by default has a request header buffer size that is lower than that (I think 8000). Contacting the code owners of the test server running nginx, and increasing this solved my problem, without me having to reduce my cookie size.
Do you have a lot of failed authentication attempts? That can leave a lot of Saml2.XYZ correlation cookies around on the domain. Try checking the browser dev tools and clean those up.
The "headers too large" is usually something that happens when a user has tried signing in several times with a failure and those cookies get stuck. The real issue is usually something else - causing the authentication to fail and those correlation cookies to be accumulating.

Static content in my C:\ drive being aliased away by jetty ResourceHandler

I am using emmedded jetty in my java project. For some reason, the path I am sending into resourceHandler is c:\ (lower case) and it is being aliased to C:\ (upper case). Because of this my static content is not being served.
I read some docs that indicated that jetty compares the absolute path and canonical path to detect aliases. In the log I see:
[qtp15485575-19] INFO org.eclipse.jetty.server.handler.ResourceHandler - file:/c:/filepath aliased to file:/C:/filepath
Anyone have any ideas on how to resolve?
Update: logged bug to eclipse for this: https://bugs.eclipse.org/bugs/show_bug.cgi?id=471526
Here's their response:
"it is indeed very annoying, but is forced on us by the poor security model of the servlet spec.
IF the spec had said that all URIs were denied unless explicitly allowed, we would not need to check for aliases. But instead it has a model where it allows all URIs except those that are specifically denied.
Thus if a security constraint is put on /secretfile.txt we have to make sure that any aliases of that file are also constrained... and do so in an FS independent way. That means on various operating systems we might need to block:
/sEcRetFile.TXT
/secretfile.txt
/SECRE~01.TXT
/secretfile.txt##0
etc. etc. etc.
So to be sure, we have implemented the alias system.
Normally we don't get problems with c: and C: because that should be normalized when configuring the context, so the correct one should be used. But filesystems do change their behaviour between releases, so it can be very annoying.
I think this is handled somewhat better in jetty 9.3 where we can use the Path classes to better examine parts of the path."
So probably the best bet is to use Jetty 9 if you can and use Joakin's fix if it is still an issue.
Give your ResourceHandler a full, absolute, and real path.
package jetty.resource;
import java.io.File;
import java.nio.file.Path;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.util.resource.PathResource;
public class ResourceHandlerFromFSExample
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
Path webRootPath = new File("src/test/webroot").toPath().toRealPath();
System.err.println("WebRoot is " + webRootPath);
ResourceHandler handler = new ResourceHandler();
handler.setBaseResource(new PathResource(webRootPath));
handler.setDirectoriesListed(true);
server.setHandler(handler);
server.start();
server.join();
}
}
And btw, DefaultServlet is still a better choice for static file serving.

Peoplecode - how to create cookies?

We are trying to create a cookie in the PeopleSoft Peoplecode by using the %Response object.
However, the code we tried is failing.
&YourCookie = %Response.AddCookie("YourCookieName", "LR");
Another snippet we tried to create the cookie
Local object &Response = %Response;
Local object &YourCookie;
&YourCookie = &Response.CreateCookie("YourCookieName");
&YourCookie.Domain = %Request.AuthTokenDomain;
&YourCookie.MaxAge = -1; /* Makes this a session cookie (default) */
&YourCookie.Path = "/";
&YourCookie.Secure = True; /* Set to true if using https (will still work with http) */
&YourCookie.Value = "Set the cookie value here. Encrypt sensitive information.";
The document reference points to IScript functions called CreateCookie methods etc.
http://docs.oracle.com/cd/E15645_01/pt850pbr0/eng/psbooks/tpcr/chapter.htm?File=tpcr/htm/tpcr21.htm
However, these don't work in Peoplecode. We don't have the knowledge to create IScript or use it. Any insight with the People code API for cookies or IScript is much appreciated.
I just tested on PeopleTools 8.54.11 and was able to create a cookie using the snippet you provided above.
I did find I had an issue if I set
&YourCookie.Secure = True;
in an environment where I was using HTTP.
If you set Secure to False the cookie will be available in both HTTP and HTTPS
if you set Secure to True the cookie is only available in HTTPS
PeopleTools 8.54 Documentation showing the CreateCookie method
I have been trying to do this (same code snippet) from within signon peoplecode, tools release is 8.54.09. I can execute the first two lines of code, but as soon as the line of code executing the CreateCookie() method executes, I get tossed out / end up on the signon error page.
This seems to support the previous answer saying that the API has removed the method, but the answer before that says it has been successful on tools 8.54.11 -- does that mean they removed it, then put it back, and I happen to be stuck with a release where it was removed? :-/

Redirecting root context path or binding it to a servlet or mapping it with a welcome-file

I am using Jetty-9 in embedded mode and need only one web application. Consequently I would like the root URL to go to the homepage of that application, i.e. something like
http://localhost:4444/
should end up in a servlet. I start out with:
ServletContextHandler scContext =
new ServletContextHandler(ServletContextHandler.SESSIONS);
scContext.setContextPath("/");
None of the following worked, neither
scContext.addServlet(ListsServlet.class, "/");
nor
scContext.setWelcomeFiles(new String[]{"/lists})
where /lists is mapped to the ListsServlet servlet. All I get is a 403 (Forbidden).
I do not use the DefaultServlet, which seems to handle welcome files. But since the ServletContextHandler has setWelcomeFiles I expected it to contain the logic to use them.
Any ideas?
For the 403 Forbidden error, you have some security setup that is not allowing you to access the handlers/servlets.
Eliminate that security (for now), verify that the rest is working, then add security a bit later to lock down specifics.
If you want to see some the suggestions below at work, consider looking at the code example in the answer from another stackoverflow: How to correctly support html5 <video> sources with jetty.
Welcome files are appended to the incoming request path if there is nothing present at that location. For example requesting a directory and then a welcome-file of 'index.html' is appended to the request path.
While this would work ...
scContext.setWelcomeFiles(new String[]{"lists"})
// Add Default Servlet (must be named "default")
ServletHolder holderDefault = new ServletHolder("default",DefaultServlet.class);
holderDefault.setInitParameter("resourceBase",baseDir.getAbsolutePath());
holderDefault.setInitParameter("dirAllowed","true");
holderDefault.setInitParameter("welcomeServlets","true");
holderDefault.setInitParameter("redirectWelcome","true");
scContext.addServlet(holderDefault,"/");
It's likely not what you are aiming for, as you said the root path only.
The above would also make changes to requests like /foo/ to /foo/lists
Instead, it might make more sense to use a Rewrite rule + handler instead of the welcome-files approach.
RewriteHandler rewrite = new RewriteHandler();
rewrite.setHandler(scContext);
RewritePatternRule rootRule = new RewritePatternRule();
rootRule.setPattern("/");
rootRule.setReplacement("/list");
rootRule.setTerminating(true);
rewrite.addRule(rootRule);
server.setHandler(rewrite);
This RewritePatternRule simply changes any request path / to /list and then forwards that request to the wrapped ssContext (if you want to see the /list on the browser, change it to a RedirectPatternRule instead.

Apply HTTP basic authentication to jax ws (HttpSpiContextHandler) in embedded Jetty

There are some similar questions for earlier versions of Jetty (pre 9) but none that address this specific problem :
Server server = new Server();
System.setProperty("com.sun.net.httpserver.HttpServerProvider",
JettyHttpServerProvider.class.getName());
JettyHttpServer jettyServer = new JettyHttpServer(server, true);
Endpoint endpoint = Endpoint.create(new SOAPService()); // this class to handle all ws requests
endpoint.publish(jettyServer.createContext("/service")); // access by path
server.start()
Simplified code example above to show the only way that I have found to bridge between Jetty and incoming soap requests to my jax-ws service. All settings are in code with no web.xml, this is part of a larger solution that has multiple contexts and connections for different purposes (servlets etc..)
I have tried to add a handler class to the jettyServer.createContext("/service",new handler()) to see if I can perform a header extraction to simulate basic auth but it never gets executed.
My problem is that i cannot find a way to specify, by code against the Jetty server, to use basic authentication. Using the setSecurityHandler method of a ServletContextHandler is easy and works great for other contexts, i just can't figure out how to use this concept for the jax-ws service.
Any help would be much appreciated.
p.s. SSL is already implemented, I just need to add http basic auth.
For anyone else that may of come across the same problem here is the answer that i stumbled on eventually.
final HttpContext httpContext = jettyServer.createContext("/service");
com.sun.net.httpserver.BasicAuthenticator a = new com.sun.net.httpserver.BasicAuthenticator("") {
public boolean checkCredentials (String username, String pw)
{
return username.equals("username") && pw.equals("password");
}
};
httpContext.setAuthenticator(a);
endpoint.publish(httpContext);//access by path
You can expand the checkCredentials for something a bit more sophisticated of course, but this shows the basic working method.