فرم ها در PHP: دریافت اطلاعات و پردازش امن آن‌ها

درسنامه درس 13 از سری آموزش PHP 7
فرم ها در PHP: دریافت اطلاعات و پردازش امن آن‌ها

دریافت اطلاعات از فرم های PHP

در جلسه ی قبل با برخی از سوپرگلوبال های PHP آشنا شدیم. برای دریافت اطلاعات فرم ها در زبان PHP از دو سوپرگلوبال دیگر به نام های GET_$ و POST_$ استفاده می کنیم. در مثال زیر یک فرم HTML ساده را مشاهده می کنید:

<!DOCTYPE HTML>
<html>  
<body>

<form action="welcome.php" method="post">
Name: <input type="text" name="name"><br>
E-mail: <input type="text" name="email"><br>
<input type="submit">
</form>

</body>
</html>

زمانی که کاربر این فرم را پر کرده و دکمه ی submit را میزند اطلاعات فرم برای پردازش به فایلی به نام welcome.php ارسال می شوند. همچنین این اطلاعات از طریق متد HTTP POST ارسال می شوند (به فرم و method آن نگاه کنید). فعلا برای تست فقط داده ها را نمایش می دهیم و پردازش خاصی روی آن ها انجام نمیدهیم. فایل welcome.php به شکل زیر است:

<html>
<body>

Welcome <?php echo $_POST["name"]; ?><br>
Your email address is: <?php echo $_POST["email"]; ?>

</body>
</html>

بنابراین خروجی شبیه به متن زیر خواهد بود:

Welcome John
Your email address is john.doe@example.com

ما می توانیم همین کار را با متد HTTP GET نیز انجام دهیم:

<!DOCTYPE HTML>
<html>  
<body>

<form action="welcome_get.php" method="get">
Name: <input type="text" name="name"><br>
E-mail: <input type="text" name="email"><br>
<input type="submit">
</form>

</body>
</html>

فایل welcome_get.php نیز بدین شکل خواهد بود:

<html>
<body>

Welcome <?php echo $_GET["name"]; ?><br>
Your email address is: <?php echo $_GET["email"]; ?>

</body>
</html>

خروجی این دو روش (متد های GET و POST) عینا یکی خواهد بود اماتفاوتشان چیست؟ قبل از بررسی تفاوت این دو متد باید نکته ی بسیار مهمی را به شما تذکر بدهیم: این روش فقط برای یادگیری است و به هیچ عنوان نباید در سرور واقعی استفاده شود! در واقع کلیت کار همین است اما در کدهای بالا هیچ کاری برای اعتبار سنجی و پاک سازی کدها انجام نداده ایم. بنابراین کسی که ذره ای دانش برنامه نویسی داشته باشد می تواند به سرور و کدهای ما حمله کند و یا حتی سایت ما را هک کند.

تفاوت GET و POST

GET و POST هر دو آرایه ای ایجاد می کنند (... key1 => value1, key2 => value2, key3 => value3) که ما از طریق index به مقدار اصلی دست پیدا می کنیم (در HTML یاد گرفته ایم که attribute ای به نام names در فرم ها همان index در سمت سرور خواهد بود). از طرفی هر دوی آن ها سوپرگلوبال هستند بنابراین در هر قسمتی از کد در دسترس خواهند بود و از نظر scope هیچ محدودیتی ندارند. تفاوت فقط در این است که:

  • GET_$ آرایه ای از مقادیر است که از طریق URL به اسکریپت ارسال می شود.
  • POST_$ آرایه ای از مقادیر است که از طریق متد HTTP POST به اسکریپت ارسال می شود.

سوال: با این حساب چه زمانی باید از GET و چه زمانی باید از POST استفاده کنیم؟

پاسخ: اطلاعاتی که از طریق GET ارسال شوند برای همه قابل دیدن هستند (همه چیز در URL است) بنابراین می توان چنین صفحاتی را bookmark کرد. همچنین GET محدودیت 2000 کاراکتری برای ارسال اطلاعات دارد. بر این اساس می توان گفت که اطلاعات حساس مانند رمز عبور هیچگاه نباید از طریق GET ارسال شوند اما اطلاعات ارسالی از طریق POST برای دیگران قابل مشاهده نیست (اطلاعات در خود درخواست HTTP request قرار دارند) و هیچ محدودیت در حجم اطلاعات ارسالی نیز ندارد. همچنین POST از ویژگی های پیشرفته مانند قابلیت ارسال ورودی های باینری و چند بخشی (مثل آپلود فایل) پشتیبانی می کند اما نمی توان این صفحات را bookmark کرد. بنابراین به صورت یک قانون کلی می توان گفت که اطلاعات فرم هایتان را همیشه به صورت POST ارسال کنید مگر اینکه دلیل خاصی داشته باشید.

