تقریباً هر وبسایت یا نرمافزار جدی که کاربران داشته باشد، به یک سیستم احراز هویت یا Authentication نیاز دارد.
وقتی کاربر در یک سایت ثبتنام میکند و بعد با ایمیل و رمز عبور وارد حساب خود میشود، در واقع یک فرآیند Authentication در حال انجام است.
در PHP میتوان سیستم احراز هویت را از صفر پیادهسازی کرد؛ از ثبتنام کاربر و ذخیره امن رمز عبور گرفته تا ورود، بررسی اطلاعات کاربر، ایجاد Session و خروج از حساب.
در این مقاله ابتدا مفهوم Authentication را بررسی میکنیم و سپس یک سیستم ورود و ثبتنام ساده با PHP و MySQL میسازیم.
Authentication در PHP چیست؟
Authentication به معنی احراز هویت است.
یعنی سیستم بررسی کند فردی که قصد ورود دارد، واقعاً همان کاربری است که ادعا میکند.
برای مثال وقتی کاربر این اطلاعات را وارد میکند:
Email:
saeed@example.com
Password:
123456
سیستم باید بررسی کند:
- آیا چنین کاربری وجود دارد؟
- آیا رمز عبور واردشده صحیح است؟
- اگر اطلاعات صحیح بود، کاربر را وارد حساب کند.
به صورت ساده:
User
↓
Email + Password
↓
PHP
↓
Database
↓
Verify
↓
Authenticated User
# Authentication و Authorization چه تفاوتی دارند؟
این دو مفهوم بسیار مهم هستند و نباید با یکدیگر اشتباه گرفته شوند.
Authentication
یعنی:
> شما چه کسی هستید؟
مثلاً:
Login
↓
Email + Password
↓
User identified
Authorization
یعنی:
> آیا اجازه انجام این کار را دارید؟
مثلاً:
Admin → حذف محصول
Customer → مشاهده محصول
ممکن است یک کاربر کاملاً احراز هویت شده باشد، اما اجازه دسترسی به پنل مدیریت را نداشته باشد.
بنابراین:
Authentication = شناسایی کاربر
Authorization = بررسی سطح دسترسی
# سیستم Authentication چگونه کار میکند؟
یک سیستم ساده ورود کاربران معمولاً چنین فرآیندی دارد:
ثبتنام
↓
ذخیره کاربر
↓
Hash کردن رمز عبور
↓
ورود
↓
دریافت Email + Password
↓
پیدا کردن کاربر
↓
بررسی Password
↓
ایجاد Session
↓
ورود به حساب کاربری
در سیستمهای API نیز ممکن است بعد از ورود، به جای Session یک Token در اختیار کاربر قرار بگیرد.
# چرا نباید رمز عبور را به صورت ساده ذخیره کنیم؟
فرض کنید کاربر رمز عبور زیر را انتخاب کرده است:
MyPassword123
نباید این مقدار را مستقیماً در دیتابیس ذخیره کنیم:
MyPassword123
اگر دیتابیس افشا شود، رمز عبور کاربران نیز در اختیار مهاجم قرار میگیرد.
روش صحیح این است که رمز عبور را با یک الگوریتم مناسب Hash کنیم.
مثلاً:
MyPassword123
↓
Password Hashing
↓
$2y$12$....
در PHP برای این کار توابع داخلی password_hash() و password_verify() وجود دارند. مستندات رسمی PHP نیز استفاده از password_hash() برای ایجاد Hash امن و password_verify() برای بررسی رمز عبور را توضیح میدهد.
# Hash چیست؟
Hash با Encryption متفاوت است.
Encryption معمولاً قابل رمزگشایی است:
Text
↓
Encrypt
↓
Encrypted Text
↓
Decrypt
↓
Text
اما Password Hashing به صورت یکطرفه طراحی شده است:
Password
↓
Hash
↓
Hash Value
هدف این نیست که Hash را دوباره به رمز عبور اصلی تبدیل کنیم.
هنگام ورود، رمز عبور واردشده را با Hash ذخیرهشده مقایسه میکنیم.
# password_hash در PHP چیست؟
برای Hash کردن رمز عبور میتوانیم از password_hash() استفاده کنیم:
$password = 'MyPassword123';
$hash = password_hash(
$password,
PASSWORD_DEFAULT
);
echo $hash;
خروجی چیزی شبیه این خواهد بود:
$2y$12$....
خود Hash شامل اطلاعات مورد نیاز برای بررسی رمز عبور است؛ بنابراین لازم نیست Salt را جداگانه در دیتابیس ذخیره کنیم.
همچنین نباید برای این کار خودمان یک Salt ثابت ایجاد کنیم.
# چرا نباید از MD5 برای رمز عبور استفاده کنیم؟
ممکن است کدی مانند این را در پروژههای قدیمی ببینید:
$password = md5($_POST['password']);
این روش برای سیستمهای جدید مناسب نیست.
همچنین استفاده از SHA-1 یا SHA-256 به صورت ساده برای ذخیره رمز عبور، جایگزین Password Hashing مناسب نیست.
برای رمز عبور بهتر است از API اختصاصی PHP استفاده کنیم:
password_hash()
و:
password_verify()
# password_verify در PHP چیست؟
برای بررسی رمز عبور، نباید خودمان Hashها را با == مقایسه کنیم.
به جای آن از password_verify() استفاده میکنیم:
$password = 'MyPassword123';
$hash = password_hash(
$password,
PASSWORD_DEFAULT
);
if (password_verify($password, $hash)) {
echo 'Password is correct';
}
password_verify() بررسی میکند که رمز عبور با Hash موردنظر مطابقت دارد یا خیر. این تابع برای جلوگیری از Timing Attack نیز طراحی شده است.
# ساخت دیتابیس کاربران در PHP
برای ساخت سیستم Authentication به یک جدول کاربران نیاز داریم.
مثلاً:
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
نکته مهم این است که ستون password را حداقل به اندازهای در نظر بگیریم که بتواند Hashهای آینده را نیز ذخیره کند.
مستندات PHP برای خروجی PASSWORD_DEFAULT توصیه میکند فضای ذخیرهسازی Hash را انعطافپذیر نگه داریم و 255 بایت را پیشنهاد میکند، زیرا الگوریتم پیشفرض ممکن است در نسخههای آینده تغییر کند.
# ساخت اتصال PHP به MySQL با PDO
برای اتصال به دیتابیس میتوانیم از PDO استفاده کنیم:
<?php
$pdo = new PDO(
'mysql:host=localhost;dbname=shop;charset=utf8mb4',
'root',
''
);
$pdo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
در پروژه واقعی بهتر است اطلاعات اتصال دیتابیس را مستقیماً داخل فایل عمومی قرار ندهیم و آنها را از Configuration یا Environment Variables دریافت کنیم.
# ساخت فرم ثبتنام
یک فرم ساده ثبتنام:
<form action="register.php" method="POST">
<input
type="text"
name="name"
placeholder="Name"
required
>
<input
type="email"
name="email"
placeholder="Email"
required
>
<input
type="password"
name="password"
placeholder="Password"
required
>
<button type="submit">
Register
</button>
</form>
کاربر اطلاعات خود را وارد میکند و فرم به register.php ارسال میشود.
# پردازش ثبتنام کاربر در PHP
فایل register.php:
<?php
require 'database.php';
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (
$name === '' ||
$email === '' ||
$password === ''
) {
exit('All fields are required.');
}
در اینجا ابتدا بررسی میکنیم که فیلدهای مورد نیاز ارسال شده باشند.
# اعتبارسنجی Email
بهتر است Email را نیز اعتبارسنجی کنیم:
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('Invalid email address.');
}
اگر Email معتبر نباشد، ثبتنام انجام نمیشود.
# بررسی وجود کاربر
قبل از ایجاد کاربر، بررسی میکنیم Email قبلاً ثبت نشده باشد:
$stmt = $pdo->prepare(
'SELECT id FROM users WHERE email = :email'
);
$stmt->execute([
'email' => $email
]);
if ($stmt->fetch()) {
exit('Email already exists.');
}
در این Query از Prepared Statement استفاده کردهایم تا مقدار ورودی کاربر مستقیماً وارد SQL نشود.
# Hash کردن رمز عبور هنگام ثبتنام
حالا رمز عبور را Hash میکنیم:
$passwordHash = password_hash(
$password,
PASSWORD_DEFAULT
);
سپس Hash را در دیتابیس ذخیره میکنیم، نه رمز عبور اصلی.
# ذخیره کاربر در دیتابیس
اکنون میتوانیم کاربر را ایجاد کنیم:
$stmt = $pdo->prepare(
'INSERT INTO users (name, email, password)
VALUES (:name, :email, :password)'
);
$stmt->execute([
'name' => $name,
'email' => $email,
'password' => $passwordHash
]);
در این مرحله اطلاعات کاربر در دیتابیس ذخیره شده است.
# کد کامل Register در PHP
میتوانیم همه بخشهای بالا را در یک فایل قرار دهیم:
<?php
require 'database.php';
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if ($name === '' || $email === '' || $password === '') {
exit('All fields are required.');
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('Invalid email address.');
}
if (strlen($password) < 8) {
exit('Password must be at least 8 characters.');
}
$stmt = $pdo->prepare(
'SELECT id FROM users WHERE email = :email'
);
$stmt->execute([
'email' => $email
]);
if ($stmt->fetch()) {
exit('Email already exists.');
}
$passwordHash = password_hash(
$password,
PASSWORD_DEFAULT
);
$stmt = $pdo->prepare(
'INSERT INTO users (name, email, password)
VALUES (:name, :email, :password)'
);
$stmt->execute([
'name' => $name,
'email' => $email,
'password' => $passwordHash
]);
echo 'Registration successful.';
# ساخت فرم Login
حالا باید امکان ورود کاربر را ایجاد کنیم:
<form action="login.php" method="POST">
<input
type="email"
name="email"
placeholder="Email"
required
>
<input
type="password"
name="password"
placeholder="Password"
required
>
<button type="submit">
Login
</button>
</form>
# دریافت اطلاعات کاربر هنگام Login
در login.php:
<?php
require 'database.php';
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if ($email === '' || $password === '') {
exit('Email and password are required.');
}
# پیدا کردن کاربر در دیتابیس
حالا کاربر را با Email پیدا میکنیم:
$stmt = $pdo->prepare(
'SELECT id, name, email, password
FROM users
WHERE email = :email
LIMIT 1'
);
$stmt->execute([
'email' => $email
]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
اگر کاربر وجود نداشته باشد، مقدار $user برابر false خواهد بود.
# بررسی رمز عبور
حالا مهمترین بخش Authentication:
if (
!$user ||
!password_verify($password, $user['password'])
) {
exit('Invalid email or password.');
}
اگر Email یا Password اشتباه باشد، اجازه ورود داده نمیشود.
نکته مهم این است که ما رمز عبور را Decode نمیکنیم؛ بلکه password_verify() رمز عبور واردشده را با Hash ذخیرهشده بررسی میکند.
# Session در Authentication چیست؟
بعد از اینکه کاربر با موفقیت احراز هویت شد، باید سیستم بتواند در درخواستهای بعدی بفهمد کاربر وارد حساب خود شده است.
برای این کار در یک سیستم سنتی PHP میتوان از Session استفاده کرد.
ابتدا:
session_start();
سپس بعد از Login موفق:
$_SESSION['user_id'] = $user['id'];
و مثلاً:
$_SESSION['user_name'] = $user['name'];
حالا Session اطلاعاتی درباره کاربر فعلی در اختیار برنامه قرار میدهد.
# کد کامل Login با Session
<?php
session_start();
require 'database.php';
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if ($email === '' || $password === '') {
exit('Email and password are required.');
}
$stmt = $pdo->prepare(
'SELECT id, name, email, password
FROM users
WHERE email = :email
LIMIT 1'
);
$stmt->execute([
'email' => $email
]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (
!$user ||
!password_verify($password, $user['password'])
) {
exit('Invalid email or password.');
}
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
echo 'Login successful.';
# چرا session_regenerate_id مهم است؟
بعد از Login موفق بهتر است شناسه Session را تغییر دهیم:
session_regenerate_id(true);
این کار یکی از اقدامات مهم برای کاهش ریسک Session Fixation است.
بنابراین بهتر است بعد از احراز هویت موفق، Session ID جدید ایجاد کنیم.
# بررسی Login بودن کاربر
فرض کنید صفحهای داریم که فقط کاربران واردشده باید بتوانند آن را مشاهده کنند.
میتوانیم بنویسیم:
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
exit('You must login first.');
}
echo 'Welcome to your account.';
اگر user_id در Session وجود داشته باشد، یعنی کاربر در این Session احراز هویت شده است.
# ساخت Middleware ساده برای Authentication
اگر صفحات زیادی داشته باشیم، بهتر است این منطق را در همه فایلها تکرار نکنیم.
مثلاً فایل:
auth.php
ایجاد کنیم:
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
حالا هر صفحه خصوصی میتواند این فایل را Include کند:
<?php
require 'auth.php';
echo 'Private dashboard';
در پروژههای بزرگتر، Frameworkهایی مانند Laravel این مفهوم را با Middleware به شکل ساختاریافتهتری ارائه میکنند.
# Logout در PHP
برای خروج کاربر باید Session را پاک کنیم.
مثلاً:
<?php
session_start();
$_SESSION = [];
session_destroy();
header('Location: login.php');
exit;
با این کار Session کاربر از بین میرود و کاربر باید دوباره Login کند.
# چرا session_destroy به تنهایی کافی نیست؟
میتوانیم:
session_destroy();
را اجرا کنیم، اما بهتر است دادههای Session را نیز پاک کنیم:
$_SESSION = [];
session_destroy();
در سیستمهای حرفهای همچنین باید Cookie مربوط به Session نیز با توجه به تنظیمات پروژه مدیریت شود.
# Authentication و Cookie
Session در PHP معمولاً با یک Session Cookie در سمت Client همراه است.
به صورت ساده:
Browser
|
| Session Cookie
↓
PHP Server
|
↓
Session Data
مرورگر شناسه Session را نگهداری میکند و PHP بر اساس آن Session مربوط به کاربر را پیدا میکند.
برای Session Cookie در محیط HTTPS میتوان تنظیمات امنیتی مانند Secure و HttpOnly را در نظر گرفت.
# HttpOnly چیست؟
اگر Cookie دارای ویژگی HttpOnly باشد، JavaScript معمولی صفحه نمیتواند مستقیماً به مقدار Cookie دسترسی پیدا کند.
این ویژگی میتواند در کاهش برخی ریسکهای سرقت Cookie در سناریوهای خاص کمک کند.
برای Cookieهای Session بهتر است تنظیمات امنیتی مناسب از جمله HttpOnly و در محیط HTTPS ویژگی Secure در نظر گرفته شود.
# SameSite چیست؟
SameSite یکی دیگر از ویژگیهای مهم Cookie است.
مقادیر رایج آن:
Strict
Lax
None
هستند.
این ویژگی مشخص میکند Cookie در درخواستهای Cross-Site تحت چه شرایطی ارسال شود.
تنظیم صحیح SameSite میتواند بخشی از استراتژی امنیتی Session و مقابله با برخی سناریوهای Cross-Site باشد.
# بررسی مجدد Hash با password_needs_rehash
PHP تابع دیگری به نام:
password_needs_rehash()
دارد.
این تابع میتواند بررسی کند آیا Hash موجود باید با تنظیمات فعلی دوباره ایجاد شود یا خیر.
مثلاً:
if (
password_verify($password, $user['password']) &&
password_needs_rehash(
$user['password'],
PASSWORD_DEFAULT
)
) {
$newHash = password_hash(
$password,
PASSWORD_DEFAULT
);
// Update $newHash in database
}
این قابلیت برای مهاجرت تدریجی Hashهای قدیمی به تنظیمات جدید مفید است.
API رسمی Password Hashing در PHP شامل password_hash(), password_verify() و password_needs_rehash() است.
# آیا باید برای هر کاربر Salt جداگانه بسازیم؟
خیر.
هنگام استفاده از:
password_hash(
$password,
PASSWORD_DEFAULT
);
PHP Salt امن مورد نیاز را به صورت خودکار تولید میکند.
بنابراین معمولاً نباید Salt را خودتان تولید و ذخیره کنید. مستندات PHP نیز استفاده از Salt تولیدشده به صورت خودکار را توصیه میکند.
# آیا میتوانیم رمز عبور را بعداً از Hash برگردانیم؟
خیر.
هدف Password Hashing این نیست که بتوانیم رمز اصلی را از Hash استخراج کنیم.
به جای آن:
Password
↓
password_hash()
↓
Stored Hash
و هنگام ورود:
Entered Password
↓
password_verify()
↓
Match / No Match
بنابراین سیستم نیازی به دانستن رمز اصلی کاربر ندارد.
# Authentication در REST API چگونه انجام میشود؟
در APIها معمولاً مدل احراز هویت میتواند با سیستم سنتی Session متفاوت باشد.
یک روش رایج استفاده از Token است:
Client
|
| Email + Password
↓
API
|
| Verify
↓
Token
|
↓
Client
سپس Client در درخواستهای بعدی Token را ارسال میکند:
Authorization: Bearer YOUR_TOKEN
سرور Token را بررسی میکند و در صورت معتبر بودن، اجازه دسترسی میدهد.
یکی از روشهای شناختهشده در این حوزه JWT است که در مقاله بعدی میتوانیم به صورت جداگانه بررسی کنیم.
# Authentication در PHP چه بخشهایی دارد؟
یک سیستم Authentication کامل میتواند شامل این بخشها باشد:
Register
↓
Email Verification
↓
Login
↓
Password Hashing
↓
Session / Token
↓
Authorization
↓
Logout
↓
Password Reset
در پروژههای حرفهای موارد دیگری مانند:
- ورود دو مرحلهای
- محدود کردن تلاشهای Login
- تأیید Email
- بازیابی رمز عبور
- مدیریت Sessionهای فعال
- ثبت رویدادهای امنیتی
نیز ممکن است اضافه شوند.
# نکات امنیتی مهم در Authentication
1. رمز عبور را Plain Text ذخیره نکنید
همیشه از Password Hashing مناسب استفاده کنید.
2. از password_hash استفاده کنید
مثلاً:
password_hash(
$password,
PASSWORD_DEFAULT
);
3. از password_verify استفاده کنید
برای بررسی رمز:
password_verify(
$password,
$hash
);
4. از Prepared Statement استفاده کنید
برای Queryهای دیتابیس:
$stmt = $pdo->prepare(...);
5. بعد از Login Session ID را تغییر دهید
session_regenerate_id(true);
6. از HTTPS استفاده کنید
اطلاعات Login نباید در یک ارتباط HTTP معمولی ارسال شوند.
7. پیام خطای Login را بیش از حد دقیق نکنید
به جای پیامهایی مانند:
User does not exist.
میتوان از پیام عمومی استفاده کرد:
Invalid email or password.
این کار میتواند از افشای وجود یا عدم وجود حسابهای کاربری جلوگیری کند.
8. Login را در برابر تلاشهای متعدد محافظت کنید
برای سیستمهای واقعی باید Rate Limiting یا مکانیزمهای مشابه برای جلوگیری از تلاشهای مکرر در نظر گرفته شود.
# ساختار پیشنهادی پروژه Authentication در PHP
برای یک پروژه کوچک میتوان ساختار سادهای داشت:
project/
│
├── database.php
├── register.php
├── login.php
├── logout.php
├── auth.php
├── dashboard.php
│
└── index.php
اما در پروژههای بزرگتر بهتر است معماری منظمتری داشته باشیم:
project/
│
├── public/
│
├── src/
│ ├── Controllers/
│ ├── Services/
│ ├── Repositories/
│ ├── Models/
│ └── Middleware/
│
├── config/
│
├── routes/
│
└── composer.json
در این حالت Authentication میتواند به بخشهای مختلف تقسیم شود.
مثلاً:
AuthController
↓
AuthService
↓
UserRepository
↓
Database
این ساختار با مفاهیمی که در مقالات MVC، Repository Pattern و Service Layer بررسی کردیم ارتباط مستقیم دارد.
# تفاوت Authentication در PHP خام و Laravel
در PHP خام باید بسیاری از بخشها را خودمان طراحی کنیم:
Session
Validation
Login
Logout
Password Hashing
Middleware
Authorization
اما Laravel امکانات و ساختارهای آمادهتری برای بخش زیادی از این فرآیندها ارائه میکند.
به همین دلیل درک Authentication با PHP خام کمک میکند وقتی وارد Laravel میشوید، مفهوم سیستم Authentication را بهتر بفهمید و فقط وابسته به کدهای آماده Framework نباشید.
# جمعبندی
Authentication یکی از پایهایترین بخشهای هر نرمافزار دارای کاربر است.
در یک سیستم Authentication باید بتوانیم:
- کاربر را ثبتنام کنیم.
- اطلاعات او را اعتبارسنجی کنیم.
- رمز عبور را به صورت امن Hash کنیم.
- هنگام Login رمز را با
password_verify()بررسی کنیم. - وضعیت ورود کاربر را با Session یا Token مدیریت کنیم.
- سطح دسترسی کاربر را بررسی کنیم.
- امکان Logout را فراهم کنیم.
- از سیستم در برابر حملات و ورودیهای مخرب محافظت کنیم.
در PHP برای Password Hashing توابع داخلی مناسبی وجود دارد و استفاده از password_hash() و password_verify() مسیر استانداردی برای مدیریت رمزهای عبور است.
اگر Authentication را به شکل صحیح درک کنید، یادگیری موضوعات بعدی مانند JWT، Session، Cookie، Authorization و Authentication در REST API بسیار سادهتر خواهد شد.
# سوالات متداول درباره Authentication در PHP
Authentication در PHP چیست؟
Authentication فرآیندی است که در آن PHP بررسی میکند اطلاعات ارائهشده توسط کاربر، مانند Email و Password، با اطلاعات ثبتشده در سیستم مطابقت دارد یا خیر.
تفاوت Authentication و Authorization چیست؟
Authentication مشخص میکند کاربر چه کسی است؛ Authorization مشخص میکند آن کاربر چه دسترسیهایی دارد.
آیا میتوان رمز عبور را در دیتابیس به صورت ساده ذخیره کرد؟
خیر. رمز عبور نباید به صورت Plain Text ذخیره شود. باید از Password Hashing مناسب مانند password_hash() استفاده شود.
password_hash در PHP چه کاری انجام میدهد؟
password_hash() رمز عبور را با یک الگوریتم مناسب Hash میکند و Hash حاصل را برای ذخیرهسازی در دیتابیس برمیگرداند.
password_verify در PHP چیست؟
password_verify() بررسی میکند که یک رمز عبور با Hash ذخیرهشده مطابقت دارد یا خیر.
آیا MD5 برای ذخیره رمز عبور مناسب است؟
برای سیستمهای جدید نباید از MD5 به عنوان روش Password Hashing استفاده کرد. بهتر است از API اختصاصی Password Hashing در PHP استفاده شود.
Session در Authentication چه کاربردی دارد؟
Session به برنامه کمک میکند وضعیت احراز هویت کاربر را بین درخواستهای مختلف حفظ کند.
آیا REST API میتواند از Authentication استفاده کند؟
بله. APIها میتوانند از روشهای مختلفی مانند Session، Token و JWT برای احراز هویت استفاده کنند.
آیا Authentication به تنهایی برای امنیت کافی است؟
خیر. علاوه بر Authentication باید Authorization، Validation، مدیریت Session، HTTPS، Rate Limiting و سایر کنترلهای امنیتی متناسب با پروژه نیز در نظر گرفته شوند.





