CSRF protection in login form - security

I'm using Symfony 2.1 to build a website with a little login form. I'm following the tutorial at this link but I don't see any part talking about the CSRF protection. However, here there are all the options for the login security and at the end I can clearly see that that type of protection should be supported. I don't understand how to use it

Here you can read in details about CSRF protection in version 2.1
In case if you don't use form classes for your forms, you can simply use csrf_token function (don't forget to pass your intention string there, which is empty by default):
<input type="hidden" name="token" value="{{ csrf_token('') }}">
It is defined here and in default cases will execute this method.
May be these answers might be useful for you also:
https://stackoverflow.com/a/12054712/970721
https://stackoverflow.com/a/11632713/970721

Nothing is preventing you from creating a form class for login. All you need to do is to tell the login controller the names of the generated fields:
form_login:
username_parameter: login[username]
password_parameter: login[password]
csrf_parameter: login[_token]
and set the form's CSRF intention to authenticate:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'intention' => 'authenticate',
]);
}

Related

JMeter Office360 online : JavaScript required to sign in error while authorization

When I try to login on sharepoint360 online site using JMeter, it gives JavaScript required to sign in error in response. I got below code in response.
<html>
<head>
<noscript>JavaScript required to sign in</noscript>
<title>Continue</title>
<script type="text/javascript">
function OnBack() {}
function DoSubmit() {
var subt = false;
if (!subt) {
subt = true;
document.fmHF.submit();
}
}
</script>
</head>
<body onload="javascript:DoSubmit();">
<form name="fmHF" id="fmHF" action="https://ApplicationURL.sharepoint.com/_forms/default.aspx?apr=1&wa=wsignin1.0" method="post" target="_self">
<input type="hidden" name="t" id="t" value="EgBZAgMAAAAEgAAAAwABJaEZDRmSdbQKqL6rJyYKAN1Z0valM74LOhnXMHkbILiqeyEyWkHmbZ3hVG8XvpXOVLvlnylOzhJx8KQgSsuO0d6P2quRRU168QMumuqH/qvLVkpZ5mPiBDDyI8l1z+E4++pxZ3H37uIevfjckj40rTlGYJ85aT0/nxcqDxlL6pxKStDm9GKNI81ypsDKv7dMEqg9lRDjJO5qmNEDiCP8jPB3WWke5pVqcn6c1MbnbHzKU2uHzekZqjxAflfvWu5LS9R+N6jciFZvicn8P0EV0LZHqmjXgUsoVMdn9gGemJYnQCoIbkk7Oo9DPaqXRsxApY5sqCwotEJvZ45AaBNAQkgBYABIAQAAAxBYNSeVFbxuVhW8blZOoQcACgAgAAAhAGtrQHNmd3Byb2R1Y3RkZXYub25taWNyb3NvZnQuY29tAGMAAC1rayVzZndwcm9kdWN0ZGV2Lm9ubWljcm9zb2Z0LmNvbUBwYXNzcG9ydC5jb20AAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAB0t1bGRlZXAABUt1bWFyAAAAACAAAAAAAAAAAAD//////////wAAFbxuVpUsb1YAAAAAAAAAAAAAAAANADEyMy42My42NS42NgAAAAAAAAAAAAAAAAAAAQAAAQAAAQAAAQAAAADU1N9WAQAAAQAAAAAAAAAAAAAAAAAANwBodHRwczovL3BvcnRhbC5taWNyb3NvZnRvbmxpbmUuY29tL0NoYW5nZVBhc3N3b3JkLmFzcHgA/////wAAAAAAAA==">
</form>
</body>
Thanks in advance.
Sunil
As per Apache JMeter Homepage
JMeter does not perform all the actions supported by browsers. In particular, JMeter does not execute the Javascript found in HTML pages. Nor does it render the HTML pages as a browser does
You need to extract the "value" attribute of that "t" hidden input and pass it along with other request parameters. I would recommend using CSS/JQuery Extractor or XPath Extractor for this.
Relevant configurations:
CSS/JQuery Extractor
Reference Name: anything meaningful, i.e. t
CSS/JQuery Expression: input[id=t]
Attribute: value
XPath Extractor
Use Tidy - check. If response is XHTML compliant it is not required however.
Reference Name: again, any JMeter Variable name, i.e. t
XPath Expression: //input[#id='t']/#value
Refer the extracted value as ${t} where required
Handle any other dynamic parameters similarly and you should be good to go. JMeter doesn't execute JavaScript but it can capture and execute JavaScript-driven HTTP requests. Given you perform load testing you shouldn't worry about what is happened in browser as JavaScript is being executed on client side only. See ASP.NET Login Testing with JMeter article for more detailed explanation and instructions.
Looking at how sharepoint authentication works, I believe you need to implement authentication using HTTP Request, instead of using login page, as explained here. Basically you will need 2 HTTP requests: first to extract the SAML token, and second to receive authentication cookies, which you can then use in the following HTTP requests, as you normally do.

