Lädt...


🔧 Building a Secured User Authentication System with PHP, MySQL, PDO and hashed password


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

Tired of Boring and insecured Logins? Let's ditch the basic sign-up and sign-in systems and build a secure, engaging application with a personalized dashboard using PHP! This tutorial will guide you through creating a user management system with a focus on security best practices and user experience.

What You'll Learn:
How to handle user registration and login.
Securely store passwords using bcrypt hashing.
Implement a basic routing system.
Leverage a service container to manage dependencies.

Prerequisites:

  • Basic knowledge of PHP, HTML forms, and SQL
  • PHP (version 7.4 or higher) installed on your local machine
  • A web server (e.g., Apache, Nginx)
  • A database server (e.g., MySQL)

1. Project Setup:

Begin by creating a new project directory and setting up the basic file structure:

└── 📁App
└── 📁Config
└── 📁Controller
└── 📁Core
└── 📁Helper
└── 📁Model
└── 📁Routes
└── 📁services
└── 📁View
└── 📁Dashboard
     └── 📁Login

**_

  1. Database Configuration (config.php):_** Create a config.php file to store your database credentials and other configuration settings:
<?php

namespace App\Config;

class Config {
    const DB_SERVER = "127.0.0.1";
    const DB_DATABASE = "hack";

    public static $info = [
        "DB_USER" => "root",
        "DB_PASSWORD" => "123456",
        "DB_DSN" => "mysql:host=" . self::DB_SERVER . ";dbname=" . self::DB_DATABASE
    ];
}

3. Database Interaction (models/UserModel.php):
This PHP code below defines a class UserModel within the namespace App\Model. The class is designed to interact with a database to perform operations related to user management, specifically creating and validating users. Here's a breakdown of the principal components and functionalities:


Namespace and Imports:

namespace App\Model;: Declares that the UserModel class is part of the App\Model namespace.

use App\Config\DataBase;: Imports the DataBase class from the App\Config namespace, which presumably handles database connections.

use PDOException;: Imports the PDOException class, which is used to catch exceptions thrown by PDO operations.
Class Definition

class** UserModel**: Defines the UserModel class, which encapsulates methods for interacting with the database regarding user data.

Constructor and Destructor :

The constructor (construct) accepts an instance of DataBase and connects to the database using $this->db::connect();.
The destructor (
destruct) unsets the $db property, effectively closing the database connection when the object is destroyed.
Public Methods :
create($data)
This method attempts to insert a new user into the users table with the provided data ($data). It uses prepared statements to prevent SQL injection.

It checks for a duplicate entry error (code 23000) and returns an error message if the email already exists in the database.
On success, it returns an array indicating success and the ID of the newly inserted user.

validateUser($data):
This method queries the database to find a user with the provided email address.
It uses a prepared statement to safely query the database.
If a user is found, it returns an array indicating success and the user's data.
If no user is found, it returns an array indicating failure.

Error Handling:
Both methods use a try-catch block to catch PDOException exceptions, which are thrown by PDO when there's an error executing a database operation.
In the create method, if a PDOException is caught and its code indicates a duplicate entry error, it returns an error message indicating that the email already exists.

<?php

namespace App\Model;

use App\Config\DataBase;
use PDOException;

class UserModel {

    public function __construct(
        private DataBase $db
    ) {
        $this->db::connect();
    }
    public function __destruct() {
        unset($this->db);
    }

    public function create($data) {
        try {
            $this->db->query(
                "
                INSERT INTO users (full_name, email, dob, gender, password_hash ,salt)
                VALUES(:full_name, :email, :dob, :gender, :password_hash , :salt)
                ",
                [
                    ':full_name' => $data['full_name'],
                    ':email' => $data['email'],
                    ':dob' => $data['dob'],
                    ':gender' => $data['gender'],
                    ':password_hash' => $data['password'],
                    ':salt' => $data['salt']
                ]
            );

            return ['success' => true, 'user_id' => $this->db->lastInsertId()];
        } catch (PDOException $e) {
            if ($e->getCode() == 23000) { // Duplicate entry error
                return [
                    'success' => false,
                    'errors' =>
                    [
                        'email' => ['Email already exists']
                    ]
                ];
            }
        }
    }


    public function validateUser($data) {
        try {
            $this->db->query(
                'SELECT * FROM users WHERE email = :email',
                [
                    ':email' => $data['email']
                ]
            );

            $user = $this->db->fetch(\PDO::FETCH_ASSOC);

            if (!empty($user)) {
                return  ['success' => true, 'user' => $user];
            }

            return  ['success' => false];
        } catch (PDOException $e) {
        }
    }
}

