Chapterبەش 10 · Lectureوانە 10.3

A Login System with Sessionsسیستەمی چوونەژوورەوە بە سێشن

Build the course's SessionLab step by step — the users table, a connection file, registration, login, a protected page and logout — the safe way: password_hash(), prepared statements, session_regenerate_id(), exit after every redirect and a complete logout. Also: why the lab's cookie version can be forged, and how to add admin and user roles.

SessionLabـی کۆرسەکە هەنگاو بە هەنگاو دروست بکە — بەکارهێنەران، پەیوەندی، ، ، لاپەڕەیەکی پارێزراو و — بە ڕێگای سەلامەت: password_hash()، ، session_regenerate_id()، exit دوای هەموو و چوونەدەرەوەیەکی تەواو. هەروەها: بۆچی وەشانی تاقیگەکە دەتوانرێت ساختە بکرێت و چۆن ڕۆڵی بەڕێوەبەر و زیاد بکەیت.

42 min read38 خولەک خوێندنەوە · 36 stepsهەنگاو

What you will learnچی فێر دەبیت

  • Build registration, login, protected pages and logout with sessions, following the steps of Lab 9.
  • Store passwords with password_hash() and check them with password_verify().
  • Use prepared statements, and explain how SQL injection breaks a login.
  • Protect pages with a session check followed by exit, and log users out completely.
  • Explain why a cookie that holds the user id is not a login, and use cookies only for harmless conveniences.
  • Add roles and tell authentication from authorization.
  • بە و بە پێی هەنگاوەکانی تاقیگەی ٩، ، ، لاپەڕەی پارێزراو و دروست بکەیت.
  • بە password_hash() خەزن بکەیت و بە password_verify() بیانپشکنیت.
  • بەکاربهێنیت و ڕوونی بکەیتەوە چۆن تێک دەدات.
  • لاپەڕەکان بە پشکنینی و دواتر exit بپارێزیت و بەکارهێنەران بە تەواوی بخەیتە دەرەوە.
  • ڕوونی بکەیتەوە بۆچی کە ناسێنەری تێدایە نییە و تەنها بۆ ئاسانکاریی بێ‌زیان بەکاربهێنیت.
  • ڕۆڵ زیاد بکەیت و لە جیا بکەیتەوە.

What we are going to buildچی دروست دەکەین

Lab 9 of the course is called “Session and Cookie”. In it you use a session (or a cookie) to remember users after they log in to their account. In general, sessions and cookies are used to keep track of users' activity on a website or web application.

تاقیگەی ٩ی کۆرسەکە ناوی «⁦Session and Cookie⁩»ـە. تێیدا (یان ) بەکار دەهێنیت بۆ ئەوەی بەکارهێنەران دوای چوونەژوورەوەیان بۆ لە بیر بگریت. بە گشتی، و بۆ شوێنکەوتنی چالاکیی بەکارهێنەران لە یان بەکار دێن.

We build the in-class project of this lab, SessionLab, step by step: a users table, a connection file, a registration page, a login page, a protected page and a logout page. The course's code works on XAMPP, but it has serious security holes. In this lecture every step is kept, every hole is shown, and every file is rebuilt the safe way.

پرۆژەی ناو پۆلی ئەم تاقیگەیە، SessionLab، هەنگاو بە هەنگاو دروست دەکەین: users، پەیوەندی، لاپەڕەی ، لاپەڕەی ، لاپەڕەیەکی پارێزراو و لاپەڕەی . کۆدەکەی کۆرسەکە لەسەر XAMPP کار دەکات، بەڵام کونی ئاسایشیی گەورەی تێدایە. لەم وانەیەدا هەموو هەنگاوێک دەپارێزرێت، هەموو کونێک پیشان دەدرێت و هەموو بە شێوەی سەلامەت دووبارە دروست دەکرێتەوە.

Definitionپێناسە

Checking who a user is, usually with a username and password. If the details are correct, the user is logged in. It answers 'Who are you?'; answers 'What may you do?'.

پشکنینی ئەوەی بەکارهێنەرێک کێیە، زۆرجار بە ناوی بەکارهێنەر و تێپەڕەوشە. ئەگەر زانیارییەکان دروست بن، بەکارهێنەرەکە دەچێتە ژوورەوە. وەڵامی «تۆ کێیت؟» دەداتەوە؛ وەڵامی «بۆت هەیە چی بکەیت؟» دەداتەوە.

A login system does two jobs. answers “Who are you?”: the user proves it with an email and a password. answers “What may you do?”: for example, only logged-in users may see index.php, and only an admin may manage users.

سیستەمی دوو کار دەکات. وەڵامی «تۆ کێیت؟» دەداتەوە: بە و دەیسەلمێنێت. وەڵامی «بۆت هەیە چی بکەیت؟» دەداتەوە: بۆ نموونە، تەنها بەکارهێنەرانی چووەژوورەوە بۆیان هەیە index.php ببینن و تەنها بەڕێوەبەر بۆی هەیە بەکارهێنەران بەڕێوە ببات.

The wristband at the gateبازنەی دەست لە دەروازە

At the university gate the guard checks your student card once. Then he gives you a coloured wristband for the day. Inside, every building only looks at the wristband; nobody asks for your card again. When you leave, you give the wristband back. In our system, checking the card is login.php, the wristband is the session, every building is a protected page that checks $_SESSION, and giving it back is logout.php.

لە دەروازەی زانکۆ پاسەوانەکە یەک جار کارتی خوێندکارییەکەت دەپشکنێت. پاشان بازنەیەکی ڕەنگاوڕەنگی دەستت بۆ ئەو ڕۆژە دەداتێ. لە ژوورەوە، هەموو باڵەخانەیەک تەنها سەیری بازنەکە دەکات؛ کەس دیسان داوای کارتەکەت ناکاتەوە. کاتێک دەڕۆیت، بازنەکە دەگەڕێنیتەوە. لە سیستەمەکەی ئێمەدا، پشکنینی کارتەکە login.phpـە، بازنەکە ، هەر باڵەخانەیەک لاپەڕەیەکی پارێزراوە کە $_SESSION دەپشکنێت و گەڕاندنەوەکەی logout.phpـە.

Where the analogy stops workingسنووری ئەم نموونەیە

A wristband can be copied by someone who looks closely. A session ID is long and random, but it can still be stolen, so we keep it in an HttpOnly cookie and give the user a new one at login.

بازنەی دەست دەتوانرێت لەلایەن کەسێکەوە کۆپی بکرێت کە بە وردی سەیری بکات. درێژ و هەڕەمەکییە، بەڵام هێشتا دەتوانرێت بدزرێت، بۆیە لە HttpOnlyدا ڕایدەگرین و لە کاتی ناسێنەرێکی نوێ دەدەین بە .

What we are going to buildچی دروست دەکەین

The login flow at a glanceڕەوتی چوونەژوورەوە بە یەک نیگا

Flow of the four pages. register.php (new users): form with username, email, password and confirm; password_hash(); INSERT INTO users with ? placeholders. An arrow 'account created' leads to login.php (check the password): SELECT … WHERE email = ?; password_verify(); if correct, a new session id and $_SESSION['user_id'] are set and the browser goes to index.php; if wrong, 'Wrong email or password.' and the user stays. index.php (only for logged-in users): session_start(); if $_SESSION['user_id'] is not set, header(…) and exit back to login.php; otherwise the page and a Logout link. Clicking Logout leads to logout.php (end the session): $_SESSION = [], setcookie(session_name(), '', time() - 42000, …), session_destroy(), header to login.php and exit. Two arrows go back to login.php: 'not logged in: redirect + exit' from index.php and 'logged out: back to login.php' from logout.php.ڕەوتی چوار لاپەڕەکە. register.php (بەکارهێنەری نوێ): فۆرم بە ناو، ئیمەیڵ، تێپەڕەوشە و دووپاتکردنەوە؛ password_hash()؛ INSERT INTO users بە نیشانەی ?. تیرێکی «هەژمار دروست کرا» دەچێت بۆ login.php (پشکنینی تێپەڕەوشە): SELECT … WHERE email = ?؛ password_verify()؛ ئەگەر دروست بێت، ناسێنەرێکی نوێی سێشن و $_SESSION['user_id'] دادەنرێن و تۆڕگەڕ دەچێت بۆ index.php؛ ئەگەر هەڵە بێت، «Wrong email or password.» و بەکارهێنەر دەمێنێتەوە. index.php (تەنها بۆ چووەژوورەوەکان): session_start()؛ ئەگەر $_SESSION['user_id'] دانەنرابێت، header(…) و exit بۆ گەڕانەوە بۆ login.php؛ ئەگینا لاپەڕەکە و بەستەری Logout. کلیک لە Logout دەچێت بۆ logout.php (کۆتاییهێنان بە سێشن): $_SESSION = []، setcookie(session_name(), '', time() - 42000, …)، session_destroy()، header بۆ login.php و exit. دوو تیر دەگەڕێنەوە بۆ login.php: «نەچووەتە ژوورەوە: ئاڕاستەکردنەوە + exit» لە index.phpـەوە و «چووەدەرەوە: گەڕانەوە بۆ login.php» لە logout.phpـەوە.
The login flow across the four pages of the corrected SessionLab.ڕەوتی بە چوار لاپەڕەکەی SessionLabـی ڕاستکراودا.CêIT

The lab plan in seven stepsپلانی تاقیگەکە بە حەوت هەنگاو

The lab lists the steps of the project. The first steps were already done in earlier labs (database and CRUD). Here they are, cleaned up, with the section of this lecture where each one is done:

تاقیگەکە هەنگاوەکانی پرۆژەکە ڕیز دەکات. یەکەم هەنگاوەکان پێشتر لە تاقیگەکانی پێشوودا کراون ( و CRUD). ئەمە هەنگاوەکانن، پاککراونەتەوە، لەگەڵ ئەو بەشەی ئەم وانەیە کە هەر هەنگاوێکی تێدا دەکرێت:

  1. Create the databaseبنکەدراوە دروست بکە

    Create a MySQL database named session (Database and table).

    MySQL بە ناوی session دروست بکە (بنکەدراوە و خشتە).

  2. Create the tableخشتە دروست بکە

    Create the table users with the fields id, username, email and password (Database and table).

    users بە id، username، email و password دروست بکە (بنکەدراوە و خشتە).

  3. Connection fileفایلی پەیوەندی

    In your NetBeans project, create connection.php (Step 1).

    لە پرۆژەی NetBeansـەکەتدا connection.php دروست بکە (هەنگاوی ١).

  4. Registration pageلاپەڕەی خۆتۆمارکردن

    Create register.php, where users create an account to use later. It needs an HTML form asking for username, email address, password and password confirmation (Step 2).

    register.php دروست بکە، کە بەکارهێنەران تێیدا هەژمار دروست دەکەن بۆ ئەوەی دواتر بەکاری بهێنن. پێویستی بە HTML هەیە کە ناوی ، ، و دووپاتکردنەوەی بپرسێت (هەنگاوی ٢).

  5. Login pageلاپەڕەی چوونەژوورەوە

    Create login.php with a form asking for email and password, and the PHP code for logging in, using a SELECT query (Step 3).

    login.php دروست بکە بە کە و بپرسێت و کۆدی PHP بۆ ، بە بەکارهێنانی SELECT (هەنگاوی ٣).

  6. Create the sessionسێشنەکە دروست بکە

    In login.php, when the user is found, create the session for the user who logged in successfully (Step 4).

    لە login.phpدا، کاتێک دۆزرایەوە، بۆ ئەو بەکارهێنەرە دروست بکە کە بە سەرکەوتوویی چووەتە ژوورەوە (هەنگاوی ٤).

  7. Check and end the sessionپشکنین و کۆتاییهێنان بە سێشن

    Check the session on every other page (Step 5), and when users log out, delete the session (and any cookie) you created (Final step).

    لە هەموو لاپەڕەکانی تردا بپشکنە (هەنگاوی ٥) و کاتێک بەکارهێنەران دەچنە دەرەوە، ئەو (و هەر ) بسڕەوە کە دروستت کردووە (هەنگاوی کۆتایی).

