1
0
mirror of https://github.com/delight-im/PHP-Auth.git synced 2025-08-10 10:04:05 +02:00

15 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
Marco
354b34a724 Add migration guide for switch from v1.x.x to v2.x.x 2016-02-24 01:00:23 +01:00
Marco
3e083f9f17 Update '.editorconfig' to indent with spaces in Markdown 2016-02-24 01:00:08 +01:00
Marco
227c37e2b5 Update MySQL database schema to ensure full Unicode support 2016-02-24 00:59:50 +01:00
Marco
5403270ed2 Always use 'bindValue' instead of 'bindParam' with PDO 2016-02-24 00:59:18 +01:00
11 changed files with 250 additions and 104 deletions

View File

@@ -7,3 +7,7 @@ indent_style = tab
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
[*.md]
indent_style = space
indent_size = 4

5
.gitignore vendored
View File

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

View File

@@ -1,25 +1,25 @@
CREATE TABLE IF NOT EXISTS `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(254) NOT NULL,
`email` varchar(249) COLLATE utf8mb4_unicode_ci NOT NULL,
`password` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
`username` varchar(100) DEFAULT NULL,
`username` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`verified` tinyint(1) unsigned NOT NULL DEFAULT '0',
`registered` int(10) unsigned NOT NULL,
`last_login` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `users_confirmations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(254) NOT NULL,
`email` varchar(249) COLLATE utf8mb4_unicode_ci NOT NULL,
`selector` varchar(16) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
`token` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
`expires` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `selector` (`selector`),
KEY `email_expires` (`email`,`expires`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `users_remembered` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
@@ -30,7 +30,7 @@ CREATE TABLE IF NOT EXISTS `users_remembered` (
PRIMARY KEY (`id`),
UNIQUE KEY `selector` (`selector`),
KEY `user` (`user`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `users_resets` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
@@ -41,14 +41,14 @@ CREATE TABLE IF NOT EXISTS `users_resets` (
PRIMARY KEY (`id`),
UNIQUE KEY `selector` (`selector`),
KEY `user` (`user`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `users_throttling` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`action_type` enum('login','register','confirm_email') NOT NULL,
`action_type` enum('login','register','confirm_email') COLLATE utf8mb4_unicode_ci NOT NULL,
`selector` varchar(44) CHARACTER SET latin1 COLLATE latin1_general_cs DEFAULT NULL,
`time_bucket` int(10) unsigned NOT NULL,
`attempts` mediumint(8) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id`),
UNIQUE KEY `action_type_selector_time_bucket` (`action_type`,`selector`,`time_bucket`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View File

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

35
Migration.md Normal file
View File

@@ -0,0 +1,35 @@
# Migration
* `v1.x.x` to `v2.x.x`
* The MySQL schema has been changed from charset `utf8` to charset `utf8mb4` and from collation `utf8_general_ci` to collation `utf8mb4_unicode_ci`. Use the statements below to update the database schema:
```sql
ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
-- ALTER DATABASE `<DATABASE_NAME>` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE `users` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users_confirmations` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users_remembered` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users_resets` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users_throttling` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
ALTER TABLE `users` CHANGE `username` `username` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL;
ALTER TABLE `users_confirmations` CHANGE `email` `email` VARCHAR(249) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
ALTER TABLE `users_throttling` CHANGE `action_type` `action_type` ENUM('login','register','confirm_email') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;
REPAIR TABLE users;
OPTIMIZE TABLE users;
REPAIR TABLE users_confirmations;
OPTIMIZE TABLE users_confirmations;
REPAIR TABLE users_remembered;
OPTIMIZE TABLE users_remembered;
REPAIR TABLE users_resets;
OPTIMIZE TABLE users_resets;
REPAIR TABLE users_throttling;
OPTIMIZE TABLE users_throttling;
```

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
* PHP 5.5.0+
* PDO
* OpenSSL
* OpenSSL extension
* MySQL 5.5.3+ **or** MariaDB 5.5.23+
## Installation
@@ -43,7 +43,7 @@ Completely framework-agnostic and database-agnostic.
### Create a new instance
```php
// $db = new PDO('mysql:dbname=database;host=localhost;charset=utf8', 'username', 'password');
// $db = new PDO('mysql:dbname=database;host=localhost;charset=utf8mb4', 'username', 'password');
// $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$auth = new \Delight\Auth\Auth($db);
@@ -222,6 +222,10 @@ If the user is not currently signed in, this returns `null`.
$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
#### Create a random string
@@ -254,8 +258,13 @@ $uuid = \Delight\Auth\Auth::createUuid();
* logout
* full and reliable destruction of session
* 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 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
* per IP address
* per account

View File

@@ -2,7 +2,9 @@
"name": "delight-im/auth",
"description": "Authentication for PHP. Simple, lightweight and secure.",
"require": {
"php": ">=5.5.0"
"php": ">=5.5.0",
"ext-openssl": "*",
"delight-im/cookie": "^1.3"
},
"type": "library",
"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']);
// start the session
@session_start();
@\Delight\Cookie\Session::start();
}
/** Improves the application's security over HTTP(S) by setting specific headers */
@@ -118,7 +118,7 @@ class Auth {
// if both selector and token were found
if (isset($parts[0]) && isset($parts[1])) {
$stmt = $this->db->prepare("SELECT a.user, a.token, a.expires, b.email, b.username FROM users_remembered AS a JOIN users AS b ON a.user = b.id WHERE a.selector = :selector");
$stmt->bindParam(':selector', $parts[0], \PDO::PARAM_STR);
$stmt->bindValue(':selector', $parts[0], \PDO::PARAM_STR);
if ($stmt->execute()) {
$rememberData = $stmt->fetch(\PDO::FETCH_ASSOC);
if ($rememberData !== false) {
@@ -168,16 +168,15 @@ class Auth {
}
$username = isset($username) ? trim($username) : null;
$registered = time();
$password = password_hash($password, PASSWORD_DEFAULT);
$verified = isset($emailConfirmationCallback) && is_callable($emailConfirmationCallback) ? 0 : 1;
$stmt = $this->db->prepare("INSERT INTO users (email, password, username, verified, registered) VALUES (:email, :password, :username, :verified, :registered)");
$stmt->bindParam(':email', $email, \PDO::PARAM_STR);
$stmt->bindParam(':password', $password, \PDO::PARAM_STR);
$stmt->bindParam(':username', $username, \PDO::PARAM_STR);
$stmt->bindParam(':verified', $verified, \PDO::PARAM_INT);
$stmt->bindParam(':registered', $registered, \PDO::PARAM_INT);
$stmt->bindValue(':email', $email, \PDO::PARAM_STR);
$stmt->bindValue(':password', $password, \PDO::PARAM_STR);
$stmt->bindValue(':username', $username, \PDO::PARAM_STR);
$stmt->bindValue(':verified', $verified, \PDO::PARAM_INT);
$stmt->bindValue(':registered', time(), \PDO::PARAM_INT);
try {
$result = $stmt->execute();
@@ -198,7 +197,7 @@ class Auth {
if ($result) {
// get the ID of the user that we've just created
$stmt = $this->db->prepare("SELECT id FROM users WHERE email = :email");
$stmt->bindParam(':email', $email, \PDO::PARAM_STR);
$stmt->bindValue(':email', $email, \PDO::PARAM_STR);
if ($result = $stmt->execute()) {
$newUserId = $stmt->fetchColumn();
@@ -235,10 +234,10 @@ class Auth {
$expires = time() + 3600 * 24;
$stmt = $this->db->prepare("INSERT INTO users_confirmations (email, selector, token, expires) VALUES (:email, :selector, :token, :expires)");
$stmt->bindParam(':email', $email, \PDO::PARAM_STR);
$stmt->bindParam(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindParam(':token', $tokenHashed, \PDO::PARAM_STR);
$stmt->bindParam(':expires', $expires, \PDO::PARAM_INT);
$stmt->bindValue(':email', $email, \PDO::PARAM_STR);
$stmt->bindValue(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindValue(':token', $tokenHashed, \PDO::PARAM_STR);
$stmt->bindValue(':expires', $expires, \PDO::PARAM_INT);
if ($stmt->execute()) {
if (isset($emailConfirmationCallback) && is_callable($emailConfirmationCallback)) {
@@ -267,9 +266,6 @@ class Auth {
* @throws AuthError if an internal problem occurred (do *not* catch)
*/
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;
if (empty($email)) {
throw new InvalidEmailException();
@@ -284,7 +280,7 @@ class Auth {
}
$stmt = $this->db->prepare("SELECT id, password, verified, username FROM users WHERE email = :email");
$stmt->bindParam(':email', $email, \PDO::PARAM_STR);
$stmt->bindValue(':email', $email, \PDO::PARAM_STR);
if ($stmt->execute()) {
$userData = $stmt->fetch(\PDO::FETCH_ASSOC);
if ($userData !== false) {
@@ -309,10 +305,16 @@ class Auth {
}
}
else {
$this->throttle(self::THROTTLE_ACTION_LOGIN);
$this->throttle(self::THROTTLE_ACTION_LOGIN, $email);
throw new InvalidPasswordException();
}
}
else {
$this->throttle(self::THROTTLE_ACTION_LOGIN);
$this->throttle(self::THROTTLE_ACTION_LOGIN, $email);
throw new InvalidEmailException();
}
}
@@ -334,10 +336,10 @@ class Auth {
$expires = time() + 3600 * 24 * 28;
$stmt = $this->db->prepare("INSERT INTO users_remembered (user, selector, token, expires) VALUES (:user, :selector, :token, :expires)");
$stmt->bindParam(':user', $userId, \PDO::PARAM_INT);
$stmt->bindParam(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindParam(':token', $tokenHashed, \PDO::PARAM_STR);
$stmt->bindParam(':expires', $expires, \PDO::PARAM_INT);
$stmt->bindValue(':user', $userId, \PDO::PARAM_INT);
$stmt->bindValue(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindValue(':token', $tokenHashed, \PDO::PARAM_STR);
$stmt->bindValue(':expires', $expires, \PDO::PARAM_INT);
if ($stmt->execute()) {
$this->setRememberCookie($selector, $token, $expires);
@@ -357,7 +359,7 @@ class Auth {
*/
private function deleteRememberDirective($userId) {
$stmt = $this->db->prepare("DELETE FROM users_remembered WHERE user = :user");
$stmt->bindParam(':user', $userId, \PDO::PARAM_INT);
$stmt->bindValue(':user', $userId, \PDO::PARAM_INT);
if ($stmt->execute()) {
$this->setRememberCookie(null, null, time() - 3600);
@@ -389,7 +391,18 @@ class Auth {
}
// 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) {
throw new HeadersAlreadySentError();
@@ -405,15 +418,13 @@ class Auth {
* @param bool $remembered whether the user was remembered ("remember me") or logged in actively
*/
private function onLoginSuccessful($userId, $email, $username, $remembered) {
$lastLogin = time();
$stmt = $this->db->prepare("UPDATE users SET last_login = :lastLogin WHERE id = :id");
$stmt->bindParam(':lastLogin', $lastLogin, \PDO::PARAM_INT);
$stmt->bindParam(':id', $userId, \PDO::PARAM_INT);
$stmt->bindValue(':lastLogin', time(), \PDO::PARAM_INT);
$stmt->bindValue(':id', $userId, \PDO::PARAM_INT);
$stmt->execute();
// 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
$this->setLoggedIn(true);
@@ -459,8 +470,17 @@ class Auth {
// get our cookie settings
$params = $this->createCookieSettings();
// set the cookie with the selector and token
$result = setcookie(session_name(), '', time() - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
// cause the session cookie to be deleted
$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) {
throw new HeadersAlreadySentError();
@@ -483,20 +503,18 @@ class Auth {
$this->throttle(self::THROTTLE_ACTION_CONFIRM_EMAIL, $selector);
$stmt = $this->db->prepare("SELECT id, email, token, expires FROM users_confirmations WHERE selector = :selector");
$stmt->bindParam(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindValue(':selector', $selector, \PDO::PARAM_STR);
if ($stmt->execute()) {
$confirmationData = $stmt->fetch(\PDO::FETCH_ASSOC);
if ($confirmationData !== false) {
if (password_verify($token, $confirmationData['token'])) {
if ($confirmationData['expires'] >= time()) {
$verified = 1;
$stmt = $this->db->prepare("UPDATE users SET verified = :verified WHERE email = :email");
$stmt->bindParam(':verified', $verified, \PDO::PARAM_INT);
$stmt->bindParam(':email', $confirmationData['email'], \PDO::PARAM_STR);
$stmt->bindValue(':verified', 1, \PDO::PARAM_INT);
$stmt->bindValue(':email', $confirmationData['email'], \PDO::PARAM_STR);
if ($stmt->execute()) {
$stmt = $this->db->prepare("DELETE FROM users_confirmations WHERE id = :id");
$stmt->bindParam(':id', $confirmationData['id'], \PDO::PARAM_INT);
$stmt->bindValue(':id', $confirmationData['id'], \PDO::PARAM_INT);
if ($stmt->execute()) {
return;
}
@@ -549,7 +567,7 @@ class Auth {
$userId = $this->getUserId();
$stmt = $this->db->prepare("SELECT password FROM users WHERE id = :userId");
$stmt->bindParam(':userId', $userId, \PDO::PARAM_INT);
$stmt->bindValue(':userId', $userId, \PDO::PARAM_INT);
if ($stmt->execute()) {
$passwordInDatabase = $stmt->fetchColumn();
if (password_verify($oldPassword, $passwordInDatabase)) {
@@ -580,8 +598,8 @@ class Auth {
$newPassword = password_hash($newPassword, PASSWORD_DEFAULT);
$stmt = $this->db->prepare("UPDATE users SET password = :password WHERE id = :userId");
$stmt->bindParam(':password', $newPassword, \PDO::PARAM_STR);
$stmt->bindParam(':userId', $userId, \PDO::PARAM_INT);
$stmt->bindValue(':password', $newPassword, \PDO::PARAM_STR);
$stmt->bindValue(':userId', $userId, \PDO::PARAM_INT);
$stmt->execute();
}
@@ -767,9 +785,9 @@ class Auth {
$timeBucket = self::getTimeBucket();
$stmt = $this->db->prepare('INSERT INTO users_throttling (action_type, selector, time_bucket, attempts) VALUES (:actionType, :selector, :timeBucket, 1)');
$stmt->bindParam(':actionType', $actionType, \PDO::PARAM_STR);
$stmt->bindParam(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindParam(':timeBucket', $timeBucket, \PDO::PARAM_INT);
$stmt->bindValue(':actionType', $actionType, \PDO::PARAM_STR);
$stmt->bindValue(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindValue(':timeBucket', $timeBucket, \PDO::PARAM_INT);
try {
$stmt->execute();
}
@@ -778,9 +796,9 @@ class Auth {
if ($e->getCode() == '23000') {
// update the old entry
$stmt = $this->db->prepare('UPDATE users_throttling SET attempts = attempts+1 WHERE action_type = :actionType AND selector = :selector AND time_bucket = :timeBucket');
$stmt->bindParam(':actionType', $actionType, \PDO::PARAM_STR);
$stmt->bindParam(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindParam(':timeBucket', $timeBucket, \PDO::PARAM_INT);
$stmt->bindValue(':actionType', $actionType, \PDO::PARAM_STR);
$stmt->bindValue(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindValue(':timeBucket', $timeBucket, \PDO::PARAM_INT);
$stmt->execute();
}
// if we have another error
@@ -791,9 +809,9 @@ class Auth {
}
$stmt = $this->db->prepare('SELECT attempts FROM users_throttling WHERE action_type = :actionType AND selector = :selector AND time_bucket = :timeBucket');
$stmt->bindParam(':actionType', $actionType, \PDO::PARAM_STR);
$stmt->bindParam(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindParam(':timeBucket', $timeBucket, \PDO::PARAM_INT);
$stmt->bindValue(':actionType', $actionType, \PDO::PARAM_STR);
$stmt->bindValue(':selector', $selector, \PDO::PARAM_STR);
$stmt->bindValue(':timeBucket', $timeBucket, \PDO::PARAM_INT);
if ($stmt->execute()) {
$attempts = $stmt->fetchColumn();
@@ -834,8 +852,6 @@ class Auth {
// get the default cookie settings
$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
$params['secure'] = $params['secure'] || $this->useHttps;
// check if we want to send cookies via HTTP(S) only
@@ -845,41 +861,6 @@ class Auth {
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
*

View File

@@ -16,14 +16,21 @@
* limitations under the License.
*/
header('Content-type: text/html; charset=utf-8');
// enable error reporting
error_reporting(E_ALL);
ini_set('display_errors', 'stdout');
$db = new PDO('mysql:dbname=php_auth;host=127.0.0.1;charset=utf8', 'root', '');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// enable assertions
ini_set('assert.active', 1);
ini_set('zend.assertions', 1);
ini_set('assert.exception', 1);
require __DIR__.'/../src/Auth.php';
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->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$auth = new \Delight\Auth\Auth($db);