4. Routing (router.php):

what is a Router ?? :

Router: The MVC Traffic Cop

In the MVC (Model-View-Controller) architectural pattern, the router acts as a traffic cop, directing incoming requests to the correct controller action. Think of it as the "switchboard" of your application.
Definition:
The router analyzes the URL of a request and determines which controller and action should handle it. It then passes the request to that controller, allowing the MVC flow to continue.
Importance:
Organizes Request Handling: Without a router, it would be chaotic to handle different types of requests. The router ensures each request goes to the right place.

*Maintainable Code: *
It keeps the code clean and organized by separating URL mapping from application logic.

Flexibility and Scalability:
Routers allow for easy addition of new features and functionalities without major restructuring.
SEO-Friendliness: Routers can create clean, descriptive URLs that are beneficial for Search Engine Optimization (SEO).

<?php

use App\Controller\AuthController;
use App\Controller\DashboardController;
use App\Routes\Router;



Router::get('/register', [AuthController::class, 'showSignupForm']); // signUp view
Router::get('/login', [AuthController::class, 'showLoginForm']); // login view


Router::post('/register', [AuthController::class, 'register']); // POST for signUp
Router::post('/login', [AuthController::class, 'login']); // PUT for login

Router::get('/dashboard', [DashboardController::class, 'index']); // dashboard


Router::get('/logout', [AuthController::class, 'logOut']); // dashboard

Image description

_
**

  1. Dependency Management (Container.php):**_

By using a Dependency Injection Container, you can:
Decouple components by injecting dependencies instead of hardcoding them.
Easily switch implementations of dependencies (e.g., for testing or different environments).
Improve code organization and testability.

<?php

namespace App\Services;


class Container {
    private $bindings = [];

    public function set(string $id, callable $factory): void {
        $this->bindings[$id] = $factory;
    }

    public function get(string $id) {
        if (!isset($this->bindings[$id])) {
            throw new \Exception("Target binding [$id] does not exist.");
        }

        $factory = $this->bindings[$id];

        return $factory($this);
    }
}

This code defines a simple Dependency Injection Container in PHP. Here's a breakdown of the important parts:

1. Namespace:
namespace App\Services;: This line indicates that the code belongs to the namespace App\Services. Namespaces help organize code and prevent naming collisions.
2. Class Definition:
class Container { ... }: This defines a class called Container, which will hold and manage dependencies.
3. Bindings Property:
private $bindings = [];: This is a private property that stores the dependencies. It's an array where keys are dependency "identifiers" and values are "factories" responsible for creating those dependencies.
4. set Method:
public function set(string $id, callable $factory): void: This method registers a dependency.
$id: The unique identifier for the dependency (e.g., 'database', 'logger').
$factory: A callable (function or closure) that knows how to create an instance of the dependency.
5. get Method:
public function get(string $id): This method retrieves a dependency by its identifier.
It checks if the dependency is registered using isset($this->bindings[$id]).
If found, it retrieves the $factory associated with the $id.
It then calls the $factory with the container itself as an argument ($factory($this)), allowing the factory to potentially access other dependencies from the container.
Finally, it returns the created instance of the dependency.
If the dependency is not found, it throws an Exception.

6. Views (views/signup.php, views/login.php):

dashbord view :

This code represents a view that generates a dynamic dashboard page. It highlights several important aspects of views in an MVC application:

``<?php

namespace App\View\Dashboard;

define('SCRIPT_ROOT', 'http://localhost/hackathon');

