Codeigniter CSRF sometimes throwing error - codeigniter

First of all, this rarely happens. The form itself was submitted successfully 500+ times. And we received about 3 reports where users get a "An Error was encountered: The action you have requested is not allowed".
This specific error can be found in the Security Class php code, and is only shown when the csrf tokens do not mach, or if one or both (cookie/post) are missing.
Here are some relevant config values:
$config['cookie_prefix'] = 'prefix_';
$config['cookie_domain'] = '';
$config['cookie_path'] = '/';
$config['cookie_secure'] = FALSE;
$config['cookie_httponly'] = FALSE;
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf';
$config['csrf_cookie_name'] = 'csrf';
$config['csrf_expire'] = 86400;
$config['csrf_regenerate'] = FALSE;
As said, in almost all cases, users have no issue submitting the form. When a user does have an issue, I tried to replicate the error by submitting the form myself, using identical input values and identical browser/os (cookies and javascript enabled, without browser plugins). But I wasn't able to simulate the error.
What could be causing Codeigniter to sometimes throw an error?

Do You allow user to submit the form twice (You didn't add a form-blocker after the first submit, so while the first request processing, user can submit this form with same CSRF value again)? If so, when user submits the form twice, the first request calls CSRF to re-generate csrf-value and the second request Your user sends, sends old CSRF value and as a result You get this error.

Related

Session object Not getting clear after log out when calling through postman

I have a page when i login and then do f12 , i copy the request header , cookies and the request url and using post in postman i get the data.
After i logout and refersh the page again i use the same cookie in the postman and post the url data but i still get the data, even if i have done log out i still get the data.
I dont want to get any data after i logged out.i want the cookie to get expire or change when i logout.
If useing postman after logout should not return any data in the response.
Please help me.
Here is my logout code
Dim authCookie As HttpCookie = Request.Cookies(FormsAuthentication.FormsCookieName)
Dim authTicket As FormsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value)
Session.Abandon()
Session.Clear()
FormsAuthentication.SignOut()
End If
Session.Contents.Clear()
Session.Clear()
Session.RemoveAll()
Session.Abandon()
HttpContext.Session.Clear()
FormsAuthentication.SignOut()
Response.Cache.SetCacheability(HttpCacheability.NoCache)
Response.Cache.SetNoStore()
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1))
Response.Cache.SetNoStore()
Response.Expires = 60
Response.ExpiresAbsolute = Now
Response.CacheControl = "no-cache"
HttpContext.User = New GenericPrincipal(New GenericIdentity(String.Empty), Nothing)
Response.Redirect(System.Configuration.ConfigurationManager.AppSettings("root").ToString() + "logon.aspx")

How to protect against CSRF on a static site?

I have a static website, being served from a CDN, that communicates with an API via AJAX. How do I protect against CSRF?
Since I do not have control over how the static website is served, I cannot generate a CSRF token when someone loads my static website (and insert the token into forms or send it with my AJAX requests). I could create a GET endpoint to retrieve the token, but it seems like an attacker could simply access that endpoint and use the token it provides?
Is there an effective way to prevent against CSRF with this stack?
Additional details: authentication is completely separate here. Some of the API requests for which I want CSRF protection are authenticated endpoints, and some are public POST requests (but I want to confirm that they are coming from my site, not someone else's)
I could create a GET endpoint to retrieve the token, but it seems like an attacker could simply access that endpoint and use the token it provides?
Correct. But CSRF tokens are not meant to be secret. They only exist to confirm an action is performed in the order expected by one user (e.g. a form POST only follows a GET request for the form). Even on a dynamic website an attacker could submit their own GET request to a page and parse out the CSRF token embedded in a form.
From OWASP:
CSRF is an attack that tricks the victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on the victim's behalf.
It's perfectly valid to make an initial GET request on page load to get a fresh token and then submit it with the request performing an action.
If you want to confirm the identity of the person making the request you'll need authentication, which is a separate concern from CSRF.
My solution is as follows
Client [static html]
<script>
// Call script to GET Token and add to the form
fetch('https:/mysite/csrf.php')
.then(resp => resp.json())
.then(resp => {
if (resp.token) {
const csrf = document.createElement('input');
csrf.name = "csrf";
csrf.type = "hidden";
csrf.value = resp.token;
document.forms[0].appendChild(csrf);
}
});
</script>
The above can be modified to target a pre-existing csrf field. I use this to add to may pages with forms. The script assumes the first form on the page is the target so this would also need to be changed if required.
On the server to generate the CSRF (Using PHP : assumes > 7)
[CSRFTOKEN is defined in a config file. Example]
define('CSRFTOKEN','__csrftoken');
Server:
$root_domain = $_SERVER['HTTP_HOST'] ?? false;
$referrer = $_SERVER['HTTP_REFERER'] ?? false;
// Check that script was called by page from same origin
// and generate token if valid. Save token in SESSION and
// return to client
$token = false;
if ($root_domain &&
$referrer &&
parse_url($referrer, PHP_URL_HOST) == $root_domain) {
$token = bin2hex(random_bytes(16));
$_SESSION[CSRFTOKEN] = $token;
}
header('Content-Type: application/json');
die(json_encode(['token' => $token]));
Finally in the code that processes the form
session_start();
// Included for clarity - this would typically be in a config
define('CSRFTOKEN', '__csrftoken');
$root_domain = $_SERVER['HTTP_HOST'] ?? false;
$referrer = parse_url($_SERVER['HTTP_REFERER'] ?? '', PHP_URL_HOST);
// Check submission was from same origin
if ($root_domain !== $referrer) {
// Invalid attempt
die();
}
// Extract and validate token
$token = $_POST[CSRFTOKEN] ?? false;
$sessionToken = $_SESSION[CSRFTOKEN] ?? false;
if (!empty($token) && $token === $sessionToken) {
// Request is valid so process it
}
// Invalidate the token
$_SESSION[CSRFTOKEN] = false;
unset($_SESSION[CSRFTOKEN]);
There is very good explanation for same, Please check
https://cloudunder.io/blog/csrf-token/
from my understanding it seems static site won't face any issue with CSRF due to CORS restriction, if we have added X-Requested-With flag.
There is one more issue i would like to highlight here, How to protect your api which is getting called from Mobile app as well as Static site?
As api is publicly exposed and you want to make sure only allowed user's should be calling it.
There is some check we can add at our API service layer for same
1) For AJAX request(From Static site) check for requesting domain, so only allowed sites can access it
2) For Mobile request use HMAC token, read more here
http://googleweblight.com/i?u=http://www.9bitstudios.com/2013/07/hmac-rest-api-security/&hl=en-IN