How does Symfony2 CRSF protection work?

I'm trying to test the CRSF protection system done by Symfony2, many thanks to them.
my security.yml template:(I modified the default one.)
security:
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/demo/secured/login$
security: false
secured_area:
pattern: ^/demo/secured/
form_login:
check_path: _security_check
login_path: _demo_login
csrf_provider: form.csrf_provider
logout:
path: _demo_logout
target: _demo
#anonymous: ~
#http_basic:
# realm: "Secured Demo Area"
access_control:
#- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
In my form :
<input type="hidden" name="_csrf_token" value="{{ csrf_token("authenticate") }}">
That generates something like this:
<input type="hidden" name="_csrf_token" value="cKzXBHRDX_sHuT4qt9TAJIwgRvtRMtPnFDtitrSZDuw">
I don't know how symfony handles the verifications with the token, but before submitting my login, I changed the value of the token manually using firebug to look like this:
<input type="hidden" name="_csrf_token" value="MODIFIEDcKzXBHRDX_sHuT4qt9TAJIwgRvtRMtPnFDtitrSZDuw">
and when I submit my login, I get logged in. which means the token has no influence .
where am I getting wrong ?
Snipe hunting
Symfony Version is 2.5.2
The system signed me in when I set manually a session variable "logged" to true. This happens after Reading from the database and comparing the passwords.
Form Html!
<form id="Loginform" onsubmit="OrganicLogin();return false;">
<input type="hidden" name="_csrf_token" value="{{ csrf_token("authenticate") }}">
<div id="Loginresponse" style="display:none;"></div>
<div class="form-group" style="overflow:hidden;">
<label style="margin-top:10px;" for="inputUsername" class="col-lg-2 control-label">Username</label>
<div class="col-lg-10">
<input type="text" class="form-control" id="inputUsername" placeholder="Username" style="width:215px;float:right;">
</div>
</div>
<div class="form-group" style="overflow:hidden;" >
<label style="margin-top:10px;" for="inputPassword" class="col-lg-2 control-label">Password</label>
<div class="col-lg-10">
<input type="password" class="form-control" id="inputPassword" placeholder="Password" style="width:215px;float:right;">
</div>
</div>
<div class="form-group" style="overflow:hidden;text-align:center;" >
<button type="submit" class="btn btn-primary btn-block" id="submitButton">Access</button>
</div></form>
Yes ! I did
Actually that what I was arguing about the whole time, I did the login process in a native way, form, read data with JS, send POST request to controller, controller checks input and set the session.
No, All done by hand
Actually this is the first time I use security.yml, I just removed some parts I judged not useful for this thread
no ..
I'm sort of guessing that your changed token is not getting posted. Stick a die in:
namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;
class DefaultCsrfProvider implements CsrfProviderInterface
{
public function isCsrfTokenValid($intention, $token)
{
die('csrf token ' . $intention . ' ' . $token);
return $token === $this->generateCsrfToken($intention);
}
If the die is reached then you know your configuration is okay and of course you can see the actual posted token.
Needless to say, you should also clearcache.
=======================================================
Update 1 - After many comments we have determined that the die() is not being called. Progress.
Unfortunately we still need to verify exactly how the poster has configured their system.
Next step - Login without adjusting the csrf token via firebug and verify that the die statement is reached.
Report one way or another.
Needless to say (but I will say it anyways), make sure you logout before trying to log back in.
========================================================
Update 2 - The die statement is not being reached even with a normal login.
So now comes my favorite part. Snipe hunting. Basically, I made a number of assumptions when reading the question. Need to determine which assumptions were incorrect by asking a number of basic questions.
Which version of Symfony 2 are you using. I am assuming at least S2.1.
How do you know the system has signed you in? Are you using the debug toolbar and does it show you as being authenticated? What happens when you try to login with an incorrect password?
Use your browser's view source functionality and copy the generated form into your question. In particular I want to see the action attribute but I also want to see the input elements.
Did you in fact add the die statement to vendor/symfony/symfony/src/Symfony/Component/Form/Csrf/CsrfProvider/DefaultCsrfProvider.php? Did you save the file after editing it?
You are in fact using the standard form_login process right? You don't have any code that, for example, checks the user password?
Are you using any other bundles like maybe FOSUserBundle?
The security.yml file in your question really is your actual file? You didn't "clean it up" after copying?
Have you checked your application into github? If so then can you provide a link? Looking at the entire application will probably be the fastest way to clear this up.
That should be enough for now. Update your question with your answers.
=========================================================================
Update 3 - The plot thickens
As I was typing in the above questions we discover that the basic login system itself is not properly configured. The debug toolbar indicated the user is not authenticated. More progress! As so often happens, the symptoms were masking the actual problem.
The security system is arguably the most complicated component in Symfony 2 that typical developers need to interact with. It's easy to get confused when configuring it and difficult to troubleshoot. One tiny typo can melt things down. It's also very important for the developer to have a working understanding of how security is implemented. Unless of course you are a really big company like Target or Home Depot.
My suggestion is to create a fresh Symfony 2 project using composer. Then go through http://symfony.com/doc/current/book/security.html step by step and configure the security system. Let this be kind of a reference application for understanding security.
By the end of the process I suspect you will have figured out the problem and can apply the solution to your existing application. As a bonus you will have something you can refer to for future problems.
==================================================================
Update 4 - The exciting conclusion
So now we find that a custom and naive login system is being used.
I would still suggest starting over with a new project and get things working the Symfony 2 way. After that, you can tweak the login form to use javascript if you really want to.
If you really really really want to use your own system then start here: Manual authenticate user
But you would be tossing out one of Symfony's major strengths for no particular reason.
The way it's supposed to work is that Symfony generates a CSRF token, which it automatically inserts into the form. It stores this token in the current session. When the form is submitted, it compares the submitted token with the value stored in the session. Regarding your specific case, it just sounds like CSRF isn't actually enabled and it may have to do with security contexts not being shared between the secured area firewall, which has CSRF enabled, and the login firewall, which does not.
Try removing this bit in your security.yml:
login:
pattern: ^/demo/secured/login$
security: false
And instead, moving it into the secured_area context and using access controls to grant access:
...
form_login:
check_path: _security_check
login_path: login
...
access_control:
- { path: ^/demo/secured/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
Alternatively, you could try adding context: secured_area for your login firewall. In my experience, not having the login firewall in the same context as the secure area prevents you from accessing the security context entirely from within your login controller.

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

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

How to use different target paths after logout?

Here's my problem: I have a project translated in 2 languages (en & fr). In the security.yml, I've to configure a firewall (with a login form), and specifically the target path after logout:
logout:
path: /user/logout
target: /
But I can't choose the target path depending on user's culture...
I don't have this problem when signin, in fact I choose the target path directly in my login form:
<input type="hidden" name="_target_path" value="/{{ app.session.locale }}/" />
So I don't know how to redirect after logout on /fr/ if you are french, or otherwise /en/ ... Do you have any solution ?
You can add a custom logout handler, see successHandler property of Symfony\Component\Security\Http\Firewall\LogoutListener. With the DIC you can easily override it.

Resources