class DashboardView {

public static function render() {

?>
<!DOCTYPE html>

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <link rel="stylesheet" type="text/css" href="<?php echo SCRIPT_ROOT ?>/public/css/style.css">
        <link rel="stylesheet" type="text/css" href="<?php echo SCRIPT_ROOT ?>/public/css/reset.css">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    </head>

    <div class="main">

        <body>

            <div class="sidebar">
                <ul>
                    <li class="logo">
                        <img src="<?php echo SCRIPT_ROOT ?>/public/images/logo.png">
                    </li>
                    <li>
                        <legend>User Tools</legend>
                    </li>
                    <li><a href="#">
                            <i class="fa-solid fa-table-cells-large"></i>
                            <p>Overview</p>
                        </a></li>
                    <li><a href="#">
                            <i class="fa-solid fa-users"></i>
                            <p>Users</p>
                        </a></li>
                    <li><a href="#">
                            <i class="fa-regular fa-circle-play"></i>
                            <p>Videos</p>
                        </a></li>

                    <li><a href="#">
                            <i class="fa-regular fa-flag"></i>
                            <p>Reports</p>
                        </a></li>
                    <li><a href="#">
                            <i class="fa-solid fa-gear"></i>
                            <p>Setting</p>
                        </a></li>
                    <li class="Logout"><a href="./logout">
                            <i class="fa-solid fa-arrow-right-from-bracket"></i>
                            <p>Logout</p>
                        </a></li>

                </ul>
            </div>
            <div class="navbar">
                <input type="search" placeholder="Search Users...">
                <div class="menu">
                    <img class="profile" src="<?php echo SCRIPT_ROOT ?>/public/images/profile.png">
                    <h2><?= ucfirst($_SESSION['full_name']) ?></h2>
                </div>
            </div>


        </body>
    </div>

    </html>

<?php
}
}
`

`

