امنیت ورودیها همیشه نقطهضعف اپلیکیشنهاست. استفاده از prepared statements در PDO یک راهکار ساده و مؤثر برای جلوگیری از SQL Injection است.
ایدهٔ اصلی (مختصر)
با آمادهسازی کوئری و جدا کردن دادهها از ساختار SQL، هر ورودی بهعنوان داده تفسیر میشود نه دستور SQL. در نتیجه حتی ورودیهای مخرب اجرا نخواهند شد.
مثال شفاف
در این مثال یک اتصال PDO امن و نمونهٔ استفاده از پارامترهای نامدار را میبینید. دقت کنید که
نکات عملی و خطاهای رایج
- هرگز ورودیها را با string concatenation داخل کوئری قرار ندهید.
- از bindValue یا آرایهٔ execute برای ارسال پارامترها استفاده کنید.
- نوع داده را در صورت نیاز explicit کنید (مثلاً PDO::PARAM_INT).
- اتصال با
اگر تجربهای در مهاجرت از mysql_* یا mysqli به PDO دارید یا سوالی هست، خوشحال میشم بشنوم و بحث کنیم.
منبع: مستندات رسمی PHP — PDO Prepared Statements
🔖 #PHP #پی_اچ_پی #php #pdo #security #sql_injection #prepared_statements
👤 Developix
💎 Channel: @DevelopixPHP
ایدهٔ اصلی (مختصر)
با آمادهسازی کوئری و جدا کردن دادهها از ساختار SQL، هر ورودی بهعنوان داده تفسیر میشود نه دستور SQL. در نتیجه حتی ورودیهای مخرب اجرا نخواهند شد.
مثال شفاف
در این مثال یک اتصال PDO امن و نمونهٔ استفاده از پارامترهای نامدار را میبینید. دقت کنید که
PDO::ERRMODE_EXCEPTION روشن است تا خطاها واضح باشند.<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// INSERT امن با پارامتر نامدار
$stmt = $pdo->prepare('INSERT INTO users (email, name) VALUES (:email, :name)');
$stmt->execute([':email' => $email, ':name' => $name]);
// استفادهٔ مجدد برای SELECT
$select = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$select->execute([':email' => $email]);
$user = $select->fetch(PDO::FETCH_ASSOC);
?>
نکات عملی و خطاهای رایج
- هرگز ورودیها را با string concatenation داخل کوئری قرار ندهید.
- از bindValue یا آرایهٔ execute برای ارسال پارامترها استفاده کنید.
- نوع داده را در صورت نیاز explicit کنید (مثلاً PDO::PARAM_INT).
- اتصال با
charset=utf8mb4 و ERRMODE_EXCEPTION را فراموش نکنید.اگر تجربهای در مهاجرت از mysql_* یا mysqli به PDO دارید یا سوالی هست، خوشحال میشم بشنوم و بحث کنیم.
منبع: مستندات رسمی PHP — PDO Prepared Statements
🔖 #PHP #پی_اچ_پی #php #pdo #security #sql_injection #prepared_statements
👤 Developix
💎 Channel: @DevelopixPHP
👍1
یکی از سادهترین و مهمترین کارها برای امنکردن اپهای PHP اینه که مستقیم متغیرها رو نچسبونیم به Query و بهجاش از Prepared Statement با PDO استفاده کنیم. با این کار جلوی خیلی از SQL Injectionها گرفته میشه 🚫💉
فرض کنیم یه فرم لاگین ساده داریم. این روش 👇 ناامن و مستعد SQL Injection هست:
راه درست، استفاده از placeholder و bind کردن مقادیر هست. اینطوری PDO خودش مقادیر رو escape میکنه و بهعنوان دیتا با دیتابیس حرف میزنه، نه کد 👇
نکات مهم 🤝
• همیشه ERRMODE_EXCEPTION رو فعال کن تا خطاها رو از دست ندی.
• از named placeholder (مثل
• حتی برای مقدارهای عددی هم از prepared statement استفاده کن؛ به شرط «عددی بودن» دیتا برای Logic خودت اعتماد نکن.
این الگو رو توی همه Queryهایی که ورودی کاربر توشون استفاده میشه پیاده کن؛ لاگین، سرچ، فیلتر، پنل ادمین و… ✅
مستندات رسمی PDO و prepared statement:
https://www.php.net/manual/en/book.pdo.php
امروز توی یکی از بخشهای پروژهات این الگو رو جایگزین روش قدیمی کن و تفاوت تمیزی و امنیت کد رو ببین 😎
🔖 #PHP #پی_اچ_پی #PHP #PDO #Security #SQL_Injection #Best_Practices
👤 Developix
💎 Channel: @DevelopixPHP
فرض کنیم یه فرم لاگین ساده داریم. این روش 👇 ناامن و مستعد SQL Injection هست:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$username = $_POST['username'];
$password = $_POST['password'];
// ❌ ناامن
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$stmt = $pdo->query($sql);
$user = $stmt->fetch();
راه درست، استفاده از placeholder و bind کردن مقادیر هست. اینطوری PDO خودش مقادیر رو escape میکنه و بهعنوان دیتا با دیتابیس حرف میزنه، نه کد 👇
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$username = $_POST['username'];
$password = $_POST['password'];
// ✅ امن با prepared statement
$sql = "SELECT * FROM users WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':username' => $username,
':password' => $password,
]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
نکات مهم 🤝
• همیشه ERRMODE_EXCEPTION رو فعال کن تا خطاها رو از دست ندی.
• از named placeholder (مثل
:username) استفاده کن تا کدت خواناتر بشه.• حتی برای مقدارهای عددی هم از prepared statement استفاده کن؛ به شرط «عددی بودن» دیتا برای Logic خودت اعتماد نکن.
این الگو رو توی همه Queryهایی که ورودی کاربر توشون استفاده میشه پیاده کن؛ لاگین، سرچ، فیلتر، پنل ادمین و… ✅
مستندات رسمی PDO و prepared statement:
https://www.php.net/manual/en/book.pdo.php
امروز توی یکی از بخشهای پروژهات این الگو رو جایگزین روش قدیمی کن و تفاوت تمیزی و امنیت کد رو ببین 😎
🔖 #PHP #پی_اچ_پی #PHP #PDO #Security #SQL_Injection #Best_Practices
👤 Developix
💎 Channel: @DevelopixPHP
🔥3
ساخت فرم لاگین امن با PHP با استفاده از password_hash 🔐
خیلی از پروژههای PHP هنوز از md5 یا sha1 برای ذخیره پسورد استفاده میکنن و این یعنی دردسر امنیتی 😅
بیایم یه لاگین ساده ولی استاندارد بسازیم که روی password_hash و password_verify سوار باشه؛ همون چیزی که توی داک رسمی PHP هم پیشنهاد شده:
php.net/manual/en/function.password-hash.php
ایده کلی اینه:
• سمت دیتابیس فقط hash پسورد ذخیره میشه، نه خود پسورد.
• برای ساخت یوزر جدید از password_hash استفاده میکنیم.
• برای لاگین از password_verify کمک میگیریم.
• بعد از لاگین موفق، یه session امن راه میندازیم.
این یه مثال خیلی مینیمال و قابلاستفاده توی پروژههای کوچیکه 👇
نکتهها و ترفندهای کوچیک ولی مهم:
• الگوریتم داخلی PASSWORD_DEFAULT ممکنه در آینده عوض بشه، ولی همون برای اکثر سناریوها عالیه.
• با password_needs_rehash میتونی موقع لاگین، hashهای قدیمی رو خودکار به نسخهی جدیدتر migrate کنی (مثلاً وقتی cost رو عوض کردی).
• حتماً روی ستون پسورد توی دیتابیس حداقل طول 255 بگذار تا با تغییر الگوریتمهای آینده مشکل نداشته باشی.
• از PDO prepared statement استفاده کن که جلوی SQL Injection رو بگیره، مثل همین نمونه.
• بعد لاگین، session_regenerate_id رو فراموش نکن تا Session Fixation اتفاق نیفته.
این الگو پایهی خیلی از سیستمهای auth امروزی توی PHP ـه و راحت میشه بعداً روش چیزهایی مثل remember me، محدودیت تلاش لاگین، و لاگ آدیت اضافه کرد.
امتحانش کن روی یه پروژه کوچیک، اگر جواب داد همون رو تمیزتر کن و بذار توی پروژههای اصلیات 💻
🔖 #PHP #پی_اچ_پی #PHP #Authentication #Security #Password_Hashing #Login #PDO
👤 Developix
💎 Channel: @DevelopixPHP
خیلی از پروژههای PHP هنوز از md5 یا sha1 برای ذخیره پسورد استفاده میکنن و این یعنی دردسر امنیتی 😅
بیایم یه لاگین ساده ولی استاندارد بسازیم که روی password_hash و password_verify سوار باشه؛ همون چیزی که توی داک رسمی PHP هم پیشنهاد شده:
php.net/manual/en/function.password-hash.php
ایده کلی اینه:
• سمت دیتابیس فقط hash پسورد ذخیره میشه، نه خود پسورد.
• برای ساخت یوزر جدید از password_hash استفاده میکنیم.
• برای لاگین از password_verify کمک میگیریم.
• بعد از لاگین موفق، یه session امن راه میندازیم.
این یه مثال خیلی مینیمال و قابلاستفاده توی پروژههای کوچیکه 👇
<?php
// config.php
$pdo = new PDO(
'mysql:host=localhost;dbname=test;charset=utf8mb4',
'db_user',
'db_pass',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
// register.php - ساخت یوزر جدید
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit('ایمیل نامعتبر است');
}
if (strlen($password) < 8) {
exit('پسورد باید حداقل 8 کاراکتر باشد');
}
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('INSERT INTO users (email, password_hash) VALUES (:email, :hash)');
$stmt->execute([
':email' => $email,
':hash' => $hash,
]);
echo 'ثبتنام با موفقیت انجام شد ✅';
}
// login.php - لاگین یوزر
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
$stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE email = :email');
$stmt->execute([':email' => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user || !password_verify($password, $user['password_hash'])) {
exit('ایمیل یا پسورد اشتباه است');
}
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
echo 'خوش اومدی! 🎉';
}
نکتهها و ترفندهای کوچیک ولی مهم:
• الگوریتم داخلی PASSWORD_DEFAULT ممکنه در آینده عوض بشه، ولی همون برای اکثر سناریوها عالیه.
• با password_needs_rehash میتونی موقع لاگین، hashهای قدیمی رو خودکار به نسخهی جدیدتر migrate کنی (مثلاً وقتی cost رو عوض کردی).
• حتماً روی ستون پسورد توی دیتابیس حداقل طول 255 بگذار تا با تغییر الگوریتمهای آینده مشکل نداشته باشی.
• از PDO prepared statement استفاده کن که جلوی SQL Injection رو بگیره، مثل همین نمونه.
• بعد لاگین، session_regenerate_id رو فراموش نکن تا Session Fixation اتفاق نیفته.
این الگو پایهی خیلی از سیستمهای auth امروزی توی PHP ـه و راحت میشه بعداً روش چیزهایی مثل remember me، محدودیت تلاش لاگین، و لاگ آدیت اضافه کرد.
امتحانش کن روی یه پروژه کوچیک، اگر جواب داد همون رو تمیزتر کن و بذار توی پروژههای اصلیات 💻
🔖 #PHP #پی_اچ_پی #PHP #Authentication #Security #Password_Hashing #Login #PDO
👤 Developix
💎 Channel: @DevelopixPHP
👍4👎2
ارسال ایمیل تأیید ثبتنام، بازیابی رمز عبور یا نوتیفیکیشن سفارش، تقریباً توی هر پروژه PHP جدی لازم میشود. اینجا با هم یک ماژول ساده و تمیز با PHPMailer میسازیم که هم امن باشد، هم قابلگسترش. 😉
منبع اصلیای که ایدهها ازش گرفته شده، داکیومنت رسمی و مثالهای PHPMailer در گیتهاب است؛ حتماً ارزش یک نگاه عمیقتر را دارد. 🔍
۱️⃣ نصب PHPMailer با Composer
اول مطمئن شو روی سرور یا سیستم لوکال، Composer نصب است. بعد داخل رoot پروژه این دستور را بزن:
composer require phpmailer/phpmailer
حالا پوشه vendor ساخته میشود و میتوانی از Autoload استفاده کنی.
۲️⃣ ساخت یک کلاس EmailService تمیز
بهجای اینکه هرجا لازم شد مستقیم شیء PHPMailer بسازی، بهتر است یک کلاس سرویس داشته باشی تا کانفیگ SMTP فقط یکجا باشد و بعداً راحت عوض شود (مثلاً وقتی از Gmail رفتی روی Mailgun یا SMTP هاست).
یک فایل مثل EmailService.php بساز و ساختار را شبیه مثال زیر نگه دار 👇
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require __DIR__ . '/vendor/autoload.php';
class EmailService
{
private PHPMailer $mailer;
public function __construct()
{
$this->mailer = new PHPMailer(true);
// تنظیمات پایه SMTP
$this->mailer->isSMTP();
$this->mailer->Host = 'smtp.gmail.com';
$this->mailer->SMTPAuth = true;
$this->mailer->Username = 'your-email@gmail.com';
$this->mailer->Password = 'app-password-here';
$this->mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$this->mailer->Port = 587;
$this->mailer->setFrom('no-reply@yourapp.test', 'Your App');
$this->mailer->isHTML(true);
$this->mailer->CharSet = 'UTF-8';
}
public function send(string $to, string $subject, string $htmlBody): bool
{
try {
$this->mailer->clearAllRecipients();
$this->mailer->addAddress($to);
$this->mailer->Subject = $subject;
$this->mailer->Body = $htmlBody;
return $this->mailer->send();
} catch (Exception $e) {
error_log('Mail error: ' . $e->getMessage());
return false;
}
}
}
🔐 نکته مهم: برای Gmail باید از App Password استفاده کنی (نه پسورد عادی اکانت). راهنماش توی صفحه رسمی گوگل موجود است و بدون این کار معمولاً اتصال SMTP بلاک میشود.
۳️⃣ استفاده در سناریوی واقعی
مثلاً بعد از ثبتنام، یک لینک فعالسازی بفرست:
$emailService = new EmailService();
$activationLink = 'https://yourapp.test/activate?token=' . urlencode($token);
$html = "سلام {$userName}!<br>برای فعالسازی حسابت روی لینک زیر کلیک کن:<br><a href='{$activationLink}'>فعالسازی حساب";
$emailService->send($userEmail, 'فعالسازی حساب کاربری', $html);
🧩 چند نکته تجربی کوتاه:
- لاگ ارور را حتماً جایی ذخیره کن تا اگر روی سرور ایمیل نرسید، راحتتر دیباگ شود.
- محتوا را هم HTML نگه دار، هم یک نسخه متن ساده برای سازگاری بهتر با بعضی کلاینتها (قابلیت AltBody در PHPMailer).
- اطلاعات SMTP و پسوردها را داخل فایل env. نگه دار، نه مستقیم داخل کلاس.
در هر پروژه جدید، داشتن یک EmailService آماده کلی زمان ذخیره میکند. اگر این ساختار را کمی برای خودت شخصیسازی کنی، عملاً یک ماژول ایمیل قابلاستفاده مجدد برای تمام پروژههای PHP خواهی داشت. 🚀
🔖 #PHP #پی_اچ_پی #PHP #PHPMailer #Email #SMTP #Backend #Security
👤 Developix
💎 Channel: @DevelopixPHP
منبع اصلیای که ایدهها ازش گرفته شده، داکیومنت رسمی و مثالهای PHPMailer در گیتهاب است؛ حتماً ارزش یک نگاه عمیقتر را دارد. 🔍
۱️⃣ نصب PHPMailer با Composer
اول مطمئن شو روی سرور یا سیستم لوکال، Composer نصب است. بعد داخل رoot پروژه این دستور را بزن:
composer require phpmailer/phpmailer
حالا پوشه vendor ساخته میشود و میتوانی از Autoload استفاده کنی.
۲️⃣ ساخت یک کلاس EmailService تمیز
بهجای اینکه هرجا لازم شد مستقیم شیء PHPMailer بسازی، بهتر است یک کلاس سرویس داشته باشی تا کانفیگ SMTP فقط یکجا باشد و بعداً راحت عوض شود (مثلاً وقتی از Gmail رفتی روی Mailgun یا SMTP هاست).
یک فایل مثل EmailService.php بساز و ساختار را شبیه مثال زیر نگه دار 👇
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require __DIR__ . '/vendor/autoload.php';
class EmailService
{
private PHPMailer $mailer;
public function __construct()
{
$this->mailer = new PHPMailer(true);
// تنظیمات پایه SMTP
$this->mailer->isSMTP();
$this->mailer->Host = 'smtp.gmail.com';
$this->mailer->SMTPAuth = true;
$this->mailer->Username = 'your-email@gmail.com';
$this->mailer->Password = 'app-password-here';
$this->mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$this->mailer->Port = 587;
$this->mailer->setFrom('no-reply@yourapp.test', 'Your App');
$this->mailer->isHTML(true);
$this->mailer->CharSet = 'UTF-8';
}
public function send(string $to, string $subject, string $htmlBody): bool
{
try {
$this->mailer->clearAllRecipients();
$this->mailer->addAddress($to);
$this->mailer->Subject = $subject;
$this->mailer->Body = $htmlBody;
return $this->mailer->send();
} catch (Exception $e) {
error_log('Mail error: ' . $e->getMessage());
return false;
}
}
}
🔐 نکته مهم: برای Gmail باید از App Password استفاده کنی (نه پسورد عادی اکانت). راهنماش توی صفحه رسمی گوگل موجود است و بدون این کار معمولاً اتصال SMTP بلاک میشود.
۳️⃣ استفاده در سناریوی واقعی
مثلاً بعد از ثبتنام، یک لینک فعالسازی بفرست:
$emailService = new EmailService();
$activationLink = 'https://yourapp.test/activate?token=' . urlencode($token);
$html = "سلام {$userName}!<br>برای فعالسازی حسابت روی لینک زیر کلیک کن:<br><a href='{$activationLink}'>فعالسازی حساب";
$emailService->send($userEmail, 'فعالسازی حساب کاربری', $html);
🧩 چند نکته تجربی کوتاه:
- لاگ ارور را حتماً جایی ذخیره کن تا اگر روی سرور ایمیل نرسید، راحتتر دیباگ شود.
- محتوا را هم HTML نگه دار، هم یک نسخه متن ساده برای سازگاری بهتر با بعضی کلاینتها (قابلیت AltBody در PHPMailer).
- اطلاعات SMTP و پسوردها را داخل فایل env. نگه دار، نه مستقیم داخل کلاس.
در هر پروژه جدید، داشتن یک EmailService آماده کلی زمان ذخیره میکند. اگر این ساختار را کمی برای خودت شخصیسازی کنی، عملاً یک ماژول ایمیل قابلاستفاده مجدد برای تمام پروژههای PHP خواهی داشت. 🚀
🔖 #PHP #پی_اچ_پی #PHP #PHPMailer #Email #SMTP #Backend #Security
👤 Developix
💎 Channel: @DevelopixPHP