پردازش امن اطلاعات فرم ها در PHP

در هنگام پردازش و دریافت اطلاعات فرم های PHP همیشه حواستان به امنیت باشد تا از شر هکرها دور بمانید. فرمی که می خواهیم روی آن کار کنیم بدین شکل است:

<!DOCTYPE HTML>  
<html>
<head>
<style>
.error {color: #FF0000;}
</style>
</head>
<body>  


<h2>PHP Form Validation Example</h2>
<p><span class="error">* required field</span></p>
<form method="post" action="/demo/demo_form_validation_complete.php">  
  Name: <input type="text" name="name" value="">
  <span class="error">* </span>
  <br><br>
  E-mail: <input type="text" name="email" value="">
  <span class="error">* </span>
  <br><br>
  Website: <input type="text" name="website" value="">
  <span class="error"></span>
  <br><br>
  Comment: <textarea name="comment" rows="5" cols="40"></textarea>
  <br><br>
  Gender:
  <input type="radio" name="gender"  value="female">Female
  <input type="radio" name="gender"  value="male">Male
  <input type="radio" name="gender"  value="other">Other  
  <span class="error">* </span>
  <br><br>
  <input type="submit" name="submit" value="Submit">  
</form>

<h2>Your Input:</h2><br><br><br><br>
</body>
</html>

مشاهده ی خروجی در JSBin

قوانینی که برای فرم بالا وضع خواهیم کرد در جدول زیر ذکر شده اند:

فیلد مورد نظر قانون اعتبار سنجی
Name (نام کاربر) Required (یعنی پر کردن این قسمت الزامی است). فقط باید شامل حروف و اسپیس باشد (نمیتواند هیچ عددی، علامت خاصی یا ... بگیرد).
E-mail (ایمیل) Required. همچنین باید قالب ایمیل را داشته باشد یعنی دارای علامت @ و . (نقطه) باشد.
Website (وب سایت کاربر) Optional (یعنی پر کردن این قسمت دلخواه است). همچنین باید از قالب URL ها پیروی کند.
Comment (نظر کاربر) Optional و درون یک textarea است.
Gender (جنسیت کاربر) Required حتما باید یکی را انتخاب کرده باشد.

یک بار دیگر به فیلدهای متنی و radio button ها نگاهی بیندازید:

Name: <input type="text" name="name">
E-mail: <input type="text" name="email">
Website: <input type="text" name="website">
Comment: <textarea name="comment" rows="5" cols="40"></textarea>


Gender:
<input type="radio" name="gender" value="female">Female
<input type="radio" name="gender" value="male">Male
<input type="radio" name="gender" value="other">Other

کد HTML خود فرم نیز بدین صورت است:

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

بنابراین از متد "method="post استفاده می کنیم. احتمالا برایتان سوال پیش آمده که ["SERVER["PHP_SELF_$ چیست؟ ["SERVER["PHP_SELF_$ یک سوپرگلوبال است که نام فایل اسکریپت فعلی و در حال اجرا را به ما برمیگرداند. بنابراین به جای رفتن به یک صفحه ی دیگر، فرم کاربر در همان صفحه پردازش می شود و اگر مشکلی داشت خطا ها نیز در همان صفحه نمایش داده خواهند شد. ()htmlspecialchars نیز کاراکترهای خاص را به کاراکترهای HTML تبدیل می کند بنابراین علامت هایی مانند < و > با ;lt& و ;gt& عوض می شوند. این کار مانع حملات Cross-site Scripting یا XSS (تزریق کد HTM یا جاوا اسکریپت) می شود.

هشدار در مورد ["SERVER["PHP_SELF_$

هکرها می توانند از ["SERVER["PHP_SELF_$ برای صدمه زدن به شما استفاده کنند؛ آن ها می توانند یک علامت اسلش / را اضافه کرده و سپس دستورات XSS خود را بنویسند. به طور مثال فرض کنید کد زیر را در فایلی به نام test_form.php داشته باشیم:

<form method="post" action="<?php echo $_SERVER["PHP_SELF"];?>">

اگر کاربر یک آدرس عادی مانند http://www.example.com/test_form.php را در سایت وارد کند کد ما به این شکل در می آید:

<form method="post" action="test_form.php">

تا اینجا مشکلی نیست اما اگر کاربری آدرس زیر را وارد کند چطور؟

http://www.example.com/test_form.php/%22%3E%3Cscript%3Ealert('hacked')%3C/script%3E

در این صورت کد ما تبدیل به این کد می شود:

<form method="post" action="test_form.php/"><script>alert('hacked')</script>

و برای کاربر پیام hacked به نمایش در می آید. این یک مثال ساده و بچگانه است اما اصل موضوع را ثابت می کند. هکرها می توانند با استفاده از همین تگ <script> کاربر را به یک آدرس دیگر منتقل کند تا اطلاعات او را بدزدد یا اینکه متغیرهای سراسری را دستکاری کند تا فرم در آدرس دیگری ثبت شود و الی آخر ...

راه جلوگیری از این مشکل استفاده از ()htmlspecialchars است:

<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

حالا اگر کاربر بخواهد خرابکاری کند کد او تبدیل به کد زیر می شود:

<form method="post" action="test_form.php/&quot;&gt;&lt;script&gt;alert('hacked')&lt;/script&gt;">

اعتبار سنجی داده های فرم

اولین کاری که باید انجام دهیم این است که تمامی متغیرها را از تابع ()htmlspecialchars رد کنیم تا از حملاتی که ذکر شد جلوگیری کنیم. دو کار دیگر نیز باید انجام شود:

  • با استفاده از تابع ()trim در PHP کاراکترهای اضافی را حذف کنید (مانند اسپیس های اضافی یا tab و ....).
  • با استفاده از تابع ()stripslashes در PHP علامت های \ (بک اسلش) را از داده های کاربر حذف کنید.

در قدم بعدی باید تابعی بنویسیم که تمامی موارد را برای ما چک کند در غیر این صورت باید هر بار و برای هر فرم کدها را دوباره‌نویسی یا کپی کنیم. ما نام این تابع را ()test_input قرار می دهیم. حالا می توانیم متغیر POST_$ را با ()test_input چک کنیم. اسکریپت نهایی ما بدین شکل خواهد بود:

<!DOCTYPE HTML>  
<html>
<head>
</head>
<body>  

<?php
// define variables and set to empty values
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST") {
  $name = test_input($_POST["name"]);
  $email = test_input($_POST["email"]);
  $website = test_input($_POST["website"]);
  $comment = test_input($_POST["comment"]);
  $gender = test_input($_POST["gender"]);
}

function test_input($data) {
  $data = trim($data);
  $data = stripslashes($data);
  $data = htmlspecialchars($data);
  return $data;
}
?>

<h2>PHP Form Validation Example</h2>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">  
  Name: <input type="text" name="name">
  <br><br>
  E-mail: <input type="text" name="email">
  <br><br>
  Website: <input type="text" name="website">
  <br><br>
  Comment: <textarea name="comment" rows="5" cols="40"></textarea>
  <br><br>
  Gender:
  <input type="radio" name="gender" value="female">Female
  <input type="radio" name="gender" value="male">Male
  <input type="radio" name="gender" value="other">Other
  <br><br>
  <input type="submit" name="submit" value="Submit">  
</form>

<?php
echo "<h2>Your Input:</h2>";
echo $name;
echo "<br>";
echo $email;
echo "<br>";
echo $website;
echo "<br>";
echo $comment;
echo "<br>";
echo $gender;
?>

</body>
</html>

صفحه ی تست این کد

دقت داشته باشید که در ابتدای کد با استفاده از ["SERVER["REQUEST_METHOD_$ چک کرده ایم تا ببینیم آیا متد POST است یا خیر. اگر POST بود یعنی فرم ثبت شده و باید اعتبار سنجی شود، در غیر این صورت یک فرم خالی را دوباره نمایش می دهیم (چرا که در همان صفحه هستیم). همچنین توجه داشته باشید که تماملی فیلدها در فرم بالا دلخواه هستند و کاربر می تواند بدون وارد کردن هیچ اطلاعات خاصی آن را ثبت کند. در جلسات بعدی به سراغ این موارد خواهیم رفت.

تکنیک های معرفی شده در این مقاله کارآمد و موثر هستند اما برای بهبود دانش خود و امنیت وب سایت هایتان باید سری به مقالات پیشرفته ی حوزه ی امنیت بزنید و روش های پیچیده را نیز یاد بگیرید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش PHP 7 توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.