Code Monkey home page Code Monkey logo

csrf's Introduction

Automatic protection from Cross-Site Request Forgery.

This library handles CSRF protection automatically for you, including generating tokens, injecting them into all forms in the page and then verifying that a valid token is present whenever a POST request is received.


Build status Code quality Code coverage Current version PHP.Gt/Csrf documentation

Usage: Protection in Three Steps

The CSRF library does two things:

  • Injects CSRF tokens into forms
  • Verifies POST requests to make sure they contain a valid token

Each is just a single method call, but you need to set up first.

Step 1: Set up

Start by creating the TokenStore. There are currently two implementations — the ArrayTokenStore and SessionTokenStore. The ArrayTokenStore is the most basic and does not persist in any way, but can be extended into custom integrations. The SessionTokenStore is an inbuilt implementation that persists tokens between requests, so that tokens generated for one page request can be checked on another. The easiest way to add CSRF protection is to use the Session:

use Gt\Csrf\SessionTokenStore;

// $session is an object-oriented representation of $_SESSION
// that implements the Gt\Session\SessionContainer Interface.
$tokenStore = new SessionTokenStore($session);

Step 2: Verify

Before running any other code (especially things that could affect data), you should check to make sure that there's a valid CSRF token in place if it's needed:

use Gt\Csrf\Exception\CSRFException;

if(this_is_a_post_request()) {
	try {
		$tokenStore->verify();
	}
	catch(CSRFException $e) {
// Stop processing this request and get out of there!
	}
}

If the request contains a POST and there is no valid CSRF token, a CSRFException will be thrown — so you should plan to catch it. Remember, if that happens, the request was fraudulent, so you shouldn't process it!

Step 3: Inject for Next Time

Finally, once you've finished processing your html code and it's ready to send back to the client, you should inject the CSRF tokens. If you don't, the request will fail to pass Step 2 when the page gets submitted!

use Gt\Csrf\HTMLDocumentProtector;

// The html can come in as anything accepted by Gt\Dom\HTMLDocument - here it's a
// plain string in a variable.
$html = "<html>...</html>";

// Now do the processing.
$protector = new HTMLDocumentProtector($html, $tokenStore);
$protector->protect();

// Output the HTML of the document - you will see the new fields have
// been automatically injected.
echo $protector->getHTMLDocument();

Using tokens of a different lengths

By default, 32 character tokens are generated. They use characters from the set [a-zA-Z0-9], meaning a 64-bit token which would take a brute-force attacker making 100,000 requests per second around 2.93 million years to guess. If this seems either excessive or inadequate you can change the token length using TokenStore::setTokenLength().

Special note about client-side requests

Note that if there are several forms on your page, a unique token will be generated and injected into each form. When a form is submitted using a client-side request (XMLHTTPRequest or Fetch, a.k.a. AJAX), the response will contain a new token that must be refreshed in the page ready for the next submission.

If you would prefer to have one token per page, shared across all forms, this can be configured by passing in the TOKEN_PER_PAGE parameter to the projectAndInject method: $page->protectAndInject(HTMLDocumentProtector::TOKEN_PER_PAGE);.

Storing one token per page will reduce the amount of server resources required, but concurrent client-side requests will fail, which is why one token per form is the default.

Alternatives to storing tokens on the session

The package includes an ArrayTokenStore, which can be stored on the session. You can implement alternative token stores such as a RDBMS or NoSQL by subclassing TokenStore and implementing the abstract methods.

csrf's People

Contributors

dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar g105b avatar j4m3s avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

csrf's Issues

Limit the memory footprint on busy sites

Spawned from #16.

On busy sites with many forms per page, long session timeouts and many users the amount of memory used for storing all these tokens could become significant.

Obvious options to resolve include a timeout or a limit to the number of tokens in circulation per user.

The timeout is not optimal because it would have to be long (2 hours?) to allow users to take a call or be part way through filling out a form then doing something else etc

