Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gokhankurtulus committed Jun 18, 2023
0 parents commit 816a4a3
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
* text=auto

/.gitattributes export-ignore
/.gitignore export-ignore
/README.md export-ignore
/examples export-ignore
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
vendor
composer.lock
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Gökhan Kurtuluş

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
108 changes: 108 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Csrf

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
![PHP Version](https://img.shields.io/badge/PHP-7.1%2B-blue.svg)
![Release](https://img.shields.io/github/v/release/gokhankurtulus/csrf.svg)

A simple PHP CSRF class that provides functionality for operating CSRF tokens.

## Installation

You can install the Csrf class using [Composer](https://getcomposer.org/). Run the following command in your project's root directory:

```bash
composer require gokhankurtulus/csrf
```

## Usage

To use the Csrf class in your PHP script, you need to include the Composer autoloader:

```php
require_once 'vendor/autoload.php';
```

### Creating a New Token

You can generate a new CSRF token using the `newToken` method. The method accepts two parameters: the token name and an optional expiry time in seconds (default is 600 seconds = 10 minutes).

```php
use Csrf\Csrf;

$token = Csrf::newToken('my_token', 1200); // Generate a token named 'my_token' that expires in 20 minutes
```

The `newToken` method returns a `stdClass` object containing the token information. The object has the following properties:

- `name`: The name of the token.
- `expiry`: The expiry timestamp of the token.
- `value`: The token value.

### Getting a Token

To retrieve a previously generated token, you can use the `getToken` method. It accepts the token name as a parameter and returns the token object if found, or `null` if the token does not exist.

```php
$token = Csrf::getToken('my_token'); // Get the token object for 'my_token'
```

### Creating an HTML Input Field

The `createInput` method generates an HTML input field with the CSRF token embedded. It accepts the token name and an optional expiry time (default is 600 seconds or 10 minutes) as parameters. The
method returns the HTML input field as a string or `null` if the session is not started or the token name is empty.

```php
$input = Csrf::createInput('my_token', 1800); // Generate an HTML input field for 'my_token' that expires in 30 minutes
echo $input; // Output the HTML input field
```

The generated HTML input field can be used in forms to send the CSRF token value along with other form data.

### Verifying a Token

To verify if a submitted token is valid, you can use the `verify` method. It accepts the token name, an optional parameter to unset the token if it is verified (default is `false`), and the token
value submitted with the request (can be retrieved from the `$_POST` superglobal by default).

```php
$isVerified = Csrf::verify('my_token', true); // Verify the submitted token for 'my_token' and unset it if verified
if ($isVerified) {
// Token is valid
} else {
// Token is invalid
}
```

The `verify` method returns a boolean value indicating whether the token is valid or not.

### Unsetting a Token

To unset a token manually, you can use the `unsetToken` method. It accepts the token name as a parameter and returns `true` if the token is successfully unset or `false` if the session is not started
or the token name is empty.

```php
Csrf::unsetToken('my_token'); // Unset the token named 'my_token'
```

## Session Status

The Csrf class relies on PHP sessions to store and retrieve CSRF tokens. The `isSessionStarted` method can be used to check if a session is already started.

```php
$isSessionStarted = Csrf::isSessionStarted();

// Check if a session is started
if ($isSessionStarted) {
// Session is active
} else {
// Session is not active
}
```

## License

Csrf is open-source software released under the [MIT License](LICENSE). Feel free to modify and use it in your projects.

## Contributions

Contributions to Csrf are welcome! If you find any issues or have suggestions for improvements, please create an issue or submit a pull request on
the [GitHub repository](https://github.com/gokhankurtulus/csrf).
27 changes: 27 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "gokhankurtulus/csrf",
"description": "A simple PHP CSRF class that provides functionality for operating CSRF tokens.",
"type": "library",
"keywords": [
"csrf"
],
"license": "MIT",
"authors": [
{
"name": "Gökhan Kurtuluş",
"email": "[email protected]",
"homepage": "https://github.com/gokhankurtulus"
}
],
"autoload": {
"psr-4": {
"Csrf\\": "src/"
}
},
"require": {
"php": "^7.1 || ^8.0"
},
"config": {
"preferred-install": "dist"
}
}
47 changes: 47 additions & 0 deletions examples/example.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
/**
* @author Gökhan Kurtuluş @gokhankurtulus
* Date: 18.06.2023 Time: 09:07
*/

use Csrf\Csrf;

require_once '../vendor/autoload.php';
session_start();

// Create a new token and generate the form
$tokenName = 'csrf_token';
$formToken = Csrf::createInput($tokenName);

echo '<pre>';
var_dump(['session' => $_SESSION, 'post' => $_POST]);
echo '</pre>';

// Verify the token when the form is submitted
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$sentToken = $_POST[$tokenName] ?? null;
$isValid = Csrf::verify($tokenName, true, $sentToken);

if ($isValid) {
echo 'Token verified!';
} else {
echo 'Token verification failed!';
}
}
echo '<pre>';
var_dump(['session' => $_SESSION, 'post' => $_POST]);
echo '</pre>';
?>

<!DOCTYPE html>
<html>
<head>
<title>CSRF Example</title>
</head>
<body>
<form method="POST" action="">
<?php echo $formToken; ?>
<input type="submit" value="Submit">
</form>
</body>
</html>
119 changes: 119 additions & 0 deletions src/Csrf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
/**
* @author Gökhan Kurtuluş @gokhankurtulus
* Date: 18.06.2023 Time: 09:05
*/

namespace Csrf;
class Csrf
{
/**
* @param string $name
* @param int $expiry default 600 = 10mins
* @return \stdClass|null
*/
public static function newToken(string $name, int $expiry = 600): ?\stdClass
{
if (!self::isSessionStarted()) {
return null;
}

$token = new \stdClass();
$token->name = $name;
$token->expiry = time() + $expiry;
$token->value = md5(uniqid(mt_rand(), true));

$_SESSION['tokens'][$name] = $token;
return $token;
}

/**
* @param string $name
* @return \stdClass|null
*/
public static function getToken(string $name): ?\stdClass
{
if (!self::isSessionStarted()) {
return null;
}

return $_SESSION['tokens'][$name] ?? null;
}

public static function isSessionStarted(): bool
{
if (session_status() === PHP_SESSION_ACTIVE) {
return true;
}

return false;
}

/**
* @param string $name
* @param int $expiry default 600 = 10mins
* @return string|null
*/
public static function createInput(string $name, int $expiry = 600): ?string
{
if (!self::isSessionStarted() || empty($name)) {
return null;
}

$token = self::getToken($name) ?? self::newToken($name, $expiry);

if (time() > $token->expiry) {
$token = self::newToken($name, $expiry);
}

return sprintf('<input type="hidden" id="token" name="%s" value="%s">', $name, $token->value);
}

/**
* @param string $name
* @param bool $unsetTokenIfVerified
* @param string|null $sentToken
* @return bool
*/
public static function verify(string $name, bool $unsetTokenIfVerified = false, ?string $sentToken = null): bool
{
if (!self::isSessionStarted() || empty($name)) {
return false;
}

$sentToken = $sentToken ?? ($_POST['token'] ?? null);
if (empty($sentToken)) {
return false;
}

$token = self::getToken($name);
if (empty($token)) {
return false;
}

if (time() > $token->expiry) {
self::unsetToken($name);
return false;
}

if ($unsetTokenIfVerified && $token->value === $sentToken) {
self::unsetToken($name);
}

return $token->value === $sentToken;
}

/**
* @param string $name
* @return bool
*/
public static function unsetToken(string $name): bool
{
if (!self::isSessionStarted() || empty($name)) {
return false;
}

unset($_SESSION['tokens'][$name]);
return true;
}
}

0 comments on commit 816a4a3

Please sign in to comment.