The lab plan in seven stepsپلانی تاقیگەکە بە حەوت هەنگاو

The lab PDF and the in-class SessionLabPDFـی تاقیگەکە و SessionLabـی ناو پۆل

There are two versions of this project in the course: the code printed in the Lab 9 PDF, and the SessionLab folder written in class. They differ in many places. This lecture shows both and builds a third, corrected version:

دوو وەشانی ئەم پرۆژەیە لە کۆرسەکەدا هەن: ئەو کۆدەی لە PDFـی تاقیگەی ٩دا چاپ کراوە و SessionLab کە لە ناو پۆلدا نووسراوە. لە زۆر شوێندا جیاوازن. ئەم وانەیە هەردووکیان پیشان دەدات و وەشانێکی سێیەمی ڕاستکراو دروست دەکات:

Three versions of the same projectسێ وەشانی هەمان پرۆژە
PointخاڵLab 9 PDFPDFـی تاقیگەی ٩SessionLab (in class)SessionLab (ناو پۆل)CêIT versionوەشانی CêIT
PHP processingکارکردنی PHPInside the HTML, after the form (headers too late)لە ناو HTMLدا، دوای (سەردێڕەکان زۆر درەنگن)At the top, before the HTMLلە سەرەوە، پێش HTMLAt the top, before the HTMLلە سەرەوە، پێش HTML
Quotes in code لە کۆدداSome curly quotes ‘ ’ “ ” (fatal errors)هەندێک خوار ‘ ’ “ ” ()Straight quotes ڕاستStraight quotes ڕاست
Password storageخەزنکردنی sha1()Plain textدەقی سادەpassword_hash()
SQL queries SQLInput pasted into the query ڕاستەوخۆ خراوەتە ناو Input pasted into the query ڕاستەوخۆ خراوەتە ناو Prepared statements
ValidationEmpty-field checks, confirm passwordپشکنینی بەتاڵ، دووپاتکردنەوەی NoneهیچEmpty fields, email format, length, confirm بەتاڵ، شێوەی ، درێژی، دووپاتکردنەوە
Error function هەڵەmysql_error() (removed in PHP 7)mysql_error() (لە ⁦PHP 7⁩دا لابراوە)mysqli_error($connect)Short messages, no database detailsپەیامی کورت، بێ وردەکاریی
Field namesناوی uname, email, pass1, pass2, submituname، email، pass1، pass2، submitusernameInput, emailInput, passwordInput, confirmBtn, loginBtnusernameInput، emailInput، passwordInput، confirmBtn، loginBtnSessionLab names, plus confirmInputناوەکانی SessionLab، لەگەڵ confirmInput
ConnectionپەیوەندیPassword 'root' (MAMP), mysqli_select_db() 'root' ⁦(MAMP)⁩، mysqli_select_db()Password '' (XAMPP), database as 4th argument '' ⁦(XAMPP)⁩، وەک چوارەمXAMPP values, exceptions handledنرخەکانی XAMPP، مامەڵەیان لەگەڵ دەکرێت
Finding the userدۆزینەوەی mysqli_num_rows() == 1mysqli_num_rows() == 1while with mysqli_fetch_assoc()while لەگەڵ mysqli_fetch_assoc()mysqli_num_rows() === 1, then password_verify()mysqli_num_rows() === 1، پاشان password_verify()
Cookie versionوەشانی Three extra code stepsسێ هەنگاوی کۆدی زیادەOne commented-out setcookie() lineیەک دێڕی setcookie() کە کراوە بە Shown only to explain why it is unsafeتەنها بۆ ڕوونکردنەوەی ئەوەی بۆچی ناسەلامەتە پیشان دەدرێت

Database and tableبنکەدراوە و خشتە

Steps 1 and 2: create a database named session and a table for the users. The lab gives only the field names, so here is a complete definition. You can run it in the SQL tab of phpMyAdmin (Creating a database in phpMyAdmin).

هەنگاوی ١ و ٢: بە ناوی session و بۆ بەکارهێنەران دروست بکە. تاقیگەکە تەنها ناوی دەدات، بۆیە ئەمە پێناسەیەکی تەواوە. دەتوانیت لە بەشی SQLـی phpMyAdminدا کاری پێبکەیت (دروستکردنی بنکەدراوە لە phpMyAdminدا).

session.sqlSQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
CREATE DATABASE session;
USE session;

CREATE TABLE users (
    id       INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50)  NOT NULL,
    email    VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL
);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

AUTO_INCREMENT PRIMARY KEY: MySQL numbers the users 1, 2, 3 … by itself.

AUTO_INCREMENT PRIMARY KEY: MySQL خۆی بەکارهێنەران ژمارە دەکات 1، 2، 3 …

Lineدێڕی 7

UNIQUE: two accounts cannot have the same email. The login finds users by email, so this matters.

UNIQUE: دوو هەژمار ناتوانن هەمان هەبێت. بەکارهێنەران بە دەدۆزێتەوە، بۆیە ئەمە گرنگە.

Lineدێڕی 8

VARCHAR(255): room for a password_hash() hash (60 characters today, longer with future algorithms). This column never holds the real password.

VARCHAR(255): شوێن بۆ هاشێکی password_hash() (ئەمڕۆ ٦٠ پیت، لەگەڵ داهاتوو درێژتر). ئەم هەرگیز ڕاستەقینەی تێدا نییە.

Step 1: the connection fileهەنگاوی ١: فایلی پەیوەندی

Every page that talks to the database needs a connection. Instead of writing it again on every page, we put it in one file, connection.php, and include it (A real use: one database connection file). This is the lab PDF's version:

هەموو لاپەڕەیەک کە قسە لەگەڵ دەکات پێویستی بە هەیە. لە جیاتی ئەوەی لە هەموو لاپەڕەیەکدا دووبارە بنووسرێتەوە، دەیخەینە یەک فایلەوە، connection.php و بە require دەیهێنینە ناو لاپەڕەکانەوە (؛ بەکارهێنانێکی ڕاستەقینە: یەک فایلی پەیوەندیی بنکەدراوە). ئەمە وەشانەکەی PDFـی تاقیگەکەیە:

connection.php (lab PDF)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php

$connect= mysqli_connect('localhost', 'root','root' );
if(!$connect){
    die("could not connect".mysqli_error($connect));

}
$db= mysqli_select_db($connect, 'session');
if(!$db){
    die("could not select database".mysqli_error($connect));
}

SessionLab, written in class, is shorter: it uses XAMPP's empty password and gives the database name as the fourth argument, so mysqli_select_db() is not needed:

SessionLab، کە لە ناو پۆلدا نووسراوە، کورتترە: بەتاڵەکەی XAMPP بەکار دەهێنێت و ناوی وەک چوارەم دەدات، بۆیە mysqli_select_db() پێویست نییە:

connection.php (SessionLab)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
    $connect = mysqli_connect('localhost', 'root', '', 'session');
    if (!$connect) {
        die("Cannot connect to the db: ".mysqli_error($connect));
    }
?>

Step 1: the connection fileهەنگاوی ١: فایلی پەیوەندی

The corrected connection.php⁦connection.php⁩ـی ڕاستکراو

Here is connection.php written for PHP 8. It uses the XAMPP values of SessionLab, catches the exception, and shows visitors only a short message:

ئەمە connection.phpـە کە بۆ ⁦PHP 8⁩ نووسراوە. نرخەکانی XAMPPـی SessionLab بەکار دەهێنێت، دەگرێت و تەنها پەیامێکی کورت پیشانی سەردانکەران دەدات:

connection.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// connection.php: the database connection, included by the other pages.
// Since PHP 8.1, mysqli throws an exception when something fails. We say so clearly:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

try {
    // server, MySQL user, password, database name (XAMPP: user "root", empty password)
    $connect = mysqli_connect('localhost', 'root', '', 'session');
    mysqli_set_charset($connect, 'utf8mb4');
} catch (mysqli_sql_exception $e) {
    // Do not show technical details to visitors
    die('Cannot connect to the database. Is MySQL running?');
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4

Errors become exceptions (this is already the default since PHP 8.1; the line makes it clear).

دەبنە (لە ⁦PHP 8.1⁩ـەوە ئەمە بنەڕەتییە؛ دێڕەکە ڕوونی دەکاتەوە).

Lineدێڕی 8

Server, MySQL user, password, database name: the XAMPP values.

، MySQL، ، ناوی : نرخەکانی XAMPP.

Lineدێڕی 9

utf8mb4: Kurdish and every other script are stored correctly.

utf8mb4: کوردی و هەموو ئەلفوبێیەکی تر بە دروستی خەزن دەکرێن.

Lineدێڕی 10-12

If MySQL is not running, the visitor sees one short sentence, not a fatal error with your file paths.

ئەگەر MySQL کار نەکات، سەردانکەر یەک ڕستەی کورت دەبینێت، نەک لەگەڵ فایلەکانت.

Step 2: the registration pageهەنگاوی ٢: لاپەڕەی خۆتۆمارکردن

register.php lets a new user create an account to use later. Its form asks for a username, an email address, a password and the password again. The lab PDF prints all of register.php like this (only the apostrophe in one comment has been straightened):

register.php ڕێگە بە نوێ دەدات هەژمارێک دروست بکات بۆ ئەوەی دواتر بەکاری بهێنێت. ناوی ، ، و دووبارە دەپرسێت. PDFـی تاقیگەکە هەموو register.php بەم شێوەیە چاپ دەکات (تەنها ئەپۆسترۆفی ناو یەک ڕاست کراوەتەوە):

register.php (lab PDF)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<html>
   <head>
     <meta charset="UTF-8">
     <title></title>
   </head>
   <body>
     <form align="center" method="post" action="<?php echo
         htmlspecialchars($_SERVER["PHP_SELF"]) ?>">
        Username: <input type="text" name="uname" size="30"><br><br>  Email:
        <input type="text" name="email" size="30"><br><br>  Password: <input
        type="password" name="pass1" size="30"><br><br>  Username: <input
        type="password" name="pass2" size="30"><br><br>
        <input type="submit" value="Create Account" name="submit"><br>
       <?php
         if(isset($_POST["submit"])){
              if(empty($_POST["uname"])){
                 echo "Username is required";
              }elseif(empty($_POST["email"])) {
                 echo"Please enter your email address";
              }elseif(empty($_POST["pass1"])) {
                 echo"Please enter your password";
              }elseif(empty($_POST["pass2"])) {
                 echo"Please confirm your password";
              }elseif($_POST["pass1"]!=$_POST["pass2"]) {
                 echo" passwords didnot match, please try again";
              }else{
                    //getting values from form inputs
                 $name=$_POST["uname"];
                 $email=$_POST["email"];
                 $password=$_POST["pass1"];
                 $password= sha1($password);
                 require './connection.php';
                 $query="INSERT INTO Users (name, email, password) VALUES ('$name', '$email',
'$password')";
                 $result= mysqli_query ($connect, $query);
                    //check the query if it's true
                 if($result){
                     echo "Account created succeffuly, you can login now";
                 }else{
                     echo"error occured, please try again".mysql_error();
                 }
                 //close the connecton
                 mysqli_close($connect);
            }
          }

       ?>
     </form>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 7-8

The form posts to itself: htmlspecialchars($_SERVER["PHP_SELF"]) is the XSS-safe way to do this.

بۆ خۆی دەنێرێت: htmlspecialchars($_SERVER["PHP_SELF"]) ڕێگای سەلامەت لە XSSـە بۆ ئەم کارە.

Lineدێڕی 16-25

The checks, in order: username, email, password, confirmation, and do both passwords match?