remember me for laravel5.2

Hello guys I want to make the remember me checkbox and I want to save the user info into cookies so next time when try to login he find the user name and password in their fields I try to use :
$rememberMe = false;
if(isset($req->remember_me)) {
$rememberMe = true;
}
if(Sentinel::authenticate($req->all(), $rememberMe)) {
$slug = Sentinel::getUser()->roles()->first()->slug();
}
The cookies was set, I see it in the chrome settings but it does not do as I expect
I'm using laravel 5.2
You can use Cookies
cookie, is a small piece of data sent from a website and stored in a user's web browser while the user is browsing that website. Every time the user loads the website, the browser sends the cookie back to the server to notify the website of the user's previous activity
To create:
$response->withCookie(Cookie::make('name', 'value', $minutes));
To retrieve
$value = Cookie::get('name');
Your question is not to remember the user login.. The question is how to fill the inputs based on saved auth information. You can do that if you print the authentication values in the input value attribute while loading the page.
larval Cookies Docs
Also Laravel has it's own implementation of "Remember Me"
if (Auth::attempt(array('email' => $email, 'password' => $password), true))
{
// The user is being remembered...
}
if (Auth::viaRemember())
{
//
}
More information about https://laravel.com/docs/5.4/authentication#remembering-users
There is two main thing need to taken care:
1) You must pass a bool value as second parameter to the method, make sure you cast it before passing it to the method. - In your code, it's perfect
$credentials = $req->only('LOGINNAME', 'PASSNAME')
if(Sentinel::authenticate($credentials , $req->has('remember_me'))){
//Other stuff
}
2) you can verify it works by ensuring a cookie is set with the key cartalyst_sentinel?
So first change as per 1) option and then check the 2) option, may be this is your answer.

How I can modify response (JSON instead of redirection) if user is already logged in in laravel 5.2?

First look at this. I have solved that problem by getting XSRF-TOKEN token from request cookie.
VerifyCsrfToken.php
$token = $request->input('_token') ? : $request->header('X-CSRF-TOKEN');
// By Me - Start
if($token == null) {
$token = $request->cookie('XSRF-TOKEN');
}
// By Me - End
This works fine. But my problem is that if user is get logged in once, then on requesting again from .net app, I'm getting html(view) response. I want to update that response as JSON response. I have tried to debug. But I can't find any function from where I can modify that response. If any one knows the solution or alternative way, it will be appreciated.

Codeigniter csrf token not in post array

When posting a form with a csrf token, $this->input->post("csrf_token") is empty.
I could post a duplicate csrf_token using another field name. But that looks a bit unnecessary.
Is there (another) way to get it?
__
All is done using AJAX. So first of all, a token must be requested, and is provided using a json template, populating it this way:
$data["json"] = array(
"csrf_token" => $this->security->get_csrf_hash()
);
Using that token, a ajax POST request is done, sending user login, password. If ?debugis added to the request url, and the ENVIRONMENT is not production, the complete post request parameters are added to the json output. Like so:
if( !is_null($this->input->get("debug")) && ENVIRONMENT != 'production'){
$debug = TRUE;
$data["json"]["post"] = $this->input->post();
}
And I get:
"post": {
"un": "test",
"pw": "test"
}
Adding $data["json"]["old_token"] = $this->input->post("csrf_token");gives me "old_token": null
The Cross-site request forgery itself, works as expected: no token, wrong token or expired token gives an error. So Codigniter does receive the token as a supposed to. It seems to be removed from the post data.
After some poking around, I've found the answer. The security class removes the token from the POST array: unset($_POST[$this->_csrf_token_name]); (core/Security.php in csrf_verify() at line 234)
I won't change that line, to be sure the controller keeps functioning after updating Codeigniter.

Resources