1
0
mirror of https://github.com/delight-im/PHP-Auth.git synced 2025-08-08 09:06:29 +02:00

11 Commits

Author SHA1 Message Date
Marco
ab55f7e562 Explain how to manage session data conveniently 2016-07-20 00:37:48 +02:00
Marco
e768243798 Update dependencies 2016-07-20 00:36:18 +02:00
Marco
27a3990472 Only throttle login attempts that have wrong credentials 2016-07-09 00:58:04 +02:00
Marco
1979799480 Improve cookie handling to fix domain issues and add same-site flag 2016-07-09 00:48:55 +02:00
Marco
fff4a59be5 Include 'delight-im/cookie' as dependency for cookie handling 2016-07-09 00:14:51 +02:00
Marco
122e2b8006 Add missing '+' 2016-06-07 19:04:31 +02:00
Marco
bebd3efce2 Use Composer autoloader for tests as well 2016-06-07 19:01:56 +02:00
Marco
00dc8c3158 Enable both error reporting and assertions in 'tests' 2016-06-07 18:48:50 +02:00
Marco
e074474955 Fix list of dependencies 2016-06-07 18:42:29 +02:00
Marco
44907f3489 Add 'NOTICE' 2016-04-02 22:25:14 +02:00
Marco
ea27e8b7d3 Remove trailing newline in 'LICENSE' 2016-04-02 22:24:51 +02:00
8 changed files with 165 additions and 53 deletions

5
.gitignore vendored
View File

@@ -1 +1,6 @@
# IntelliJ
.idea/ .idea/
# Composer
vendor/
composer.phar

View File

@@ -199,4 +199,3 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.

5
NOTICE Normal file
View File