پشکنینەکان، بە ڕیزبەندی: ناوی ، ، ، دووپاتکردنەوە و ئایا هەردوو وەک یەکن؟

Signup and Login with PHP and MySQLDave Hollingworth · Englishئینگلیزی · 326K viewsبینین · 6.1K likesلایک · Partبەشی 4:04–18:55 (ofلە 43:16)

Builds a signup page step by step: checking the form on the server, password_hash(), connecting to MySQL, and saving the user with a prepared statement.

هەنگاو بە هەنگاو دروست دەکات: بۆ ، password_hash()، پەیوەندیکردن بە و هەڵگرتنی بە .

Watch 4:04–18:55: processing the signup form up to the duplicate-email check.

لە ⁦4:04⁩ تا ⁦18:55⁩ سەیر بکە: کارکردن لەسەر ، تا پشکنینی ئەوەی ئایا پێشتر بەکارهاتووە.

Step 2: the registration pageهەنگاوی ٢: لاپەڕەی خۆتۆمارکردن

SessionLab's register.php⁦register.php⁩ـی SessionLab

SessionLab's register.php moves the PHP to the top of the file, which is good, but it drops all the checks:

register.phpـی SessionLab کۆدی PHP دەباتە سەرەوەی ، کە باشە، بەڵام هەموو پشکنینەکان لادەبات:

register.php (SessionLab)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
    include('connection.php');

    if (isset($_POST['confirmBtn']))
    {
        $username = $_POST['usernameInput'];
        $email = $_POST['emailInput'];
        $address = $_POST['addressInput'];
        $password = $_POST['passwordInput'];
        $query = mysqli_query($connect, "insert into Users (username, email, password) values ('$username', '$email', '$password')");
        if ($query)
        {
            echo "Account created successfully!";
        } else {
            echo "An error has been occured!".mysqli_error($connect);
        }
    }
?>

<html>
    <body>
        <form method="POST">
            Username: <input type="text" name="usernameInput"><br>
            Email Address: <input type="text" name="emailInput"><br>
            Password: <input type="text" name="passwordInput"><br>
            <input type="submit" name="confirmBtn" value="Create Account">
        </form>
    </body>
</html>

Step 2: the registration pageهەنگاوی ٢: لاپەڕەی خۆتۆمارکردن

The corrected register.php⁦register.php⁩ـی ڕاستکراو

The corrected page keeps the lab's order of checks and its messages, uses SessionLab's field names, and adds what is missing: an email check, a minimum length, hashing, a prepared statement, sticky values and a message for a duplicate email.

لاپەڕە ڕاستکراوەکە ڕیزبەندیی پشکنینەکان و پەیامەکانی تاقیگەکە دەهێڵێتەوە، ناوی خانەکانی SessionLab بەکار دەهێنێت و ئەوەی کەمە زیادی دەکات: پشکنینی ، کەمترین درێژی، هاشکردن، ، لکاو و پەیامێک بۆ دووبارە.

register.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require 'connection.php';

$message  = '';
$username = '';
$email    = '';