This code represents a simple view in a PHP MVC application, likely designed for a dashboard. Here's a breakdown:

  1. Namespace: namespace App\View\Dashboard;: Similar to the previous example, this indicates the code belongs to the namespace App\View\Dashboard, likely organizing view-related code.
  2. SCRIPT_ROOT Constant: define('SCRIPT_ROOT', 'http://localhost/hackathon');: This defines a constant SCRIPT_ROOT with the base URL of your application. This constant is used throughout the HTML to build paths to resources like CSS files and images.
  3. DashboardView Class: class DashboardView { ... }: This defines a class DashboardView, which likely encapsulates the logic for generating the HTML of the dashboard.
  4. render Method: public static function render() { ... }: This static method generates the actual HTML content of the dashboard. It's designed to be called from a controller, which would pass any necessary data to populate the view. Key Elements Within the render Method: HTML Structure: The method outputs a standard HTML document structure, including the section with metadata and links to CSS stylesheets, and the section containing the main content. Dynamic Paths: The SCRIPT_ROOT constant is used to create dynamic paths for resources: href="<?php echo SCRIPT_ROOT ?>/public/css/style.css" src="<?php echo SCRIPT_ROOT ?>/public/images/logo.png" This ensures that resources are loaded correctly regardless of where the application is deployed. Sidebar and Navbar: The HTML defines a sidebar () containing navigation links, and a navbar () for search and user information. PHP Integration: There are some PHP snippets embedded within the HTML:

    <?= ucfirst($_SESSION['full_name']) ?>

    : This line likely displays the user's name retrieved from a session variable.

    *singIn view : *

    `
    <?php

    namespace App\View\Login;

    class SignInView {
    public static function render($errors) {
    ob_start();
    ?>


    Welcome! Please enter your details.


    Create an account? Sign Up

        <form method="post" action="./login" class="__input-group">
            <div>
                <div>
                    <span><?= $errors['null'] ?? '' ?></span>
                    <div>
                        <div class="input">
                            <input type="text" id="email" name="email" placeholder=" ">
                            <label for="email">Email</label>
                        </div>
                        <span><?= $errors['email'][0] ?? '' ?></span>
                    </div>
                    <div>
                        <div class="input">
                            <input type="text" id="password" name="password" placeholder=" ">
                            <label for="password">Password</label>
                        </div>
                        <span><?= $errors['password'][0] ?? '' ?></span>
                    </div>
                </div>
            </div>
            <div class="__submit">
                <button class="submit" type="submit">Sign In</button>
            </div>
        </form>
        </div>
    

    <?php
    return ob_get_clean();
    }
    }

    `

    1. Namespace: namespace App\View\Login;: Indicates the code belongs to the App\View\Login namespace, keeping login view code organized.
    2. SignInView Class: class SignInView { ... }: Defines a class named SignInView to handle login form rendering.
    3. render Method: public static function render($errors) { ... }: This static method generates the HTML for the login form. The $errors parameter is crucial as it likely contains an array of validation errors passed from the controller. Key Elements Within the render Method: Output Buffering: ob_start(); and ob_get_clean();: These functions utilize output buffering. The HTML content is generated and stored in a buffer instead of being immediately sent to the browser. This allows for manipulation or processing of the content before it's displayed. Welcome Message and Signup Link: Provides a simple welcome message and a link to the registration page (./register). Login Form: form method="post" action="./login": Defines a form that uses the POST method to submit data to the ./login URL (likely handled by a login controller). Input Fields: Creates input fields for "email" and "password," including labels for user clarity. Error Display: <?= $errors['null'] ?? '' ?>: Uses the null coalescing operator (??) to display an error message associated with the 'null' key in the $errors array if it exists, otherwise displays nothing. This could be for a general login error. <?= $errors['email'][0] ?? '' ?> and <?= $errors['password'][0] ?? '' ?>: Similar to the above, these lines display specific error messages associated with the 'email' and 'password' fields, respectively, if they exist in the $errors array. Sign In Button: Sign In: Creates a button for users to submit the form.

    **
    signUp view : **

    `
    <?php

    namespace App\View\Login;

    class SignUpView {
    public static function render($errors) {
    ob_start();

    ?>


    Welcome! Please enter your details.


    Already have an account? Log In

        <form method="post" action="./register" class="__input-group" id="formSignUp">
            <div class="--scroll" data-page='0'>
                <div>
                    <div>
                        <div class="input">
                            <input type="text" id="fullname" name="full name" placeholder=" ">
                            <label for="fullname">Full name</label>
                        </div>
                        <span><?= $errors['full_name'][0] ?? '' ?></span>
                    </div>
    
                    <div>
                        <div class="input">
                            <input type="text" id="email" name="email" placeholder=" ">
                            <label for="email">Email</label>
                        </div>
                        <span><?= $errors['email'][0] ?? '' ?></span>
                    </div>
    
                    <div>
                        <div class="input">
                            <input type="date" id="age" name="dob">
                        </div>
                        <span><?= $errors['dob'][0] ?? '' ?></span>
                    </div>
    
                </div>
    
                <div>
                    <div>
                        <div class="inputGender">
                            <select name="gender" id="">
                                <option>please select gender</option>
                                <option value="women">women</option>
                                <option value="man">man</option>
                            </select>
                        </div>
                        <span><?= $errors['gender'][0] ?? '' ?></span>
                    </div>
    
                    <div>
                        <div class="input">
                            <input type="text" id="password" name="password" placeholder=" ">
                            <label for="password">Password</label>
                        </div>
                        <span><?= $errors['password'][0] ?? '' ?></span>
                    </div>
    
                    <div>
                        <div class="input">
                            <input type="text" id="confirm_password" name="confirm password" placeholder=" ">
                            <label for="confirm_password">Confirm password</label>
                        </div>
                        <span><?= $errors['confirm_password'][0] ?? '' ?></span>
                    </div>
                </div>
            </div>
            <div class="__submit">
                <button class="prev hidden" type="button"><i class="bi bi-chevron-left"></i> previous</button>
                <button class="next" type="button">next <i class="bi bi-chevron-right"></i></button>
                <button class="submit hidden" type="submit">Sign up</button>
            </div>
        </form>
        </div>
    

    <?php
    return ob_get_clean();
    }
    }

    `

    1. Namespace: namespace App\View\Login;: Indicates the code belongs to the App\View\Login namespace, keeping login view code organized.
    2. SignInView Class: class SignInView { ... }: Defines a class named SignInView to handle login form rendering.
    3. render Method: public static function render($errors) { ... }: This static method generates the HTML for the login form. The $errors parameter is crucial as it likely contains an array of validation errors passed from the controller. Key Elements Within the render Method: Output Buffering: ob_start(); and ob_get_clean();: These functions utilize output buffering. The HTML content is generated and stored in a buffer instead of being immediately sent to the browser. This allows for manipulation or processing of the content before it's displayed. Welcome Message and Signup Link: Provides a simple welcome message and a link to the registration page (./register). Login Form: form method="post" action="./login": Defines a form that uses the POST method to submit data to the ./login URL (likely handled by a login controller). Input Fields: Creates input fields for "email" and "password," including labels for user clarity. Error Display: <?= $errors['null'] ?? '' ?>: Uses the null coalescing operator (??) to display an error message associated with the 'null' key in the $errors array if it exists, otherwise displays nothing. This could be for a general login error. <?= $errors['email'][0] ?? '' ?> and <?= $errors['password'][0] ?? '' ?>: Similar to the above, these lines display specific error messages associated with the 'email' and 'password' fields, respectively, if they exist in the $errors array. Sign In Button: Sign In: Creates a button for users to submit the form.

    Congratulations! You've built a basic PHP authentication system!
    Key Security Considerations:
    Password Hashing: Always hash passwords using bcrypt (password_hash()) before storing them. Never store passwords in plain text.
    Input Validation: Thoroughly validate and sanitize user input to prevent vulnerabilities like SQL injection and cross-site scripting (XSS).
    Session Management: Implement secure session management practices to protect user data.
    This is a simple starting point. You can expand this system to include password reset functionality, email verification, and more complex authorization rule