@@ -0,0 +1,5 @@
PHP-Auth
Copyright (c) delight.im <info@delight.im>
This product includes software developed by
delight.im (http://www.delight.im/).

View File

@@ -17,8 +17,8 @@ Completely framework-agnostic and database-agnostic.
## Requirements ## Requirements
* PHP 5.5.0+ * PHP 5.5.0+
* PDO * OpenSSL extension
* OpenSSL * MySQL 5.5.3+ **or** MariaDB 5.5.23+
## Installation ## Installation
@@ -222,6 +222,10 @@ If the user is not currently signed in, this returns `null`.
$ip = $auth->getIpAddress(); $ip = $auth->getIpAddress();
``` ```
### Read and write session data
For detailed information on how to read and write session data conveniently, please refer to [the documentation of the session library](https://github.com/delight-im/PHP-Cookie), which is included by default.
### Utilities ### Utilities
#### Create a random string #### Create a random string
@@ -254,8 +258,13 @@ $uuid = \Delight\Auth\Auth::createUuid();
* logout * logout
* full and reliable destruction of session * full and reliable destruction of session
* session management * session management
* protection against session hijacking * protection against session hijacking via cross-site scripting (XSS)
* do *not* permit script-based access to cookies
* restrict cookies to HTTPS to prevent session hijacking via non-secure HTTP
* protection against session fixation attacks * protection against session fixation attacks
* protection against cross-site request forgery (CSRF)
* works automatically (i.e. no need for CSRF tokens everywhere)
* do *not* use HTTP `GET` requests for "dangerous" operations
* throttling * throttling
* per IP address * per IP address
* per account * per account

View File

@@ -2,7 +2,9 @@
"name": "delight-im/auth", "name": "delight-im/auth",
"description": "Authentication for PHP. Simple, lightweight and secure.", "description": "Authentication for PHP. Simple, lightweight and secure.",
"require": { "require": {
"php": ">=5.5.0" "php": ">=5.5.0",
"ext-openssl": "*",
"delight-im/cookie": "^1.3"
}, },
"type": "library", "type": "library",
"keywords": [ "auth", "authentication", "login", "security" ], "keywords": [ "auth", "authentication", "login", "security" ],

99
composer.lock generated Normal file
View File

@@ -0,0 +1,99 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "32e4912115e164a1a86227d49db9ac29",
"content-hash": "1a5c0056d726ae6195da0faa38f37fdd",
"packages": [
{
"name": "delight-im/cookie",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/delight-im/PHP-Cookie.git",
"reference": "481c569d6f4bcb0391f56203f078d425b3339001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/delight-im/PHP-Cookie/zipball/481c569d6f4bcb0391f56203f078d425b3339001",
"reference": "481c569d6f4bcb0391f56203f078d425b3339001",
"shasum": ""
},
"require": {
"delight-im/http": "^1.1",
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Delight\\Cookie\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"description": "Modern cookie management for PHP",
"homepage": "https://github.com/delight-im/PHP-Cookie",
"keywords": [
"cookie",
"cookies",
"csrf",
"http",
"same-site",
"samesite",
"xss"
],
"time": "2016-07-19 22:20:24"
},
{
"name": "delight-im/http",
"version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/delight-im/PHP-HTTP.git",
"reference": "2ca9001f047c8b4e1b7ca7281823a1a9437850f8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/delight-im/PHP-HTTP/zipball/2ca9001f047c8b4e1b7ca7281823a1a9437850f8",
"reference": "2ca9001f047c8b4e1b7ca7281823a1a9437850f8",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Delight\\Http\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"description": "Hypertext Transfer Protocol (HTTP) utilities for PHP",
"homepage": "https://github.com/delight-im/PHP-HTTP",
"keywords": [
"headers",
"http",
"https"
],
"time": "2016-07-08 21:19:02"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.5.0",
"ext-openssl": "*"
},
"platform-dev": []
}

View File

@@ -85,7 +85,7 @@ class Auth {
session_set_cookie_params($params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']); session_set_cookie_params($params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']);
// start the session // start the session
@session_start(); @\Delight\Cookie\Session::start();
} }
/** Improves the application's security over HTTP(S) by setting specific headers */ /** Improves the application's security over HTTP(S) by setting specific headers */
@@ -266,9 +266,6 @@ class Auth {
* @throws AuthError if an internal problem occurred (do *not* catch) * @throws AuthError if an internal problem occurred (do *not* catch)
*/ */
public function login($email, $password, $remember = false) { public function login($email, $password, $remember = false) {
$this->throttle(self::THROTTLE_ACTION_LOGIN);
$this->throttle(self::THROTTLE_ACTION_LOGIN, $email);
$email = isset($email) ? trim($email) : null; $email = isset($email) ? trim($email) : null;
if (empty($email)) { if (empty($email)) {
throw new InvalidEmailException(); throw new InvalidEmailException();
@@ -308,10 +305,16 @@ class Auth {
} }
} }
else { else {
$this->throttle(self::THROTTLE_ACTION_LOGIN);
$this->throttle(self::THROTTLE_ACTION_LOGIN, $email);
throw new InvalidPasswordException(); throw new InvalidPasswordException();
} }
} }
else { else {
$this->throttle(self::THROTTLE_ACTION_LOGIN);
$this->throttle(self::THROTTLE_ACTION_LOGIN, $email);
throw new InvalidEmailException(); throw new InvalidEmailException();
} }
} }
@@ -388,7 +391,18 @@ class Auth {
} }
// set the cookie with the selector and token // set the cookie with the selector and token
$result = setcookie(self::COOKIE_NAME_REMEMBER, $content, $expires, $params['path'], $params['domain'], $params['secure'], $params['httponly']); $cookie = new \Delight\Cookie\Cookie(self::COOKIE_NAME_REMEMBER);
$cookie->setValue($content);
$cookie->setExpiryTime($expires);
if (!empty($params['path'])) {
$cookie->setPath($params['path']);
}
if (!empty($params['domain'])) {
$cookie->setDomain($params['domain']);
}
$cookie->setHttpOnly($params['httponly']);
$cookie->setSecureOnly($params['secure']);
$result = $cookie->save();
if ($result === false) { if ($result === false) {
throw new HeadersAlreadySentError(); throw new HeadersAlreadySentError();
@@ -410,7 +424,7 @@ class Auth {
$stmt->execute(); $stmt->execute();
// re-generate the session ID to prevent session fixation attacks // re-generate the session ID to prevent session fixation attacks
session_regenerate_id(true); \Delight\Cookie\Session::regenerate(true);
// save the user data in the session // save the user data in the session
$this->setLoggedIn(true); $this->setLoggedIn(true);
@@ -456,8 +470,17 @@ class Auth {
// get our cookie settings // get our cookie settings
$params = $this->createCookieSettings(); $params = $this->createCookieSettings();
// set the cookie with the selector and token // cause the session cookie to be deleted
$result = setcookie(session_name(), '', time() - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']); $cookie = new \Delight\Cookie\Cookie(session_name());
if (!empty($params['path'])) {
$cookie->setPath($params['path']);
}
if (!empty($params['domain'])) {
$cookie->setDomain($params['domain']);
}
$cookie->setHttpOnly($params['httponly']);
$cookie->setSecureOnly($params['secure']);
$result = $cookie->delete();
if ($result === false) { if ($result === false) {
throw new HeadersAlreadySentError(); throw new HeadersAlreadySentError();
@@ -829,8 +852,6 @@ class Auth {
// get the default cookie settings // get the default cookie settings
$params = session_get_cookie_params(); $params = session_get_cookie_params();
// optimize the cookie domain
$params['domain'] = self::optimizeCookieDomain($params['domain']);
// check if we want to send cookies via SSL/TLS only // check if we want to send cookies via SSL/TLS only
$params['secure'] = $params['secure'] || $this->useHttps; $params['secure'] = $params['secure'] || $this->useHttps;
// check if we want to send cookies via HTTP(S) only // check if we want to send cookies via HTTP(S) only
@@ -840,41 +861,6 @@ class Auth {
return $params; return $params;
} }
/**
* Optimizes the specified cookie domain
*
* @param string $domain the supplied cookie domain
* @return string the optimized cookie domain
*/
private static function optimizeCookieDomain($domain) {
// if no domain has been explicitly provided
if (empty($domain)) {
// use the current hostname as a default
$domain = $_SERVER['SERVER_NAME'];
}
// if the domain name starts with the `www` subdomain
if (substr($domain, 0, 4) === 'www.') {
// strip the subdomain
$domain = substr($domain, 4);
}
// count the dots in the domain name
$numDots = substr_count($domain, '.');
// if there is no dot at all (usually `localhost`) or only a single dot (no subdomain)
if ($numDots < 2) {
// if the domain doesn't already start with a dot
if (substr($domain, 0, 1) !== '.') {
// prepend a dot to allow all subdomains
$domain = '.'.$domain;
}
}
// return the optimized domain name
return $domain;
}
/** /**
* Creates a random string with the given maximum length * Creates a random string with the given maximum length
* *

View File

@@ -16,15 +16,22 @@
* limitations under the License. * limitations under the License.
*/ */
header('Content-type: text/html; charset=utf-8'); // enable error reporting
error_reporting(E_ALL); error_reporting(E_ALL);
ini_set('display_errors', 'stdout'); ini_set('display_errors', 'stdout');
// enable assertions
ini_set('assert.active', 1);
ini_set('zend.assertions', 1);
ini_set('assert.exception', 1);
header('Content-type: text/html; charset=utf-8');
require __DIR__.'/../vendor/autoload.php';
$db = new PDO('mysql:dbname=php_auth;host=127.0.0.1;charset=utf8mb4', 'root', ''); $db = new PDO('mysql:dbname=php_auth;host=127.0.0.1;charset=utf8mb4', 'root', '');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
require __DIR__.'/../src/Auth.php';
$auth = new \Delight\Auth\Auth($db); $auth = new \Delight\Auth\Auth($db);
$result = processRequestData($auth); $result = processRequestData($auth);