if (isset($_POST['confirmBtn'])) {
    // Read the form values (?? '' avoids warnings if a field is missing)
    $username = trim($_POST['usernameInput'] ?? '');
    $email    = trim($_POST['emailInput'] ?? '');
    $password = $_POST['passwordInput'] ?? '';
    $confirm  = $_POST['confirmInput'] ?? '';

    // Check the inputs in order, like the lab does
    if ($username === '') {
        $message = 'Username is required.';
    } elseif ($email === '') {
        $message = 'Please enter your email address.';
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $message = 'Please enter a valid email address.';
    } elseif ($password === '') {
        $message = 'Please enter your password.';
    } elseif (strlen($password) < 8) {
        $message = 'The password needs at least 8 characters.';
    } elseif ($confirm === '') {
        $message = 'Please confirm your password.';
    } elseif ($password !== $confirm) {
        $message = 'The passwords did not match. Please try again.';
    } else {
        // Never store the password itself: store a slow, salted hash
        $hash = password_hash($password, PASSWORD_DEFAULT);

        // Prepared statement: the ? marks are filled in safely (no SQL injection)
        $stmt = mysqli_prepare($connect, 'INSERT INTO users (username, email, password) VALUES (?, ?, ?)');
        mysqli_stmt_bind_param($stmt, 'sss', $username, $email, $hash);
        try {
            mysqli_stmt_execute($stmt);
            $message  = 'Account created successfully. You can log in now.';
            $username = '';
            $email    = '';
        } catch (mysqli_sql_exception $e) {
            // Error 1062 = duplicate entry: the email column is UNIQUE
            if ($e->getCode() === 1062) {
                $message = 'An account with this email already exists.';
            } else {
                $message = 'An error occurred. Please try again.';
            }
        }
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Create an account</title>
</head>
<body>
    <h1>Create an account</h1>
    <p><?php echo htmlspecialchars($message); ?></p>
    <form method="post">
        Username: <input type="text" name="usernameInput" value="<?php echo htmlspecialchars($username); ?>"><br>
        Email Address: <input type="email" name="emailInput" value="<?php echo htmlspecialchars($email); ?>"><br>
        Password: <input type="password" name="passwordInput"><br>
        Confirm Password: <input type="password" name="confirmInput"><br>
        <input type="submit" name="confirmBtn" value="Create Account">
    </form>
    <p>Already have an account? <a href="login.php">Log in</a></p>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

require: without the connection the page cannot work, so stop if the file is missing.

require: بەبێ پەیوەندییەکە لاپەڕەکە ناتوانێت کار بکات، بۆیە ئەگەر نەبوو بوەستە.

Lineدێڕی 10-13

trim() removes spaces around the text; ?? '' avoids warnings. Passwords are not trimmed: a space may be part of a password.

trim() بۆشاییەکانی دەوری دەقەکە لادەبات؛ ?? '' ڕێگە لە دەگرێت. trim ناکرێن: لەوانەیە بەشێک بێت لە .

Lineدێڕی 20

filter_var(…, FILTER_VALIDATE_EMAIL) checks the shape of an email address.

filter_var(…, FILTER_VALIDATE_EMAIL) شێوەی دەپشکنێت.

Lineدێڕی 32

A slow, salted hash such as $2y$12$…. The real password is never stored.

هاشێکی خاو و خوێدار وەک $2y$12$…. ڕاستەقینە هەرگیز خەزن ناکرێت.

Lineدێڕی 35-36

The SQL has ? placeholders; bind_param fills them with three strings ('sss'). The data can never change the SQL.

SQLـەکە ?ـی تێدایە؛ bind_param بە سێ ('sss') پڕیان دەکاتەوە. هەرگیز ناتوانێت SQLـەکە بگۆڕێت.

Lineدێڕی 42-45

Error 1062 is MySQL's “duplicate entry”: the email is already registered.

هەڵەی ⁦1062⁩ «⁦duplicate entry⁩»ـی MySQLـە: پێشتر تۆمار کراوە.

Lineدێڕی 63-64

Sticky values, escaped with htmlspecialchars(). Password fields are never refilled.

لکاو، پارێزراو بە htmlspecialchars(). خانەکانی هەرگیز پڕ ناکرێنەوە.

register.php, tested request by request⁦register.php⁩، بە تاقیکراوەتەوە
The visitor sends…سەردانکەر دەنێرێت…The page answersلاپەڕەکە دەداتەوە
an empty usernameناوی بەتاڵUsername is required.
aram@example as the emailaram@example وەک Please enter a valid email address.
two different passwordsدوو جیاوازThe passwords did not match. Please try again.
correct data دروستAccount created successfully. You can log in now.
the same email a second timeهەمان بۆ جاری دووەمAn account with this email already exists.
Quick checkپشکنینی خێراWhy does the corrected register.php use ? placeholders and mysqli_stmt_bind_param()?بۆچی register.phpـی ڕاستکراو ? و mysqli_stmt_bind_param() بەکار دەهێنێت؟

Step 2: the registration pageهەنگاوی ٢: لاپەڕەی خۆتۆمارکردن

Why passwords are hashedبۆچی تێپەڕەوشەکان هاش دەکرێن

Definitionپێناسە

Storing a password as a slow, salted hash instead of the password itself. In PHP use password_hash() to save it and password_verify() to check it at login; never use md5() or sha1() for this.

خەزنکردنی تێپەڕەوشەیە وەک هاشێکی خاو کە نرخێکی هەڕەمەکی ⁦(salt)⁩ی تێکەڵ کراوە، نەک خودی تێپەڕەوشەکە. لە PHPـدا password_hash() بۆ خەزنکردن و password_verify() بۆ پشکنین لە کاتی بەکار بهێنە؛ هەرگیز md5() یان sha1() بۆ ئەم کارە بەکار مەهێنە.

Why hash? If someone steals your users table, plain-text passwords (SessionLab) can be used at once, also on other websites where people reuse them. sha1() and md5() hashes (the lab PDF, a student's version) are cracked very fast, because these functions are made to be fast. password_hash() is made to be slow, and adds a random salt, so the same password gives a different hash each time. password_verify() knows how to check it:

بۆچی هاش؟ ئەگەر کەسێک usersـەکەت بدزێت، دەقی سادە ⁦(SessionLab)⁩ دەستبەجێ بەکار دەهێنرێن، لەسەر تریش کە خەڵک هەمان بەکار دەهێنن. هاشەکانی sha1() و md5() (PDFـی تاقیگەکە، وەشانی خوێندکارێک) زۆر خێرا دەشکێنرێن، چونکە ئەم بۆ خێرایی دروست کراون. password_hash() بۆ خاوی دروست کراوە و هەڕەمەکی زیاد دەکات، بۆیە هەمان هەر جارێک هاشێکی جیاواز دەدات. password_verify() دەزانێت چۆن بیپشکنێت:

hash.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$password = 'tea-and-bread-42';

$hash1 = password_hash($password, PASSWORD_DEFAULT);
$hash2 = password_hash($password, PASSWORD_DEFAULT);
echo $hash1 . "<br>";
echo $hash2 . "<br>";                                  // different: each hash has its own random salt

var_dump(password_verify('tea-and-bread-42', $hash1)); // the right password
var_dump(password_verify('Tea-and-bread-42', $hash1)); // one letter is different
Quick checkپشکنینی خێراWhy is password_hash() better than sha1() or md5() for passwords?بۆچی password_hash() بۆ لە sha1() و md5() باشترە؟

Password Storage Tier List: encryption, hashing, salting, bcrypt, and beyondStudying With Alex · Englishئینگلیزی · 337K viewsبینین · 18K likesلایک · Partبەشی 0:00–8:45 (ofلە 10:16)

Ranks ways to store passwords, from plain text and encryption to hashing, salting and slow hashes like bcrypt (the method password_hash() uses).

ڕێگاکانی هەڵگرتنی پلەبەندی دەکات: لە دەقی ئاسایی و ، تا ، زیادکردنی و هێواش وەک bcrypt کە password_hash() بەکاری دەهێنێت.

Watch 0:00–8:45: from plain text up to slow hashing (bcrypt).

لە ⁦0:00⁩ تا ⁦8:45⁩ سەیر بکە: لە دەقی ئاساییەوە تا هێواش وەک bcrypt.

Step 3: the login pageهەنگاوی ٣: لاپەڕەی چوونەژوورەوە

login.php lets users log in to their account. It has a form asking for email and password, and PHP code that runs a SELECT query. The lab explains the idea: after running the query, check whether the result has exactly one row. If it does, a matching user was found: fetch the user's information from the database, and then create a session (or a cookie) for that user.

login.php ڕێگە بە بەکارهێنەران دەدات بچنە ناو . هەیە کە و دەپرسێت و کۆدی PHP کە SELECT کارپێدەکات. تاقیگەکە بیرۆکەکە ڕوون دەکاتەوە: دوای کارپێکردنی ، بپشکنە ئایا ئەنجامەکە تەنها یەک ڕیزی هەیە. ئەگەر وا بوو، هاوتا دۆزرایەوە: زانیارییەکانی لە بهێنە و پاشان (یان ) بۆ ئەو بەکارهێنەرە دروست بکە.

login.php (lab PDF, part)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
$result= mysqli_query ($connect, $query);
         $num_row=mysqli_num_rows($result);
         //check the row number is equal to 1, so we founded matched user
      if($num_row==1){
               //fetch user information and put them in array
               $row=mysqli_fetch_array($result, MYSQLI_ASSOC);
               //HERE YOU CREATE SESSION OR COOKIE


      }
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

mysqli_num_rows() counts the rows in the result of a SELECT.

mysqli_num_rows() ڕیزەکانی ئەنجامی SELECTێک دەژمێرێت.

Lineدێڕی 4

Exactly one row = one matching user. (“founded” should be “found”.)

تەنها یەک ڕیز = یەک هاوتا. («founded» دەبێت «found» بێت.)

Lineدێڕی 6

mysqli_fetch_array($result, MYSQLI_ASSOC) returns the row as an associative array, the same as mysqli_fetch_assoc($result).

mysqli_fetch_array($result, MYSQLI_ASSOC) ڕیزەکە وەک دەگەڕێنێتەوە، هەمان شتی mysqli_fetch_assoc($result).

Step 3: the login pageهەنگاوی ٣: لاپەڕەی چوونەژوورەوە

Step 4: create the session in login.phpهەنگاوی ٤: سێشنەکە لە ⁦login.php⁩ـدا دروست بکە

Inside that if, the lab creates the session. First call session_start(). Then create a session variable with a name and give it a value from the database. The syntax is $_SESSION['session name'] = 'session value';. The lab stores the user's id, and then sends the user to index.php. This is the code as printed in the lab:

لە ناو ئەو ifـەدا، تاقیگەکە دروست دەکات. سەرەتا session_start() بانگ بکە. پاشان بە ناوێک دروست بکە و لە پێ بدە. بریتییە لە $_SESSION['session name'] = 'session value';. تاقیگەکە ناسێنەری ⁦(id)⁩ هەڵدەگرێت و پاشان دەنێرێت بۆ index.php. ئەمە کۆدەکەیە وەک لە تاقیگەکەدا چاپ کراوە:

login.php (lab PDF, part, as printed: does not run)TEXT
$result= mysqli_query ($connect, $query);
         $num_row=mysqli_num_rows($result);
         //check the the row number is equal to 1, so we found matched user
      if($num_row==1){
               //fetch user information and put them in array
               $row=mysqli_fetch_array($result, MYSQLI_ASSOC);
               //HERE YOU CREATE SESSION OR COOKIE
                     session_start();
                     $_SESSION[‘user_id’]=$row[‘id’];
                     header(‘Location: index.php’);
                     exit();


      }
What PHP 8.5 says about lines 9 and 10⁦PHP 8.5⁩ دەربارەی دێڕەکانی 9 و ⁦10⁩ چی دەڵێت
Fatal error: Uncaught Error: Undefined constant "‘user_id’" …

Fatal error: Uncaught Error: Undefined constant "index" …

In the corrected login.php below, these lines become:

لە login.phpـی ڕاستکراوی خوارەوەدا، ئەم دێڕانە دەبن بە:

login.php (lines 32-39)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
if (password_verify($password, $row['password'])) {
    // HERE WE CREATE THE SESSION
    session_regenerate_id(true);          // new session id after login
    $_SESSION['user_id']  = $row['id'];
    $_SESSION['username'] = $row['username'];
    header('Location: index.php');
    exit;                                 // stop: nothing else may run
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

The password is checked in PHP against the stored hash.

لە PHPـدا بەرامبەر بە هاشە خەزنکراوەکە دەپشکنرێت.

Lineدێڕی 3

A new session ID, so that an ID known before login is useless ().

نوێ، بۆ ئەوەی ناسێنەرێک کە پێش زانرابێت بێ‌سوود بێت ().

Lineدێڕی 4-5

Store the id (and the name, for greeting the user). The password is never stored in the session.

ناسێنەرەکە (و ناوەکە، بۆ بەخێرهاتنی ) هەڵبگرە. هەرگیز لە هەڵناگیرێت.

Lineدێڕی 7

exit ends the script at once (Why exit must follow header()).

exit دەستبەجێ کۆتایی پێدەهێنێت (بۆچی دەبێت exit دوای ⁦header()⁩ بێت).

Quick checkپشکنینی خێراWhy does the corrected login.php call session_regenerate_id(true) after a successful login?بۆچی login.phpـی ڕاستکراو دوای سەرکەوتوو session_regenerate_id(true) بانگ دەکات؟

Step 3: the login pageهەنگاوی ٣: لاپەڕەی چوونەژوورەوە

The lab’s complete login.php⁦login.php⁩ـی تەواوی تاقیگەکە

The lab PDF prints the whole file like this (curly quotes as printed, so it cannot run as it is):

PDFـی تاقیگەکە هەموو بەم شێوەیە چاپ دەکات ( خوار وەک چاپکراوە، بۆیە وەک خۆی کار ناکات):

login.php (lab PDF, as printed)TEXT
<html>
   <head>
     <meta charset="UTF-8">
     <title></title>
   </head>
   <body>
     <form align="center" method="post" action="<?php echo
         htmlspecialchars($_SERVER["PHP_SELF"]) ?>">
         Email: <input type="text" name="email" size="30"><br><br>  Password:
        <input type="password" name="pass1" size="30"><br><br>
        <input type="submit" value="login" name="submit"><br>
       <?php
         if(isset($_POST["submit"])){
                    if(empty($_POST["email"])) {
                 echo"Please enter your email address";
              }elseif(empty($_POST["pass1"])) {
                 echo"Please enter your password";
              } else{
                    //getting values from form inputs
                    $email=$_POST["email"];
                 $password=$_POST["pass1"];
                 $password= sha1($password);
                 require './connection.php';
                 $query="SELECT * FROM Users WHERE email='$email' AND password='$password'";
                 $result= mysqli_query ($connect, $query);
                    $num_row=mysqli_num_rows($result);
                    //check the the row number is equal to 1, so we found matched user
                 if($num_row==1){
                              //fetch user information and put them in array
                              $row=mysqli_fetch_array($result, MYSQLI_ASSOC);
                              //HERE YOU CREATE SESSION OR COOKIE
                                        session_start();
                                        $_SESSION[‘user_id’] = $row[‘id’];
                                        header(‘Location: index.php’);
                                        exit();
                              }else{
                     echo"error occured, please try again".mysql_error();
                 }
                 //close the connecton
                 mysqli_close($connect);
            }
          }

       ?>
     </form>

Step 3: the login pageهەنگاوی ٣: لاپەڕەی چوونەژوورەوە

SessionLab’s login.php⁦login.php⁩ـی SessionLab

The in-class version puts the PHP at the top and uses straight quotes, so it runs:

وەشانی ناو پۆل PHP دەخاتە سەرەوە و ڕاست بەکار دەهێنێت، بۆیە کار دەکات:

login.php (SessionLab)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php

    include('connection.php');

    if (isset($_POST['loginBtn']))
    {
        $email = $_POST['emailInput'];
        $password = $_POST['passwordInput'];
        $query = "SELECT * FROM Users WHERE email='$email' AND password='$password'";
        $result = mysqli_query ($connect, $query);

        while ($row = mysqli_fetch_assoc($result))
        {
            //HERE YOU CREATE SESSION OR COOKIE
            session_start();
            $_SESSION['user_id'] = $row['id'];

            //setcookie('user_id', $row['id'], time() + 50, "/index.php");

            header('Location:index.php');
            exit();
        }
    }
?>

<html>
    <body>
        <form method="post">
            Email: <input type="text" name="emailInput"><br>
            Password: <input type="text" name="passwordInput"><br>
            <input type="submit" name="loginBtn" value="Login">
        </form>
    </body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 9

The email and password are pasted into the SQL: SQL injection. The password is compared as plain text.

و ڕاستەوخۆ خراونەتە ناو SQL: . وەک دەقی سادە بەراورد دەکرێت.

Lineدێڕی 12

A while loop used as “if there is a row”. if ($row = mysqli_fetch_assoc($result)) says it more clearly.

while کە وەک «ئەگەر ڕیزێک هەبێت» بەکارهاتووە. if ($row = mysqli_fetch_assoc($result)) ڕوونتر دەیڵێت.

Lineدێڕی 18

The cookie alternative, commented out (see below).

جێگرەوەی ، کراوە بە (خوارەوە ببینە).

SessionLab’s login.php under test⁦login.php⁩ـی SessionLab لە ژێر تاقیکردنەوەدا
We sent…ناردمان…What happened (PHP 8.5)چی ڕوویدا ⁦(PHP 8.5)⁩
the right email with a wrong password ڕاست لەگەڵ هەڵەThe form appears again, with no message at all. دیسان دەردەکەوێت، بەبێ هیچ پەیامێک.
the email x' OR '1'='1' -- and any password x' OR '1'='1' -- و هەر 302 redirect to index.php: logged in as the first user, with no password.⁦302⁩ بۆ ⁦index.php⁩: وەک یەکەم چووە ژوورەوە، بەبێ .

Step 3: the login pageهەنگاوی ٣: لاپەڕەی چوونەژوورەوە

A student’s login.php⁦login.php⁩ـی خوێندکارێک

A student's practice project checks a username and an md5() password, and stores two session variables:

پرۆژەی ڕاهێنانی خوێندکارێک ناوی و md5() دەپشکنێت و دوو هەڵدەگرێت:

login.php (student project)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<!DOCTYPE html>
<html lang="en">

<body>
    <?php
    include("config.php");

    if (isset($_POST['Login'])){

        $username = $_POST['user'];
        $password= md5($_POST['pass']);

        $selectQuery = mysqli_query($connection,"select * from users where username ='$username' and password = '$password' ");

        if (mysqli_num_rows($selectQuery)){
            while ($row = mysqli_fetch_assoc($selectQuery)){

            session_start();

            $_SESSION['userId'] = $row['id'];

            $_SESSION['userInput'] = $row['username'];

            header('location: view.php');
            exit();

            }
        }else{
            echo "username or password failed.";
        }
    }


    ?>

    <form method="post">
        username : <input type="text" name="user"><br><br>
        password : <input type="password" name="pass"><br><br>
        <input type="submit" name="Login" value="Login">



    </form>
</body>

</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 1

HTML output starts on line 1, so session_start() and header() come too late (they work only with output buffering).

HTML لە دێڕی 1ـەوە دەست پێدەکات، بۆیە session_start() و header() زۆر درەنگ دێن (تەنها بە کار دەکەن).

Lineدێڕی 11

md5() is as unsafe for passwords as sha1().

md5() بۆ هێندەی sha1() ناسەلامەتە.

Lineدێڕی 13

SQL injection again: try the username admin' -- .

دیسان : ناوی admin' -- تاقی بکەرەوە.

Lineدێڕی 24-25

Good: exit() right after header(). But the target is view.php, while other pages of this project use View.php. On Linux file names are case-sensitive, so one of them fails there.

باشە: exit() ڕاستەوخۆ دوای header(). بەڵام ئامانجەکە view.phpـە، لە کاتێکدا لاپەڕەکانی تری ئەم پرۆژەیە View.php بەکار دەهێنن. لەسەر Linux ناوی ، بۆیە یەکێکیان لەوێ شکست دەهێنێت.

Step 3: the login pageهەنگاوی ٣: لاپەڕەی چوونەژوورەوە

How SQL injection logs in without a passwordتێخزاندنی SQL چۆن بەبێ تێپەڕەوشە دەچێتە ژوورەوە

Definitionپێناسە

An attack in which someone types SQL code into a form or URL. If the script puts that input straight into its query, the attacker's code runs and can show or delete data. stop it.

هێرشێکە کە تێیدا هێرشبەر کۆدی SQL لە یان دەنووسێت. ئەگەر کۆدەکە ئەو نووسینە ڕاستەوخۆ لەگەڵ تێکەڵ بکات، کۆدی هێرشبەرەکە جێبەجێ دەبێت و دەتوانێت دراوەکان ئاشکرا بکات یان بیانسڕێتەوە. ڕێگەی لێ دەگرن.

All three login pages above build the SQL by pasting the user's text into it. Watch what happens when the “email” is x' OR '1'='1' -- . This demo uses a small SQLite table, but the SQL is the same in MySQL:

هەر سێ لاپەڕەی سەرەوە SQLـەکە بە دانانی دەقی لە ناویدا دروست دەکەن. سەیر بکە چی ڕوودەدات کاتێک «» x' OR '1'='1' -- ـە. ئەم پیشاندانە بچووکی SQLite بەکار دەهێنێت، بەڵام SQLـەکە لە MySQLـیشدا هەمانە:

injection.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// A small users table in memory (SQLite), only to show the attack
$db = new PDO('sqlite::memory:');
$db->exec("CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, email TEXT, password TEXT)");
$db->exec("INSERT INTO users (username, email, password) VALUES
           ('admin', 'admin@example.com', 'k8#Lq2!z'),
           ('Aram',  'aram@example.com',  'tea-and-bread-42')");

// What an attacker types into the Email box (the password does not matter)
$email    = "x' OR '1'='1' -- ";
$password = "anything";

// UNSAFE: the input becomes part of the SQL text (like the lab's login.php)
$query = "SELECT * FROM users WHERE email='$email' AND password='$password'";
echo $query . "\n";
$rows = $db->query($query)->fetchAll(PDO::FETCH_ASSOC);
echo "Rows found: " . count($rows) . ", logged in as: " . $rows[0]['username'] . "\n";

// SAFE: a prepared statement keeps the input as data only
$stmt = $db->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
echo "Prepared statement, rows found: " . count($stmt->fetchAll()) . "\n";
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 14

The attacker's quote ' closes the email string; OR '1'='1' is always true; -- turns the rest (the password test) into a comment.

'ـی هێرشبەر دادەخات؛ OR '1'='1' هەمیشە ڕاستە؛ -- باقی دێڕەکە (پشکنینی ) دەکات بە .

Lineدێڕی 20-21

With a prepared statement the whole text is just an email address, and no user has it.

بە هەموو دەقەکە تەنها ناونیشانێکی ئیمەیڵە و هیچ ئەوەی نییە.

The student's version falls the same way with the username admin' -- : the comment removes the password check, so the attacker becomes admin:

وەشانەکەی خوێندکار بە هەمان شێوە بە ناوی admin' -- دەڕووخێت: پشکنینی لادەبات، بۆیە هێرشبەر دەبێت بە admin:

injection2.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
$db = new PDO('sqlite::memory:');
$db->exec("CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, password TEXT)");
$db->exec("INSERT INTO users (username, password) VALUES ('admin', '" . md5('k8#Lq2!z') . "')");
$username = "admin' -- ";
$password = md5("I do not know it");
$query = "select * from users where username ='$username' and password = '$password' ";
echo $query . "\n";
$rows = $db->query($query)->fetchAll(PDO::FETCH_ASSOC);
echo "Rows found: " . count($rows) . ", logged in as: " . $rows[0]['username'] . "\n";

The cure is always the same: never paste user input into SQL. Use a prepared statement, look the user up by email only, and check the password in PHP with password_verify(). More about this in Database security.

چارەسەرەکە هەمیشە هەمانە: هەرگیز ڕاستەوخۆ مەخەرە ناو SQL. بەکاربهێنە، تەنها بە بدۆزەوە و لە PHPـدا بە password_verify() بپشکنە. زیاتر لە ئاسایشی بنکەدراوە.

Quick checkپشکنینی خێراIn the attack email x' OR '1'='1' -- , what does -- do?لە هێرشی x' OR '1'='1' -- دا، -- چی دەکات؟

Hacking Websites with SQL Injection - ComputerphileComputerphile · Englishئینگلیزی · 2.5M viewsبینین · 51K likesلایک · Partبەشی 2:17–8:43 (ofلە 8:59)

How SQL injection changes a PHP database query, why escaping is only a weak fix, and why prepared statements are the right one.

چۆن لە دەگۆڕێت، بۆچی escaping تەنها چارەسەرێکی لاوازە و بۆچی چارەسەری ڕاستن.

Watch 2:17–8:43: how injection works and how prepared statements stop it.

لە ⁦2:17⁩ تا ⁦8:43⁩ سەیر بکە: چۆن کار دەکات و چۆن ڕێگەی لێ دەگرن.

Step 3: the login pageهەنگاوی ٣: لاپەڕەی چوونەژوورەوە

The corrected login.php⁦login.php⁩ـی ڕاستکراو

Here is the whole corrected login.php. It keeps the lab's steps (form, SELECT, exactly one row, session, redirect) and fixes every problem shown above:

ئەمە هەموو login.phpـی ڕاستکراوە. هەنگاوەکانی تاقیگەکە دەهێڵێتەوە (، SELECT، تەنها یەک ڕیز، ، ) و هەموو ئەو کێشانەی سەرەوە چاک دەکات:

login.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();              // before any output
require 'connection.php';

// Already logged in? Go straight to the protected page.
if (isset($_SESSION['user_id'])) {
    header('Location: index.php');
    exit;
}

$message = '';

if (isset($_POST['loginBtn'])) {
    $email    = trim($_POST['emailInput'] ?? '');
    $password = $_POST['passwordInput'] ?? '';

    if ($email === '') {
        $message = 'Please enter your email address.';
    } elseif ($password === '') {
        $message = 'Please enter your password.';
    } else {
        // Find the user by email only; the password is checked in PHP below
        $stmt = mysqli_prepare($connect, 'SELECT id, username, password FROM users WHERE email = ?');
        mysqli_stmt_bind_param($stmt, 's', $email);
        mysqli_stmt_execute($stmt);
        $result = mysqli_stmt_get_result($stmt);

        // Exactly one row: a user with this email exists (email is UNIQUE)
        if (mysqli_num_rows($result) === 1) {
            $row = mysqli_fetch_array($result, MYSQLI_ASSOC);   // the row as an associative array

            if (password_verify($password, $row['password'])) {
                // HERE WE CREATE THE SESSION
                session_regenerate_id(true);          // new session id after login
                $_SESSION['user_id']  = $row['id'];
                $_SESSION['username'] = $row['username'];
                header('Location: index.php');
                exit;                                 // stop: nothing else may run
            }
        }
        // One message for "unknown email" and "wrong password"
        $message = 'Wrong email or password.';
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Log in</title>
</head>
<body>
    <h1>Log in</h1>
    <p><?php echo htmlspecialchars($message); ?></p>
    <form method="post">
        Email: <input type="email" name="emailInput"><br>
        Password: <input type="password" name="passwordInput"><br>
        <input type="submit" name="loginBtn" value="Login">
    </form>
    <p>No account yet? <a href="register.php">Create one</a></p>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

session_start() on the first line, before any output.

session_start() لە یەکەم دێڕدا، پێش هەر .

Lineدێڕی 6-9

Already logged in? Do not show the form again.

پێشتر چوویتەتە ژوورەوە؟ دیسان پیشان مەدە.

Lineدێڕی 23-26

A prepared SELECT by email only. mysqli_stmt_get_result() gives a normal result (it needs the mysqlnd driver, which XAMPP includes).

SELECTێکی ئامادەکراو تەنها بە . mysqli_stmt_get_result() ئەنجامێکی ئاسایی دەدات (پێویستی بە mysqlnd هەیە، کە XAMPP تێیدایە).

Lineدێڕی 29-30

The lab's own check: exactly one row, fetched as an associative array.

پشکنینەکەی خودی تاقیگەکە: تەنها یەک ڕیز، وەک هێنراوە.

Lineدێڕی 32

password_verify() compares the typed password with the stored hash.

password_verify() نووسراوەکە لەگەڵ هاشە خەزنکراوەکە بەراورد دەکات.

Lineدێڕی 34

New session ID after login.

نوێ دوای .

Lineدێڕی 42

One message for both cases, so attackers cannot test which emails exist.

یەک پەیام بۆ هەردوو حاڵەتەکە، بۆ ئەوەی هێرشبەران نەتوانن تاقی بکەنەوە کام هەن.

Lineدێڕی 56-57

type="password" hides what is typed.

type="password" ئەوەی دەنووسرێت دەشارێتەوە.

The corrected login.php, tested (PHP 8.5 with an SQLite stand-in for MySQL)⁦login.php⁩ـی ڕاستکراو، تاقیکراوە (⁦PHP 8.5⁩ لەگەڵ SQLite لە جیاتی MySQL)
RequestServer response
POST: right email, wrong passwordPOST: ڕاست، هەڵە200 — the form again, with “Wrong email or password.”⁦200⁩ — دیسان، لەگەڵ «⁦Wrong email or password⁩.»
POST: email x' OR '1'='1' -- POST: هێرش بە x' OR '1'='1' -- 200 — “Wrong email or password.” (the attack fails)⁦200⁩ — «⁦Wrong email or password⁩.» (هێرشەکە شکست دەهێنێت)
POST: right email and passwordPOST: و ڕاست302, Location: index.php, and a new Set-Cookie: PHPSESSID=… (the regenerated ID)⁦302⁩، Location: index.php و Set-Cookie: PHPSESSID=…ی نوێ (ناسێنەرە نوێکراوەکە)
GET login.php while logged in⁦GET login.php⁩ لە کاتی بوون لە ژوورەوە302, Location: index.php⁦302⁩، Location: index.php
Quick checkپشکنینی خێراWhich is the safe way to check a password at login?ڕێگای سەلامەت بۆ پشکنینی لە کاتی کامەیە؟

Step 5: protect every other pageهەنگاوی ٥: هەموو لاپەڕەکانی تر بپارێزە

After a user logs in, every other page must check whether the session exists. If it does, the user may see the page. If the user has not logged in, the page must send them to the login page first. For example, to protect index.php, call session_start() and then check the session. The lab prints this:

دوای ئەوەی دەچێتە ژوورەوە، هەموو لاپەڕەکانی تر دەبێت بپشکنن ئایا هەیە. ئەگەر هەبێت، بۆی هەیە لاپەڕەکە ببینێت. ئەگەر نەچووبێتە ژوورەوە، لاپەڕەکە دەبێت سەرەتا بینێرێت بۆ لاپەڕەی . بۆ نموونە، بۆ پاراستنی index.php، session_start() بانگ بکە و پاشان بپشکنە. تاقیگەکە ئەمە چاپ دەکات:

check (lab PDF, as printed)TEXT
         Session_start();
If(! isset($_SESSION[‘user_id’])){
         Header(‘Location:login.php’);
}
index.php (lab PDF, as printed)TEXT
<? Php
        Session_start();
        If(! isset($_SESSION[‘user_id’])){
        Header(‘Location:login.php’);
}

echo “<h1> Welcome to website</h1>”;




?>
What PHP 8.5 does with the lab’s index.php⁦PHP 8.5⁩ چی لەگەڵ ⁦index.php⁩ـی تاقیگەکە دەکات
With short_open_tag = On:
Parse error: syntax error, unexpected identifier "Session_start" in index.php on line 2

With short_open_tag = Off (the php.ini default):
the whole file is sent to the browser as plain text, PHP code included.

SessionLab's index.php uses straight quotes and runs, but it also forgets exit:

index.phpـی SessionLab ڕاست بەکار دەهێنێت و کار دەکات، بەڵام ئەویش exitی لە بیر دەچێت:

index.php (SessionLab)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
    session_start();
    if(!isset($_SESSION['user_id'])){
        header('Location: login.php');
    }
    echo "<h1>Welcome to website</h1>";

    echo "<a href='logout.php'>Logout</a>";
?>

Step 5: protect every other pageهەنگاوی ٥: هەموو لاپەڕەکانی تر بپارێزە

The corrected index.php⁦index.php⁩ـی ڕاستکراو

The corrected index.php checks the session, redirects and stops with exit, and escapes the name it prints:

index.phpـی ڕاستکراو دەپشکنێت، سەردانکەر ئاڕاستە دەکاتەوە و دەوەستێت بە exit و ئەو ناوەی چاپی دەکات دەیپارێزێت:

index.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();

// Not logged in? Send the visitor to the login page and STOP the script.
if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Home</title>
</head>
<body>
    <h1>Welcome to the website, <?php echo htmlspecialchars($_SESSION['username']); ?>!</h1>
    <p><a href="logout.php">Logout</a></p>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

Open the session (before any output).

بکەرەوە (پێش هەر ).

Lineدێڕی 5

No user_id in the session: this visitor has not logged in.

هیچ user_idێک لە نییە: ئەم سەردانکەرە نەچووەتە ژوورەوە.

Lineدێڕی 7

Stop. Nothing below this line is sent.

بوەستە. هیچ شتێکی خوار ئەم دێڕە نانێردرێت.

Lineدێڕی 17

Greet the user by name, escaped.

بە ناوی خۆیەوە بەخێرهاتنی بکە، بە پاراستنەوە.

Quick checkپشکنینی خێراA visitor who is not logged in opens SessionLab's original index.php. What does the response contain?سەردانکەرێکی نەچووەژوورەوە index.phpـی ئەسڵیی SessionLab دەکاتەوە. چی تێدایە؟

Step 5: protect every other pageهەنگاوی ٥: هەموو لاپەڕەکانی تر بپارێزە

One check for all pages: auth.phpیەک پشکنین بۆ هەموو لاپەڕەکان: ⁦auth.php⁩

A real site has many protected pages. Instead of copying the check into each one, put it in auth.php and require it on the first line of every protected page. If you ever improve the check, you change one file.

ڕاستەقینە زۆر لاپەڕەی پارێزراوی هەیە. لە جیاتی کۆپیکردنی پشکنینەکە بۆ هەریەکەیان، بیخە ناو auth.php و لە یەکەم دێڕی هەموو لاپەڕەیەکی پارێزراودا requireی بکە. ئەگەر ڕۆژێک پشکنینەکەت باشتر کرد، تەنها یەک دەگۆڕیت.

auth.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// auth.php: require this file at the very top of every protected page
session_start();

if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}
profile.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require 'auth.php';   // only logged-in users get past this line
?>
<!DOCTYPE html>
<html>
<body>
    <h1>Your profile</h1>
    <p>You are logged in as <?php echo htmlspecialchars($_SESSION['username']); ?>.</p>
</body>
</html>

Final step: the logout pageهەنگاوی کۆتایی: لاپەڕەی چوونەدەرەوە

In every web-based project, when users log out of their account, the application should delete the session. The lab's logout.php (the same in the PDF and in SessionLab) does this:

لە هەموو پرۆژەیەکی وێبیدا، کاتێک بەکارهێنەران لە دەچنە دەرەوە، دەبێت بسڕێتەوە. logout.phpـی تاقیگەکە (لە PDF و SessionLabـدا وەک یەکە) ئەمە دەکات:

logout.php (lab)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
 session_start();
//clear session variable
unset($_SESSION["user_id"]);
//destroy session on the server
session_destroy();
//redirect user to login page
header("Location: login.php");

A student's version clears all variables with session_unset() before destroying the session:

وەشانی خوێندکارێک پێش لەناوبردنی بە session_unset() هەموو بەتاڵ دەکات:

logout.php (student project)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php


session_start();

session_unset();

session_destroy();

header('location: login.php');



?>
logout.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();                 // open the current session so that we can end it

$_SESSION = [];                  // 1. remove all session variables

// 2. delete the session cookie in the browser (same name, path and domain; time in the past)
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
    $params['path'], $params['domain'], $params['secure'], $params['httponly']);

session_destroy();               // 3. delete the session data on the server

header('Location: login.php');   // 4. back to the login page
exit;
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

We need the session open to end it.

دەبێت کراوە بێت بۆ ئەوەی کۆتایی پێ بهێنین.

Lineدێڕی 4

1. Remove all variables.

١. هەموو لاببە.

Lineدێڕی 7-9

2. Delete the session cookie with the same name, path and domain.

٢. بە هەمان ناو، و دۆمەین بسڕەوە.

Lineدێڕی 11

3. Delete the data on the server.

٣. لەسەر بسڕەوە.

Lineدێڕی 13-14

4. Back to the login page, and stop.

٤. گەڕانەوە بۆ لاپەڕەی و وەستان.

Quick checkپشکنینی خێراWhat does the lab's logout.php leave behind?logout.phpـی تاقیگەکە چی بەجێ دەهێڵێت؟

Try the whole systemهەموو سیستەمەکە تاقی بکەرەوە

  1. Start XAMPPXAMPP دەست پێبکە

    Open the XAMPP Control Panel and start Apache and MySQL (XAMPP).

    XAMPP بکەرەوە و Apache و MySQL دەست پێبکە (XAMPP).

  2. Create the databaseبنکەدراوە دروست بکە

    In phpMyAdmin (http://localhost/phpmyadmin), run the SQL from Database and table.

    لە phpMyAdminدا (http://localhost/phpmyadmin)، SQLـەکەی بنکەدراوە و خشتە کارپێبکە.

  3. Copy the filesفایلەکان کۆپی بکە

    Put connection.php, register.php, login.php, index.php, logout.php (and auth.php) into a folder such as htdocs/session-lab/.

    connection.php، register.php، login.php، index.php، logout.php (و auth.php) بخە ناو فۆڵدەرێکی وەک htdocs/session-lab/.

  4. Registerخۆت تۆمار بکە

    Open http://localhost/session-lab/register.php and create an account. Look at the users table in phpMyAdmin: the password column holds a hash that starts with $2y$.

    http://localhost/session-lab/register.php بکەرەوە و هەژمارێک دروست بکە. لە phpMyAdminدا سەیری users بکە: password هاشێکی تێدایە کە بە $2y$ دەست پێدەکات.

  5. Try the protectionپاراستنەکە تاقی بکەرەوە

    Open index.php directly: you land on login.php. Log in with a wrong password, then with the right one.

    ڕاستەوخۆ index.php بکەرەوە: دەگەیتە login.php. بە هەڵە بچۆ ژوورەوە، پاشان بە ڕاستەکە.

  6. Watch the cookieسەیری کووکییەکە بکە

    In the developer tools (F12 → Application/Storage → Cookies), note the PHPSESSID value before and after login: it changes. After logout it is gone.

    لە ئامرازەکانی پەرەپێدەردا (⁦F12⁩ ← ⁦Application/Storage⁩ ← Cookies)، PHPSESSID پێش و دوای بنووسە: دەگۆڕێت. دوای نامێنێت.

We ran the corrected files through PHP 8.5 request by request. This is what a browser receives:

فایلە ڕاستکراوەکانمان بە بە ⁦PHP 8.5⁩ کارپێکرد. ئەمە ئەوەیە کە وەریدەگرێت:

A full test runتاقیکردنەوەیەکی تەواو
#ژ.RequestStatusدۆخImportant headersسەردێڕە گرنگەکانThe page showsلاپەڕەکە پیشان دەدات
1GET /index.php302Set-Cookie: PHPSESSID=1b1e…; Location: login.php(nothing, redirect)(هیچ، )
2POST /register.php (passwords differ)⁦POST /register.php⁩ ( جیاوازن)200—The passwords did not match. Please try again.
3POST /register.php (correct)⁦POST /register.php⁩ (دروست)200—Account created successfully. You can log in now.
4POST /login.php (wrong password)⁦POST /login.php⁩ ( هەڵە)200—Wrong email or password.
5POST /login.php (correct)⁦POST /login.php⁩ (دروست)302Set-Cookie: PHPSESSID=5642… Location: index.php(redirect; the session ID changed)(؛ گۆڕا)
6GET /index.php200—Welcome to the website, Aram!
7GET /logout.php302Set-Cookie: PHPSESSID=deleted; Max-Age=0 Location: login.php(redirect to the login page)( بۆ لاپەڕەی )
8GET /index.php302Set-Cookie: PHPSESSID=7b67…; Location: login.php(redirect: logged out)(: چووەتە دەرەوە)

Admins and users: a login with rolesبەڕێوەبەر و بەکارهێنەر: چوونەژوورەوە بە ڕۆڵ

A student's project has two kinds of users, “Admin” and “User”. The login form has a where the visitor chooses one, and the query checks it. This is the part of the code that is visible in the screenshot (one long line is cut off):

پرۆژەی خوێندکارێک دوو جۆر هەیە، «Admin» و «User». هەیە کە سەردانکەر یەکێکیان تێدا هەڵدەبژێرێت و دەیپشکنێت. ئەمە ئەو بەشەی کۆدەکەیە کە لە وێنەکەدا دیارە (دێڕێکی درێژ بڕاوە):

login.php (student project, part)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
include 'connection.php';
if (isset($_POST['LoginButton']))
{
    $username = $_POST['usernameInput'];
    $password = $_POST['passwordInput'];
    $adminOrUser = $_POST['AdminOrUserRadio'];
    $selectQuery = mysqli_query($connect, "select * from UserManagementTable where Username='$username' and Password='$password' and AdminOrUser='$adminOrUser'"   // (the line is cut off in the screenshot)

    while ($row = mysqli_fetch_assoc($selectQuery))
    {
        if ($selectQuery)
        {
            if ($row['AdminOrUser'] == "Admin")
            {
                //If admin logged in
                //echo "Admin with the username of $username and password of $password has logged in.";

                // setcookie("user_id", $row['UserID'], time() + 1000);
                // setcookie("username", $row['Username'], time() + 1000);

                session_start();
                $_SESSION["user_id"] = $row['UserID'];
                $_SESSION["username"] = $row['Username'];

                header('Location: usermanagement.php');
                exit();
            }
            else if ($row['AdminOrUser'] == "User")
            {
                //If user logged in
                echo "A user with the username of $username and password of $password has logged in.";
            }
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 6

The role comes from a radio button, so the visitor chooses it.

ڕۆڵەکە لە دێت، بۆیە سەردانکەر هەڵیدەبژێرێت.

Lineدێڕی 7

Plain-text password and SQL injection in one line.

دەقی سادە و لە یەک دێڕدا.

Lineدێڕی 11

Always true inside the loop: the loop runs only when there are rows.

لە ناو هەمیشە ڕاستە: تەنها کاتێک کار دەکات کە ڕیز هەبێت.

Lineدێڕی 31

It prints the password back on the screen.

دیسان لەسەر شاشەکە چاپ دەکات.

Admins and users: a login with rolesبەڕێوەبەر و بەکارهێنەر: چوونەژوورەوە بە ڕۆڵ

Roles done safelyڕۆڵ بە شێوەی سەلامەت

The corrected design: add a role column to the users table, let the database decide each user's role, copy it into the session at login, and check it on admin pages. Checking the role is ; checking the login is .

دیزاینی ڕاستکراو: role بۆ users زیاد بکە، با ڕۆڵی هەر دیاری بکات، لە کاتی بیگوازەرەوە بۆ ناو و لە لاپەڕەکانی بەڕێوەبەردا بیپشکنە. پشکنینی ڕۆڵ ؛ پشکنینی .

roles.sqlSQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
ALTER TABLE users ADD role VARCHAR(10) NOT NULL DEFAULT 'user';

-- make one account an administrator
UPDATE users SET role = 'admin' WHERE email = 'shilan@example.com';

In login.php, select the role as well (SELECT id, username, password, role FROM users WHERE email = ?), and replace the success block with this:

لە login.phpـدا ڕۆڵەکەش هەڵبژێرە (SELECT id, username, password, role FROM users WHERE email = ?) و بلۆکی سەرکەوتن بەمە بگۆڕە:

login.php (success block with roles)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
if (password_verify($password, $row['password'])) {
    session_regenerate_id(true);
    $_SESSION['user_id']  = $row['id'];
    $_SESSION['username'] = $row['username'];
    $_SESSION['role']     = $row['role'];     // from the database, not from the form

    if ($row['role'] === 'admin') {
        header('Location: usermanagement.php');
    } else {
        header('Location: index.php');
    }
    exit;
}
usermanagement.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();

// 1. Authentication: is anybody logged in?
if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}

// 2. Authorization: is this user allowed to see this page?
if (($_SESSION['role'] ?? '') !== 'admin') {
    http_response_code(403);   // 403 = Forbidden
    exit('Only administrators can open this page.');
}
?>
<!DOCTYPE html>
<html>
<body>
    <h1>User management</h1>
    <p>Welcome, administrator <?php echo htmlspecialchars($_SESSION['username']); ?>.</p>
</body>
</html>
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5-8

Authentication: anybody logged in?

: کەسێک چووەتە ژوورەوە؟

Lineدێڕی 11-14

Authorization: is this user an admin? If not, answer 403 Forbidden and stop.

: ئەم بەکارهێنەرە بەڕێوەبەرە؟ ئەگەر نا، بە ⁦403 Forbidden⁩ بدەرەوە و بوەستە.

Definitionپێناسە

Deciding what a logged-in user is allowed to see or do, for example letting only an admin delete users. It comes after .

بڕیاردانە لەسەر ئەوەی بەکارهێنەرێکی چووەژوورەوە بۆی هەیە چی ببینێت یان چی بکات، بۆ نموونە تەنها بەڕێوەبەر بتوانێت بەکارهێنەرەکان بسڕێتەوە. دوای دێت.

Quick checkپشکنینی خێراWhere should a user's role (admin or user) come from at login?لە کاتی ڕۆڵی (بەڕێوەبەر یان بەکارهێنەر) دەبێت لە کوێوە بێت؟

From the lab code to safe codeلە کۆدی تاقیگەکەوە بۆ کۆدی سەلامەت

A security checklist for login systemsلیستی پشکنین بۆ ئاسایشی سیستەمەکانی
Lab / SessionLabتاقیگە / SessionLabWhy it is dangerousبۆچی مەترسیدارەSafe versionوەشانی سەلامەت
Passwords in plain text, sha1() or md5() بە دەقی سادە، sha1() یان md5()A stolen table reveals or quickly gives up every password دزراو هەموو ئاشکرا دەکات یان بە خێرایی دەیانداتpassword_hash() / password_verify()
User input pasted into SQL ڕاستەوخۆ لە ناو SQLSQL injection: log in as anyone without a password: وەک هەر کەسێک بەبێ بچۆ ژوورەوەmysqli_prepare() + mysqli_stmt_bind_param()
session_start() / header() after HTMLsession_start() / header() دوای HTMLWorks only with output buffering; breaks elsewhereتەنها بە کار دەکات؛ لە شوێنی تر تێک دەچێتPHP block at the top of the fileبلۆکی PHP لە سەرەوەی
No exit after header('Location: …')هیچ exitێک دوای header('Location: …')The protected page is still sentلاپەڕە پارێزراوەکە هێشتا دەنێردرێتheader(…); exit;
Same session ID before and after loginهەمان پێش و دوای Session fixationsession_regenerate_id(true)
user_id in a plain cookieuser_id لە سادەداAnyone can type user_id=1هەر کەسێک دەتوانێت user_id=1 بنووسێت$_SESSION['user_id']$_SESSION['user_id']
Logout without deleting the cookie, without exit بەبێ سڕینەوەی ، بەبێ exitThe old session ID stays usable as an empty sessionناسێنەرە کۆنەکەی وەک بەتاڵ بەکارهێنراو دەمێنێتەوە$_SESSION = [], delete the cookie, session_destroy(), exit$_SESSION = []، سڕینەوەی ، session_destroy()، exit
“error occured” or the MySQL error for a wrong password«⁦error occured⁩» یان MySQL بۆ هەڵەConfuses users, leaks detailsبەکارهێنەران سەرلێشێواو دەکات، وردەکاری ئاشکرا دەکات“Wrong email or password.”پەیامی «⁦Wrong email or password⁩.»
Password field type="text" type="text"The password is visible on the screen لەسەر شاشەکە دەبینرێتtype="password"
Printing user data without escapingچاپکردنی بەبێ پاراستنXSSهێرشی XSShtmlspecialchars()

Common mistakesهەڵە باوەکان

  • Curly quotes from a word processor (‘ ’ “ ”) in code: fatal errors. Copy code from a code editor, not from Word or a PDF.
  • <? Php instead of <?php: a parse error, or the code is shown as text.
  • Forgetting session_start() on a protected page: $_SESSION does not exist on that page, so the check fails and every visitor is sent to the login page, even after logging in.
  • Different key names, such as $_SESSION['userId'] in login.php and $_SESSION['user_id'] in index.php. Use one name everywhere.
  • A password column that is too short (for example VARCHAR(40)): password_hash() needs VARCHAR(255), or the hash is cut and no login ever works.
  • Comparing the hash in SQL (WHERE password = ? with a new password_hash()): it never matches, because every hash has its own salt. Use password_verify().
  • File-name case: View.php and view.php are the same file on Windows and macOS, but different on a Linux server.
  • خوار لە پرۆگرامی نووسینەوە (‘ ’ “ ”) لە کۆددا: . کۆد لە کۆپی بکە، نەک لە Word یان PDF.
  • <? Php لە جیاتی <?php: ، یان کۆدەکە وەک دەق پیشان دەدرێت.
  • لە بیرچوونی session_start() لە لاپەڕەیەکی پارێزراودا: $_SESSION لەو لاپەڕەیەدا بوونی نییە، بۆیە پشکنینەکە شکست دەهێنێت و هەموو سەردانکەرێک دەنێردرێت بۆ لاپەڕەی ، تەنانەت دوای چوونەژوورەوەش.
  • ناوی جیاواز بۆ ، وەک $_SESSION['userId'] لە ⁦login.php⁩ و $_SESSION['user_id'] لە ⁦index.php⁩ـدا. لە هەموو شوێنێک یەک ناو بەکاربهێنە.
  • زۆر کورت (بۆ نموونە VARCHAR(40)): password_hash() پێویستی بە VARCHAR(255) هەیە، ئەگینا هاشەکە دەبڕدرێت و هیچ هەرگیز کار ناکات.
  • بەراوردکردنی هاش لە SQLـدا (WHERE password = ? لەگەڵ password_hash()ێکی نوێ): هەرگیز یەک ناگرێتەوە، چونکە هەر هاشێک خۆی هەیە. password_verify() بەکاربهێنە.
  • قەبارەی پیتی ناوی : View.php و view.php لەسەر Windows و macOS یەک فایلن، بەڵام لەسەر Linux جیاوازن.

Lab tasks and exercisesئەرکەکانی تاقیگە و ڕاهێنانەکان

These are the tasks of Lab 9, in order, with worked answers. Together they build SessionLab.

ئەمانە ئەرکەکانی تاقیگەی ٩ن، بە ڕیزبەندی، لەگەڵ وەڵامی تەواو. پێکەوە SessionLab دروست دەکەن.

Lab tasks and exercisesئەرکەکانی تاقیگە و ڕاهێنانەکان

Lab tasks 1–8: the session versionئەرکەکانی تاقیگە ١–٨: وەشانی سێشن

ExerciseڕاهێنانLab task 1: the databaseئەرکی تاقیگە ١: بنکەدراوە

Create a database in MySQL with the name session.

لە MySQLـدا بە ناوی session دروست بکە.

Show answerوەڵامەکە پیشان بدە
SQLRuns in the databaseلە ناو بنکەدراوە کار دەکات
CREATE DATABASE session;

Or in phpMyAdmin: Databases → name session → Create.

یان لە phpMyAdminدا: Databases ← ناوی session ← Create.

ExerciseڕاهێنانLab task 2: the users tableئەرکی تاقیگە ٢: خشتەی بەکارهێنەران

Create the table for the users, with the fields id, username, email and password.

بۆ بەکارهێنەران دروست بکە، بە id، username، email و password.

Show answerوەڵامەکە پیشان بدە

See Database and table: id is an INT AUTO_INCREMENT PRIMARY KEY, email is UNIQUE, and password is VARCHAR(255) for the hash.

بنکەدراوە و خشتە ببینە: id بریتییە لە INT AUTO_INCREMENT PRIMARY KEY، email UNIQUEـە و password بۆ هاشەکە VARCHAR(255)ـە.

ExerciseڕاهێنانLab task 3: connection.phpئەرکی تاقیگە ٣: ⁦connection.php⁩

Create a connection file in your NetBeans project and name it connection.php.

لە پرۆژەی NetBeansـەکەتدا پەیوەندی دروست بکە و ناوی بنێ connection.php.

Show answerوەڵامەکە پیشان بدە
connection.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// connection.php: the database connection, included by the other pages.
// Since PHP 8.1, mysqli throws an exception when something fails. We say so clearly:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

try {
    // server, MySQL user, password, database name (XAMPP: user "root", empty password)
    $connect = mysqli_connect('localhost', 'root', '', 'session');
    mysqli_set_charset($connect, 'utf8mb4');
} catch (mysqli_sql_exception $e) {
    // Do not show technical details to visitors
    die('Cannot connect to the database. Is MySQL running?');
}

Explained in Step 1.

لە هەنگاوی ١دا ڕوون کراوەتەوە.

ExerciseڕاهێنانLab task 4: register.phpئەرکی تاقیگە ٤: ⁦register.php⁩

Create register.php, used by people who want to create an account. Its HTML form asks for username, email address, password and password confirmation.

register.php دروست بکە، کە ئەو کەسانە بەکاری دەهێنن کە دەیانەوێت هەژمار دروست بکەن. HTMLـەکەی ناوی ، ، و دووپاتکردنەوەی دەپرسێت.

Show answerوەڵامەکە پیشان بدە

The full answer is the corrected register.php in Step 2. Its heart:

وەڵامی تەواو register.phpـی ڕاستکراوە لە هەنگاوی ٢دا. ناوەڕۆکی سەرەکیی:

register.php (core)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = mysqli_prepare($connect, 'INSERT INTO users (username, email, password) VALUES (?, ?, ?)');
mysqli_stmt_bind_param($stmt, 'sss', $username, $email, $hash);
mysqli_stmt_execute($stmt);
ExerciseڕاهێنانLab task 5: login.phpئەرکی تاقیگە ٥: ⁦login.php⁩

Create login.php, used when users want to log in. It needs an HTML form asking for email and password, and PHP code for logging in that uses a SELECT query.

login.php دروست بکە، کە کاتێک بەکارهێنەران دەیانەوێت بچنە ژوورەوە بەکار دێت. پێویستی بە HTML هەیە کە و بپرسێت و کۆدی PHP بۆ کە SELECT بەکار دەهێنێت.

Show answerوەڵامەکە پیشان بدە

See The corrected login.php. The query looks the user up by email only, with a prepared statement; the password is then checked with password_verify().

⁦login.php⁩ـی ڕاستکراو ببینە. تەنها بە و بە بەدوای بەکارهێنەرەکەدا دەگەڕێت؛ پاشان بە password_verify() دەپشکنرێت.

ExerciseڕاهێنانLab task 6: create the sessionئەرکی تاقیگە ٦: سێشنەکە دروست بکە

In login.php, inside the success check, create the session for the user who logged in successfully, and redirect to index.php.

لە login.phpـدا، لە ناو پشکنینی سەرکەوتندا، بۆ ئەو بەکارهێنەرە دروست بکە کە بە سەرکەوتوویی چووەتە ژوورەوە و ئاڕاستە بکەرەوە بۆ index.php.

Show answerوەڵامەکە پیشان بدە
login.php (success)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
session_regenerate_id(true);
$_SESSION['user_id']  = $row['id'];
$_SESSION['username'] = $row['username'];
header('Location: index.php');
exit;

session_start() is already on line 2 of the file. See Step 4.

session_start() پێشتر لە دێڕی 2ی فایلەکەدایە. هەنگاوی ٤ ببینە.

ExerciseڕاهێنانLab task 7: check the sessionئەرکی تاقیگە ٧: سێشنەکە بپشکنە

Check the session in all other pages of the project. If the user has not logged in, redirect to the login page. Example: index.php.

لە هەموو لاپەڕەکانی تری پرۆژەکەدا بپشکنە. ئەگەر نەچووبێتە ژوورەوە، ئاڕاستە بکەرەوە بۆ لاپەڕەی . نموونە: index.php.

Show answerوەڵامەکە پیشان بدە
auth.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// auth.php: require this file at the very top of every protected page
session_start();

if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}

Put require 'auth.php'; on the first line of every protected page (Step 5).

require 'auth.php'; لە یەکەم دێڕی هەموو لاپەڕەیەکی پارێزراودا دابنێ (هەنگاوی ٥).

ExerciseڕاهێنانLab task 8: logout.phpئەرکی تاقیگە ٨: ⁦logout.php⁩

When users log out, delete the session (and any cookie) you created. Create logout.php.

کاتێک بەکارهێنەران دەچنە دەرەوە، ئەو (و هەر ) بسڕەوە کە دروستت کردووە. logout.php دروست بکە.

Show answerوەڵامەکە پیشان بدە
logout.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
session_start();                 // open the current session so that we can end it

$_SESSION = [];                  // 1. remove all session variables

// 2. delete the session cookie in the browser (same name, path and domain; time in the past)
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
    $params['path'], $params['domain'], $params['secure'], $params['httponly']);

session_destroy();               // 3. delete the session data on the server

header('Location: login.php');   // 4. back to the login page
exit;

Explained in Final step.

لە هەنگاوی کۆتاییدا ڕوون کراوەتەوە.

Lab tasks and exercisesئەرکەکانی تاقیگە و ڕاهێنانەکان

More exercises and a quick checkڕاهێنانی زیاتر و پشکنینی خێرا

ExerciseڕاهێنانRemember the email addressناونیشانی ئیمەیڵ لە بیر بگرە

Make the login form remember the last email address for 30 days, so the user does not have to type it again. The cookie must not log anybody in; it only fills in the email field.

وا بکە دوایین بۆ ٣٠ ڕۆژ لە بیر بگرێت، بۆ ئەوەی پێویست نەکات دیسان بینووسێتەوە. نابێت کەس بخاتە ژوورەوە؛ تەنها خانەی پڕ دەکاتەوە.

Hintئاماژە

A cookie is fine for conveniences that are harmless if someone changes them. Escape the value when you print it.

بۆ ئەو ئاسانکارییانە باشە کە ئەگەر کەسێک بیانگۆڕێت زیانیان نییە. کاتێک چاپی دەکەیت بپارێزە.

Show answerوەڵامەکە پیشان بدە

Two changes in login.php. After a successful login, next to the session lines:

دوو گۆڕانکاری لە login.phpـدا. دوای سەرکەوتوو، لە تەنیشت دێڕەکانی :

login.php (after a successful login)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
// A harmless convenience cookie: only pre-fills the form, proves nothing
setcookie('last_email', $email, time() + 30 * 24 * 60 * 60, '', '', false, true);

And in the form:

و لە :

login.php (form)PHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
Email: <input type="email" name="emailInput" value="<?php echo htmlspecialchars($_COOKIE['last_email'] ?? ''); ?>"><br>
ExerciseڕاهێنانA profile pageلاپەڕەی پرۆفایل

Add profile.php, which only logged-in users may open, and which shows “You are logged in as (name).”. Reuse auth.php.

profile.php زیاد بکە، کە تەنها بەکارهێنەرانی چووەژوورەوە بۆیان هەیە بیکەنەوە و «⁦You are logged in as⁩ (ناو).» پیشان دەدات. auth.php دووبارە بەکاربهێنەرەوە.

Show answerوەڵامەکە پیشان بدە
profile.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
require 'auth.php';   // only logged-in users get past this line
?>
<!DOCTYPE html>
<html>
<body>
    <h1>Your profile</h1>
    <p>You are logged in as <?php echo htmlspecialchars($_SESSION['username']); ?>.</p>
</body>
</html>
Quick checkپشکنینی خێراWhich cookie is fine in a login system?کام لە سیستەمی گونجاوە؟

Summary and reviewکورتە و پێداچوونەوە

  • A login system has five files: connection.php, register.php, login.php, protected pages such as index.php (with auth.php), and logout.php.
  • Register: validate the input, password_hash(), INSERT with a prepared statement, handle a duplicate email.
  • Log in: SELECT by email with a prepared statement, password_verify(), session_regenerate_id(true), store the user id in $_SESSION, redirect and exit.
  • Protect: session_start(), check $_SESSION['user_id'], otherwise redirect and exit.
  • Log out: empty $_SESSION, delete the session cookie, session_destroy(), redirect and exit.
  • Never use a plain cookie such as user_id=1 as a login: it can be forged.
  • Roles come from the database; check them on every admin page (authorization).
  • سیستەمی پێنج هەیە: connection.php، register.php، login.php، لاپەڕە پارێزراوەکانی وەک index.php (لەگەڵ auth.php) و logout.php.
  • : پشتڕاست بکەرەوە، password_hash()، INSERT بە ، دووبارە مامەڵە بکە.
  • : SELECT بە و ، password_verify()، session_regenerate_id(true)، ناسێنەری لە $_SESSIONدا هەڵبگرە، ئاڕاستە بکەرەوە و exit.
  • پاراستن: session_start()، $_SESSION['user_id'] بپشکنە، ئەگینا ئاڕاستە بکەرەوە و exit.
  • : $_SESSION بەتاڵ بکە، بسڕەوە، session_destroy()، ئاڕاستە بکەرەوە و exit.
  • هەرگیز سادەی وەک user_id=1 وەک بەکار مەهێنە: دەتوانرێت ساختە بکرێت.
  • ڕۆڵەکان لە دێن؛ لە هەموو لاپەڕەیەکی بەڕێوەبەردا بیانپشکنە ().
Review questionsپرسیارەکانی پێداچوونەوە
What is the difference between authentication and authorization in this project?جیاوازی نێوان پشتڕاستکردنەوەی ناسنامە و ڕێگەپێدان لەم پرۆژەیەدا چییە؟

Authentication is login.php checking the email and password. Authorization is a page deciding whether this logged-in user may see it, for example usermanagement.php allowing only role = 'admin'.

ئەوەیە کە login.php و دەپشکنێت. ئەوەیە کە لاپەڕەیەک بڕیار دەدات ئایا ئەم بەکارهێنەرە چووەژوورەوەیە بۆی هەیە بیبینێت، بۆ نموونە usermanagement.php تەنها ڕێگە بە role = 'admin' دەدات.

Why does the lab PDF's login.php work on XAMPP but not on every server?بۆچی ⁦login.php⁩ـی PDFـی تاقیگەکە لەسەر XAMPP کار دەکات بەڵام لەسەر هەموو ڕاژەکارێک نا؟

Its form HTML is output before session_start() and header(). XAMPP's output buffering hides this; without buffering the headers are already sent and both calls fail. (It also has curly quotes, which must be fixed first.)

HTMLـی فۆرمەکەی پێش session_start() و header() . XAMPP ئەمە دەشارێتەوە؛ بەبێ ڕاگرتن سەردێڕەکان پێشتر نێردراون و هەردوو بانگکردنەکە شکست دەهێنن. (هەروەها خواری تێدایە، کە دەبێت سەرەتا ڕاست بکرێنەوە.)

How did the email x' OR '1'='1' -- log in to SessionLab?ئیمەیڵی x' OR '1'='1' -- چۆن چووە ناو SessionLabـەوە؟

The text was pasted into the SQL. The quote closed the email string, OR '1'='1' made the condition true for every row, and -- commented out the password test, so the first user was returned.

دەقەکە ڕاستەوخۆ خرایە ناو SQL. ئیمەیڵەکەی داخست، OR '1'='1' مەرجەکەی بۆ هەموو ڕیزێک ڕاست کرد و -- پشکنینی کرد بە ، بۆیە یەکەم گەڕێنرایەوە.

Why is the password column VARCHAR(255)?بۆچی ستوونی تێپەڕەوشە VARCHAR(255)ـە؟

It stores a password_hash() hash (60 characters with bcrypt, possibly longer with future algorithms), never the password itself.

هاشێکی password_hash() هەڵدەگرێت (٦٠ پیت بە bcrypt، لەوانەیە بە داهاتوو درێژتر)، هەرگیز خودی نا.

What three things does a complete logout do, and why each?چوونەدەرەوەیەکی تەواو چ سێ کارێک دەکات و بۆچی هەریەکەیان؟

It empties $_SESSION (no data is left for the rest of the request), deletes the PHPSESSID cookie (the browser forgets the ID), and calls session_destroy() (the data on the server is deleted). Then it redirects and exits.

$_SESSION بەتاڵ دەکات (هیچ بۆ باقی نامێنێتەوە)، PHPSESSID دەسڕێتەوە ( ناسێنەرەکە لە بیر دەکات) و session_destroy() بانگ دەکات ( سەر دەسڕدرێنەوە). پاشان ئاڕاستە دەکاتەوە و دەوەستێت.

Why should the login page say “Wrong email or password.” instead of “This email does not exist”?بۆچی لاپەڕەی چوونەژوورەوە دەبێت بڵێت «⁦Wrong email or password⁩.» لە جیاتی «⁦This email does not exist⁩»؟

So that an attacker cannot use the login form to find out which email addresses have accounts.

بۆ ئەوەی هێرشبەر نەتوانێت بەکاربهێنێت بۆ دۆزینەوەی ئەوەی کام هەژماریان هەیە.

Videoڤیدیۆ

Watch a videoسەیری ڤیدیۆیەک بکە

A video that viewers liked a lot and praised in their comments, chosen for this lesson.ڤیدیۆیەک کە بینەران زۆریان پێ خۆش بووە و لە کۆمێنتەکانیاندا ستایشیان کردووە، بۆ ئەم وانەیە هەڵبژێردراوە.

How To Create A Login System In PHP For Beginners | Procedural MySQLi | PHP TutorialDani Krossing · Englishئینگلیزی · 1.2M viewsبینین · 19K likesلایک · Partبەشی 1:26:47–1:54:12 (ofلە 1:57:38)

A login built step by step: find the user with a prepared statement, check password_verify(), save the user in the session, then log out.

هەنگاو بە هەنگاو سیستەمێکی دروست دەکات: دۆزینەوەی بە ، پشکنین بە password_verify()، هەڵگرتنی لە ، پاشان .

Watch 1:26:47–1:54:12: login script, logged-in pages and logout.

لە ⁦1:26:47⁩ تا ⁦1:54:12⁩ سەیر بکە: ، دوای و .

Lesson complete!وانەکە تەواو بوو!

Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.

Key termsزاراوە سەرەکییەکان

Next lessonوانەی داهاتوو

Web APIs and JSONAPIـی وێب و JSON

References and further readingسەرچاوەکان و خوێندنەوەی زیاتر

This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.