...

🔧 Building a Secured User Authentication System with PHP, MySQL, PDO and hashed password


📈 108.34 Punkte
🔧 Programmierung

🕵️ Medium CVE-2020-29285: Point of sales in php\/pdo project Point of sales in php\/pdo


📈 57.09 Punkte
🕵️ Sicherheitslücken

🕵️ Point of Sales in PHP-PDO 1.0 edit_category.php id sql injection


📈 32.03 Punkte
🕵️ Sicherheitslücken

📰 TeamViewer stored user passwords encrypted, not hashed, and key now public


📈 31.25 Punkte
📰 IT Security Nachrichten

🕵️ Oracle MySQL up to 5.6.5 Password Authentication sql/password.c memcmp weak authentication


📈 31.23 Punkte
🕵️ Sicherheitslücken

🎥 Fingerprint a Hashed Password with Hash-Identifier [Tutorial]


📈 29.93 Punkte
🎥 IT Security Video

🔧 How to Persist a Hashed Format Password Inside ProxySQL


📈 29.93 Punkte
🔧 Programmierung

🕵️ Olate Olatedownload 3.4.1 environment.php pdo::attr_server_version memory corruption


📈 28.55 Punkte
🕵️ Sicherheitslücken

💾 Online Job Portal In PHP/PDO 1.0 SQL Injection


📈 28.55 Punkte
💾 IT Security Tools

🔧 PowerLite PDO: A Powerful PHP Database Abstraction Layer


📈 28.55 Punkte
🔧 Programmierung

⚠️ E-Commerce Site Using PHP PDO 1.0 Directory Traversal


📈 28.55 Punkte
⚠️ PoC

⚠️ E-Commerce Site Using PHP PDO 1.0 Insecure Settings


📈 28.55 Punkte
⚠️ PoC

💾 E-Commerce Site Using PHP PDO 1.0 Cross Site Scripting


📈 28.55 Punkte
💾 IT Security Tools

📰 Firefox Master Password System Has Been Poorly Secured for the Past 9 Years


📈 28.01 Punkte
📰 IT Security Nachrichten

📰 Firefox Master Password System Has Been Poorly Secured for the Past 9 Years, Researcher Says


📈 28.01 Punkte
📰 IT Security Nachrichten

📰 Firefox Master Password System Has Been Poorly Secured for the Past 9 Years, Researcher Says


📈 28.01 Punkte
📰 IT Security Nachrichten

⚠️ [local] - MySQL / MariaDB / PerconaDB - 'mysql' System User Privilege Escalation / Race Condition


📈 25.67 Punkte
⚠️ PoC

⚠️ [local] - MySQL / MariaDB / PerconaDB - 'mysql' System User Privilege Escalation / Race Condition


📈 25.67 Punkte
⚠️ PoC

📰 AnyVan confirms digital break-in, says customer names, emails and hashed passwords exposed


📈 25.55 Punkte
📰 IT Security Nachrichten

📰 Poorly secured PostgreSQL, MySQL servers targeted by ransomware bot


📈 25.35 Punkte
📰 IT Security Nachrichten

📰 Equifax Website Secured By The Worst Username And Password Possible


📈 25.28 Punkte
📰 IT Security Nachrichten

🕵️ Zend Framework bis 1.12.15 PDO Adapter SQL Injection


📈 25.06 Punkte
🕵️ Sicherheitslücken

🕵️ ADOdb Library bis 5.20.6 auf PHP5 PDO Driver qstr SQL Injection


📈 25.06 Punkte
🕵️ Sicherheitslücken

🕵️ Zend Framework bis 1.12.15 PDO Adapter SQL Injection


📈 25.06 Punkte
🕵️ Sicherheitslücken

🕵️ ADOdb Library bis 5.20.6 auf PHP5 PDO Driver qstr SQL Injection


📈 25.06 Punkte
🕵️ Sicherheitslücken

🕵️ http://pdo.vec.go.th/index.html


📈 25.06 Punkte
🕵️ Hacking

🕵️ Adminer up to 4.8.0 PDO Extension cross site scripting


📈 25.06 Punkte
🕵️ Sicherheitslücken

matomo