The fixed number of tokens seems more useful as it can be tuned to an appropriate depth for a given application - a number much smaller than the total number of tokens generated over a period of hours. HOWEVER, should a user leave one tab open then start working on another they could invalidate the tokens associated with the first page. This could be mitigated by setting the limit to a fairly high number (1,000?) but the risk would remain.

Thoughts @g105b?

Use inbuilt crypto features of PHP 7.2

PHP 7.2 ships with inbuilt crypto features, such as cryptographically secure random byte generator (https://www.php.net/manual/en/function.random-bytes.php)

There is no need to depend on ircmaxel's fantastic library any more, and as such, we will no longer have to depend on packages such as:

    "symfony/console": "^3.4.17 || ^4.1.6",
    "symfony/event-dispatcher": "^3.0 || ^4.0",
    "symfony/filesystem": "^3.0 || ^4.0",
    "symfony/finder": "^3.0 || ^4.0",
    "symfony/options-resolver": "^3.0 || ^4.0",
    "symfony/polyfill-php70": "^1.0",
    "symfony/polyfill-php72": "^1.4",
    "symfony/process": "^3.0 || ^4.0",
    "symfony/stopwatch": "^3.0 || ^4.0"

Allow DOMDocument usage

I was working on a legacy project for a client that uses the DOMDocument to render the HTML. Using this repo on that project would involve rendering the DOMDocument into a HTMLDocument, then back again.

A few more type checks on $html in the HTMLDocumentProtector's constructor will be beneficial.

DatabaseTokenStore

This would use Gt\Database's QueryBuilder to ensure engine-agnostic queries are generated independent of whatever database engine the developer is using.

One token per form

Rather than one token for all forms, this will allow ajax requests to use the already generated tokens rather than having to generate their own.

Use case: sending token as HTTP header?

When updating parts of the page using JavaScript, rather than having to parse the response for form tags and extract the input elements, it would be really useful to have the tokens provided by HTTP tokens.

  1. Are there any security implications of doing this?
  2. How would responses containing multiple forms be handled?

Add csrf token meta tag to <head>

Currently, javscript would have to parse the token out of a form somewhere in the page (assuming one exists). Adding a specific tag to the head (which is reliably in the same place) would simplify the javascript logic considerably.

Make scrutinizer pass

I can't tell what's going wrong. If you check the history of my circleci pull request you'll see it did pass once... but goodness knows why.

It appears to be failing to consolidate the results of the various steps.

Implement token time-outs

This would reduce the attack surface, countering the fact that far more tokens will be generated to allow AJAX forms to be permitted (feature #14).

At the moment, the ArrayTokenStore remains in-play for as long as the session survives.

@g105b any thoughts on the timeout? 30 mins maybe? Don't want to frustrate people who take a phone-call or take a long time filling out a form before they click a button.

Inappropriate copyright notice?

As an open source project I don't think it's right to mark code as copyright Brightflair Ltd - that's simply not the case for code written by others and will discourage contributions :)

One token per form by default?

There are so many instances where multiple forms are output to the same response that I think it would be beneficial to have the PER_FORM flag as default.

However, this is still up for the decision of the implementor, in which case WebEngine can make this choice.

Leaving this issue here while I ponder, or if anyone else wants to share their thoughts.

"HTMLDocument" clash?

There is already the HTMLDocument within the DOM, so having a class of this name within the csrf package might be confusing?

SessionTokenStore

Idea of an extra TokenStore type that abstracts the need to get, set and check the session variables yourself.

TokenStore::verify does not need to return bool

It is never going to return false, as it should throw an exception if the token is invalid.

Leaving the bool return type could lead to confusion in how new implementations of TokenStore should behave.

One token per form

Rather than one token for all forms, this will allow ajax requests to use the already generated tokens rather than having to generate their own.

Allow the length of token to be altered/ specified

Currently it's set at 64 characters, and there is no way for users to change it. Frankly, 6 characters would probably be more than sufficient and considerably quicker (though it's pretty snappy already...)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.