How to programmatically send POST request to JSF page without using HTML form? - jsf

I have very simple JSF bean like shown below:
import org.jboss.seam.annotations.Name;
#Name(Sample.NAME)
public class Sample {
public static final String NAME="df";
private String text = "text-test";
public void sampleM(){
System.out.println("Test: "+text);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
And JSF form connected with this component:
<h:form id="sampleForm">
<h:commandButton id="sampleButton" action="#{df.sampleM()}" value="ok" />
</h:form>
Now, I would like to programmatically send POST request to this form.
According to my investigation the key here are POST parameters.
Selected properly gives proper results (String 'Test: text-test' is printed on serwer's console).
So the question is: How should I select POST data that was correct?
JSF form shown above produces this HTML form:
<form id="sampleForm" name="sampleForm" method="post" action="/pages/main/main.smnet" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="sampleForm" value="sampleForm" />
<input id="sampleForm:sampleButton" type="submit" name="sampleForm:sampleButton" value="ok" />
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id65" autocomplete="off" />
</form>
So these parameters are corrent.
But how can I find out what parameters (name and value) will be sufficient for any other component?
For example: when I send POST data the same like in shown HTML form but with different 'javax.faces.ViewState' parameter value, component method will not be executed.

I understand that you're basically asking how to submit a JSF form programmatically using some HTTP client such as java.net.URLConnection or Apache HttpComponents Client, right?
You need to send a GET request first and make sure that you maintain the same HTTP session (basically, the JSESSIONID cookie) across requests. Let your HTTP client extract the Set-Cookie header from the response of the first GET request, obtain the JSESSIONID cookie from it and send it back as Cookie header of subsequent POST requests. This will maintain the HTTP session in the server side, otherwise JSF will treat it as a "View Expired" which may return either on a decently configured JSF web application a HTTP 500 error page with ViewExpiredException, or on a badly configured JSF web application behave as a page refresh.
As part of JSF's stateful nature and implied CSRF attack prevention, the forms must be submitted with a valid javax.faces.ViewState value as the client has retrieved itself on the initial GET request. You also need to make sure that you send the name=value pair of all other hidden fields and particularly the one of the submit button along as well.
So, if your initial GET request gives you this HTML back
<form id="sampleForm" name="sampleForm" method="post" action="/pages/main/main.smnet" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="sampleForm" value="sampleForm" />
<input id="sampleForm:sampleButton" type="submit" name="sampleForm:sampleButton" value="ok" />
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id65" autocomplete="off" />
</form>
then you need to parse it (Jsoup may be helpful in this) and extract the following request parameters:
sampleForm=sampleForm
sampleForm:sampleButton=ok
javax.faces.ViewState=j_id65
Finally send a POST request on /pages/main/main.smnet with exactly those request parameters (and the JSESSIONID cookie!). Be careful though, it's possible that a (poor) JSF developer has skipped e.g. id="sampleButton" from the <h:commandButton> and then JSF would autogenerate one which looks like in this format sampleForm:j_id42. You can't hardcode them as the value may change depending on the component's position in the server side tree and you would then really need to parse it out the obtained HTML.
Nonetheless, it's wise to contact the site owner/admin and ask if there isn't a web service API available for the task you had in mind. A decent Java EE website which uses a JSF application for a HTML frontend usually also uses a separate JAX-RS application for a REST frontend. It is much more easy and reliable to extract information via such a web service API than by scraping a HTML document.
See also:
How can i programmatically upload a file to a website? (this also concerns JSF)
How to use java.net.URLConnection to fire and handle HTTP requests

Related

How can I prevent JSF 2.2 from accepting a ViewState from a different session?

I am using JSF 2.2 (Glassfish 4.1). Our webapp has Primefaces 6.0 and Omnifaces 2.6.9 as dependencies. JSF state is stored on server.
As an example say I have this form where userModel is a javax.faces.view.ViewScoped bean.
<h:form id="user">
<p:inputText id="name" value="#{userModel.name}"/>
<p:inputText id="pass" value="#{userModel.pass}"/>
<p:commandButton id="create" value="#{msgs.lbl_add}" action="#{userModel.addUser()}"/>
</h:form>
A company scanned our web app for security issues and claims it has a CSRF vulnerability. An attacker can provide a made up form to one of our app users like this to perform unwanted actions.
<!DOCTYPE html>
<html>
<body>
<form action="http://appserver:8080/myapp/users.jsf" method="POST">
<input type="hidden" name="javax.faces.source" value="user:create"/>
<input type="hidden" name="user:create" value="user:create"/>
<input type="hidden" name="user" value="user"/>
<div>
<input type="text" name="user:name" value="FAKEUSER"/>
<input type="text" name="user:pass" value="FAKEPASSWORD"/>
<input type="text" name="javax.faces.ViewState" value="1185295409278172717:-3206872038807094332"/>
</div>
<input type="submit" name="submit" value="Create User"/>
</form>
</body>
</html>
I read on SO that ViewState is JSF way of preventing CSRF. But following scenario are possible in our web app (with HTTP protocol if it matters).
Attacker visits our app login page to find a valid ViewState in his page source.
Attacker prepares HTML file mentioned above with his ViewState and sends to victim.
Victim opens HTML file in browser and submits (from local file system e.g. file:///C:/... or hosted by local webserver)
User is created.
The conclusion is that our webapp/JSF in this scenario does not check if the received ViewState belongs to the session identified by JSESSIONID.
Isn't this a vulnerability? How can I avoid this?
In our case it was a bug in our application. We also use Deltaspike library and registered a 'Handle-All-Exceptions' class in our app.
import org.apache.deltaspike.core.api.exception.control.ExceptionHandler;
import org.apache.deltaspike.core.api.exception.control.Handles;
import org.apache.deltaspike.core.api.exception.control.event.ExceptionEvent;
#ExceptionHandler
public class ExceptionDispatcher {
public void processException(#Handles ExceptionEvent<Throwable> evt) {
// Handle exception by just logging
}
}
This also "handled" javax.faces.application.ViewExpiredException which occur if JSF finds a invalid ViewState. As a result the request was normally processed.
A better implementation will redirect to an error page and invalidate session.

Faces in ADF can't get the request parameters

I have an ADF web application with Faces 1.2
In my managed bean I am trying to access the parameters of the post request, but always getting null.
This is my post form that I am posting to the Faces:
<form name="input" action="http://127.0.01:7072/myapplication/faces/login.jspx" method="post">
<input type="hidden" name="user" id="user" value="myUserName"/>
<input type="submit" value="Submit"/>
</form>
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("user");
FacesContext.getCurrentInstance().getExternalContext().getRequest().getParameter("user");
But I always get null and the method of the form I get is: GET, strange!
when you make a request for a URL like:
http://server/faces/somepage.jspx
ADF framework respone you by a "HTTP Error 302 Moved temporarily" response and redirect you to the URL like :
http://server/faces/somepage.jspx?_adf.ctrl-state=888888&_afrLoop=9999999
so your first POST request is omit and browser send an other request to the new URL that ADF returned in response, so because POST method send parameter in the request body the browser dont resend them in new Get request
so i think there is no way for you to get parameters direcly in side the ADF page ! :(
but i think the best way is to create a Servlet and get you param in it and put them to session and then redirect the user from servlet to your ADF page :)
I managed reading the request parameters through including my parsing operations in the filter mappings that my application was using.
In a very abnormal way FacesContext always rejected revealing the current parameters, till now it's a mystery for me why it's designed liked that, even through its external context ...

How to submit xml data to a web site?

I am using a third party to process transactions. They have an api that says XML content should submitted via an HTTP POST variable named “XML”.
I know how to create the xml, but not sure how to post it to their site. They have a destination url. Can you tell me how to do the Post to their site?
You need to carefully check. Usually you just post XML to an URL. However in this case (indicated by the variable name) it seems that a (typically only used for html forms) form post is needed.
The easiest way is to create a html form with that one field, something like this:
<form method="post" action="http://their url" name="payload">
<input type="hidden" id="XML" name="XML" />
</form>
Then you can fill the field with your XML and do a payload.submit()
Let us know how it goes

Verifying additional parameter with j-security-check

I have implemented web application login using j-security-check which accepts parameters j_username and j_password for authentication with background registry. Now, I need to add date of birth as one more parameter and need to authenticate the user with this parameter too.
Is there a way to extend j-security-check to accept additional parameters?
I would like to avoid performing the check in a servlet filter, if possible.
Using WebSphere V8, MyFaces JSF 2.0, Servlet 3.0, Custom database based authentication
The easiest way would be to append the date of birth to the actual j_username (ie. with JavaScript and then manually split it in the login module.
Replace j_security_check by programmatic login via HttpServletRequest#login().
E.g.
<h:form>
<h:inputText value="#{bean.username}" />
<h:inputSecret value="#{bean.password}" />
<h:inputText value="#{bean.birthdate}" />
<h:commandButton value="Login" action="#{bean.login}" />
</h:form>
with
public void login() {
// Do your thing with birthdate here.
// ...
// Then perform programmatic login.
try {
request.login(username, password);
// Login success. Redirect to landing page.
} catch (ServletException e) {
// Login fail. Return to login page.
}
}
This is in detail outlined in 2nd part of this answer: Performing user authentication in Java EE / JSF using j_security_check

Understanding HttpServletRequest and cookies in JSF

In order to create a "Remember me" login in JSF, I am trying to understand how Cookies work. I have created a brand new Web Application using JSF, with this bean that creates a Cookie expiring with the session:
CookieBean class
#ManagedBean
#ViewScoped
public class CookieBean implements Serializable {
public void create() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.addResponseCookie("MyTestCookie", "Hello Cookie", null);
}
}
and index.xhtml has this body:
<h:form>
<h:commandButton value="Create Cookie!" action="#{cookieBean.create()}" >
<f:ajax render="#form" />
</h:commandButton>
<p></p>
<h:outputText value="Cookie value: #{cookie['MyTestCookie'].value}" />
</h:form>
As a result, when the page first loads, there is no cookie, correctly, because it's the first time the application runs, and no cookie is there.
After clicking the button once, no cookie is displayed. Why? The button invokes the cookieBean#create() method, and the ajax tag should force a revaluation of the outputText component. And this should generate an HttpSerlvetRequest with the cookie... or not? The cookie shows only after I press the button again!.
More surprisingly, when I press the refresh button of the browser, the cookie is not shown, although I'd expect to see it, because the older session is still alive.
It's like if (re)loading the page doesn't send an HttpServletRequest to the server...
The #{cookie} refers to the cookies of the current HTTP request. If you add a new cookie, then it appears only on the HTTP response, but the HTTP request which is associated with this HTTP response does of course not have the cookie yet. It's only present in the subsequent request, depending on the age of the cookie.
You basically need to send a redirect afterwards to make the cookie available during rendering the HTTP response for the HTTP request.
As to the refresh matter, the request was most likely re-executed in the browser cache.

Resources