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

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.

Related

Implementing Oauth Server with Authorization Code grant

A bit naive question. I am implementing OAuth server 2.0 with grant type = Authorization code grant. Here's what I have done
1) Created 2 endpoints as /login and /token
2) Created a login page with html as
<html>
<form action=\"/login\" method=\"post\">
<div class=\"textcontainer\">
<label><b>Sign in</b></label>
</div>
<div class=\"emailcontainer\">
<input type=\"text\" placeholder=\"Enter email\"
name=\"uname\" required>
</div>
<div class=\"passwordcontainer\">
<input type=\"text\" placeholder=\"Enter Password\"
name=\"psw\" required>
</div>
<div class=\"signincontainer\">
<button type=\"submit\">Sign in</button>
<input type=\"checkbox\" checked=\"checked\"> Remember me
</div>
<div class=\"container\" style=\"background-color:#f1f1f1\">
<span class=\"psw\">Forgot password?</span>
</div>
</form>
3) The above page will be displayed when client sends
GET
/login?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&state=STATE_STRING&scope=REQUESTED_SCOPES&response_type=code
4) I will save the state and redirect URI and the html page is displayed.
5) When user enters email and password and clicks submit button, then a POST will be issued to my server as POST /login and I will fetch the email and password from the request and verify.
Now the problem is I am not able to figure out a way to know if this POST has come from the same client who has called the previous GET.
Is there any way/html trick I can do to resend the state again in my submit button post? Is saving client IP the only way?
I understand the POST /token so that part is clear.
Thanks
Sammy
You can generate a unique identifier (GUID) on the server, before sending the form.
Then put it into the form inside a hidden input :
<form>
... your code
<input type="hidden" value= "<%= <your identifier> %>">
</form>
Then this unique value will be returned to you in the POST.
the way you insert the identifier depends on your programming language, I used C# an the above example.

REST API Security : Use ID to update database

I built a simple REST FULL API with NodeJS.
When it comes to HTML code, I do have :
<form method="post" action="/registry/api">
<input name="id" type="text" value="6" readonly>
<input name="name" type="text" value="TEST name">
<input name="description" type="text" value="TEST desc">
<input name="approved" type="checkbox" checked>
<input type="submit" value="Update">
</form>
But if I go in the console, erase 'readonly' and change this id from '6' to '7', my API indeed receives the bad id and update the bad row in database.
How can I fight this ?
Your API should implement some kind of authentication and authorisation layer. One way to do this is to pass a token along with the request (e.g. in Authorization header), so you will know on the backend side, who is the user requesting this action. Then you can simple check whether given user is permitted to do such an action (with given id).
You can take a look at this blogpost, which describes this pattern using JWT (JSON web tokens):
https://scotch.io/tutorials/authenticate-a-node-js-api-with-json-web-tokens

j_security_check invalid direct reference

I currently have a login form on a public, unprotected http page. Upon login I want the page to redirect to a secure https page. I am receiving this error screen when I try to login however:
I'm 90% sure my syntax is accurate. I have this exact form as a login page elsewhere on the site. This implementation is for a drop down box that enables the user to login. Here is my form for reference. Any help is appreciated, Thanks.
<div class="login-options">
<form id="login" name="j_security_form" action="<%out.write(Dropdown.getXML("https://www.xxx.com/public/j_security_check")); %>" method="post" accept-charset="UTF-8">
<strong>Account Login</strong><br>
Username:<br>
<input type="text" name="j_username"><br>
Password:<br>
<input type="password" name="j_password"><br>
<br>
<input type="submit" value="Login"><br>
<br>
New users register here<br>
Forgot password?
<input type="hidden" name="auth_mode" value="basic">
<script>
var newloc = document.location.href;
newloc =newloc.replace('index.jsp','index.jsp');
document.write('<input type="hidden" name="orig_url" value="'+newloc+'">');
</script>
</form>
</div>
Might be because you're trying to directly reference the url for j_security_check. I would try referencing a page that requires login creds, then it'll redirect to the secure page once it sees that there are adequate credentials.

"Cannot POST /dialog/authorize/decision" while implementing the OAuthorize example

I'm implementing OAuth 1.0a with the OAuthorize library. I'm following the example provided by the library.
I'm hosting my app at Heroku. Using the test client I'm able to GET a request token, and use it to /dialog/authorize:
Hi Bob Smith!
Samplr is requesting access to your account.
Do you approve?
When I press Allow, I get a Cannot POST /dialog/authorize/decision.
I've registered the endpoint in app.js:
app.post('/dialog/authorize/decision', oauth.userDecision);
Either I am missing something silly from the OAuthorize example or there is a fault in the way I'm handling the transaction.
dialog.ejs:
<form action="/dialog/authorize/decision" method="post">
<input name="transaction_id" type="hidden" value="<%= transactionID %>">
<div>
<input type="submit" value="Allow" id="allow">
<input type="submit" value="Deny" name="cancel" id="deny">
</div>
</form>
For me, the issue was that I didn't provide a proper callbackURL in server.userAuthorization callback.
see https://github.com/jaredhanson/oauthorize/blob/master/examples/express2/db/accessTokens.js#L10 and https://github.com/jaredhanson/oauthorize/blob/master/examples/express2/oauth.js#L152

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

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

Resources