در نوشته های قبلی در مورد مبانی امنیت برنامههای تحت وب و امنیت در PHP توضیحاتی رو ارائه دادم. در این پست قصد دارم نکات و آسیب پذیریهایی را که هنگام نوشتن کدهای PHP، امکان رخ دادنشان هست و نیاز به توجه و حساسیت بیشتری از جانب برنامه نویسان و توسعه دهندگان دارد را توضیح دهم.
زبان PHP محبوبترین زبان برنامه نویسی سمت سرور است. بر اساس دادههای سایت W3Techs در سال ۲۰۱۹، ۷۹ درصد از وب سایتها، قدرت گرفته از PHP هستند. از آنجایی که PHP زبان محبوبی است، امنیت در PHP امری ضروری است. متاسفانه تعداد برنامههای آسیب پذیر نوشته شده با PHP بسیار زیاد است.
منابعی که این آسیب پذیریها را شرح دادن اکثرا از mitre.org و OWASP هستند. اگر فرصتی باشه در مورد هر کدام از این آسیب پذیریها در پستهای جداگانهای توضیحات بیشتری ارائه میدم. این نکته رو هم بگم که بعضی از آسیب پذیریها انواع مختلفی دارند. ولی از آنجایی که هدف این پست تنها معرفی گونههای مختلف آسیب پذیری است، در اینجا ذکر نشدن. این پست فقط برای برنامه نویسان PHP نیست و میتواند برای همه برنامه نویسان وب مفید باشد. ممکن است خیلی از آسیب پذیریها را فراموش کرده باشم که در این پست بیارم، اگر شما هم آسیب پذیری غیر از این موارد میشناسید و یا ایرادی میبینید خوشحال میشم تو نظرها اعلام کنید تا اعمال کنم. نکته بعدی اینکه، قطعه کدهایی که در زیر آسیب پذیریها آورده شده، نمونه کدآسیب پذیر است و نباید به این فرم در برنامه استفاده شود. برای یادگیری نحوه صحیح نوشتن و جلوگیری از حمله، میتوانید از مراجع کمک بگیرید.
آسیب پذیری در برنامههای تحت وب به ۴ دسته تقسیم میشود: Medium, High, Critical و Low:
آسیب پذیریهایی با درجه اهمیت حیاتی که معمولا برای اکسپلویت کردنشان نیاز به استفاده از تکنیکهای مهندسی اجتماعی نیست و میتواند منجر به آسیب دیدن زیرساخت و یا از دست رفتن اطلاعات حساس شود. در ادامه آسیب پذیریهای این دسته رو به صورت مختصر توضیح میدم.
این حمله زمانی اتفاق میافتد که داده ورودی به عنوان یک دستور سیستمی تفسیر و اجرا شود:
$file=$_GET['filename']; system("rm $file");
زمانی رخ میدهد که داده ورودی به عنوان یک دستور SQL، تفسیر و اجرا شود:
$articleid = $_GET['article']; $query = "SELECT * FROM articles WHERE articleid = '$articleid'"
زمانی زخ میدهد که داده ورودی به عنوان یک کد PHP، تفسیر و اجرا شود:
$myvar = "varname" $x = $_GET['arg']; eval("\$myvar = \$x;");
زمانی که مهاجم بتواند هر فایل با هر پسوند دلخواهی را در سرور بارگذاری یا ایجاد کند این حمله صورت میگیرد:
$target_dir = "uploads/" $target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]); if(isset($_POST["submit"])) { move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)}
با استفاده از include برنامه نویس قادر است تا یک فایل php را گرفته و از کدهای آن در فایل جاری استفاده کند. حالا چه اتفاقی میافتد اگر این فایل، یک فایل مخرب باشد:
$file = $_GET['file']; include($file);
این آسیب پذیری هم زمانی رخ میدهد که ورودی کاربر قبل از آنکه sanitize شود در اختیار تابع unserialize قرار گیرد:
$user_data = unserialize($_GET['data']);
آسیب پذیریهای این دسته معمولا اکسپلویت کردنشان سختتر از دسته Critical است. مهاجم بعد از کسپلویت، میتواند سطح دسترسی خودش رو در سیستم افزایش، بخشی از دادهها را سرقت و یا باعث از کار افتادن سیستم شود.
شبیه آسیب پذیری File Inclusion است. با این تفاوت که مهاجم فقط قادر به اجرای فایلهای محلی در سرور است:
$file = $_GET['file']; include('directory/' . $file);
اِلدَپ یک پروتکل در لایه کاربرد، برای ارتباط با دایرکتوری سرویس است. در صورتی که ورودی کاربر sanitize نشود مهاجم قادر است که اطلاعات موجود در LDAP را مشاهده، تغییر و یا دستور مد نظرش را اجرا کند:
class LDAPAuthenticator { public $conn; public $host; function __construct($host = "localhost") { $this->host = $host; } function authenticate($user, $pass) { $result = []; $this->conn = ldap_connect($this->host); ldap_set_option( $this->conn, LDAP_OPT_PROTOCOL_VERSION, 3 ); if (!@ldap_bind($this->conn)) return -1; $result = ldap_search( $this->conn, "", "(&(uid=$user)(userPassword=$pass))" ); $result = ldap_get_entries($this->conn, $result); return ($result["count"] > 0 ? 1 : 0); } } if(isset($_GET["u"]) && isset($_GET["p"])) { $ldap = new LDAPAuthenticator(); if ($ldap->authenticate($_GET["u"], $_GET["p"])) { echo "You are now logged in!" } else { echo "Username or password unknown!" } }
اشاره به یک نوع حمله، که بخاطر sanitize نکردن ورودی، مهاجم را قادر میسازد که به فایلهای غیر از آنچه برنامه نویس انتظار دارد دسترسی داشته باشد:
$file = $_GET['file']; file_get_contents('directory/' . $file);
var_dump(PHP_INT_MAX+1); //float(9.2233720368548E+18)
var_dump("0" == "-0");
این آسیب پذیری اولین بار در سال ۲۰۱۸ و در کنفرانس Black Hat معرفی شد. مشابه Object Injection است، با این تفاوت که نیازی به تابع unserialize برای اجرا ندارد. ولی نیاز به یک سری شرایط مثل تعریف magic methodها دارد که اگر فرصت کنم توی پستهای بعدی مفصلتر توضیح میدم. مثال زیر دارای آسیب پذیری از این نوع است:
class AnyClass { function __destruct() { echo $this->data; } } include('phar://test.phar');
آسیب پذیریهای این دسته معمولا برای بهره برداری نیاز به استفاده از تکنیکهای مهندسی اجتماعی دارند. معمولا بعد از اکسپولیت تاثیر کمی برو روی کسب و کار سازمان میگذارند. اکسپلویت کردن آنها معمولا مشکل است. یا اینکه نیاز به سطح دسترسی خاصی دارند.
زمانی رخ میدهد که به کاربر اجازه دهیم فایل دلخواهی را حذف کند:
unlink($_GET($file));
گاهی اوقات مهاجم نیازی به سرقت دادهها ندارد. با دستکاری دادهها، به آنچه نیاز دارد، میرسد.نباید به مهاجم اجازه دهیم تا محتویات فایل یا داه ای را تغییر دهد.
یک نمونه از حملات تزریق کد است که مهاجم کدهای مخرب خود را در یک وب سایت مطمئن تزریق میکند. هدف این گونه حملات، بازدیدکنندگان سایت است.
print "Not found: " . urldecode($_SERVER["REQUEST_URI"]);
زبان XQuery (XML Query)، یک پرس و جو بر روی داده های ساخت یافته و غیر ساخت یافته مانند XML و فایلهای متنی است. در این نوع حمله، مهاجم قادر به تغییر مفهوم پرس و جو است:
$memstor = InMemoryStore::getInstance(); $z = Zorba::getInstance($memstor); try { // get data manager $dataman = $z->getXmlDataManager(); // load external XML document $dataman->loadDocument('users.xml', file_get_contents('users.xml')); // create and compile query $express = "for \$user in doc(users.xml)//user[username='" . $_GET["username"] . "'and pass='" . $_GET["password"] . "'] return \$user" $query = $zorba->compileQuery($express); // execute query $result = $query->execute();
زبان XPath یک پرس و جو برای انتخاب نودها در یک سند XML است . این آسیب پذیری زمانی رخ میدهد که دادههای ورودی از کاربر بدون sanitize کردن در در یک پرس و جو قرار داده شود:
"//Employee[UserName/text()='" & $_GET['UserName'] & "' And Password/text()='" & $_GET['Password'] & "']"
این آسیب پذیری هم زمانی رخ میدهد که به اشتباه از Reflection در برنامه استفاده کنید. مهاجم معمولا در صورت وجود این نوع آسیب پذیری قادر به کنترل جریان برنامه و تزریق کد است.
زبان XSLT مبتنی بر XML است و شیوه تبدیل از یک فایل XML به فایلی دیگر را توصیف میکند. در این نوع حمله مهاجم قادر است، ساختار و محتوای یک فایل XML رو تغییر دهد. که منجر به خواندن از یک فایل یا اجرای یک دستور دلخواه میشود:
$xml = new DOMDocument; $xml->load('local.xml'); $xsl = new DOMDocument; $xsl->load($_GET['key']); $processor = new XSLTProcessor; $processor->registerPHPFunctions(); $processor->importStyleSheet($xsl); echo $processor->transformToXML($xml);
یک نمونه از حملات که هدف آن برنامه ای است که ورودی XML را پردازش میکند. زمانی که ورودی XML حاوی یک رفرنس به یک موجودیت خارجی باشد و پیکربندی پردازشگر XML ضعفی داشته باشد، این حمله احتمال رخ دادنش وجود دارد.
حملهای مشابه SQL Injection، با این تفاوت که بر روی پایگاه دادههای NoSQL است.
در این حمله مهاجم یک session id معتبری دارد که کاربر را به سمت استفاده از این session id هدایت میکند. در واقع این حمله زمانی رخ میدهد که برنامه به جای تولید یک session id جدید در هر بار ارتباط، از یک session id ذخیره شده استفاده کند.
این حمله زمانی اتفاق میافتد که برنامه، ورودی را از مهاجم دریافت و به آن تغییر مسیر دهد. این کار میتواند کاربر را به یک سایت مخرب هدایت کند:
<?php header("Location: http://www.mysite.com"); exit; ?>
این حمله زمانی رخ میدهد که داده از طریق یک منبع نامطمئن مانند HTTP Request به برنامه ارسال شود. و یا اینکه داده ارسال شده به کاربر نهایی، بدون هیچگونه اعتبار سنجی در کاراکترهایش، در HTTP response header قرار گیرد.
این حمله زمانی اتفاق که مهاجم با قرار گرفتن در بین کاربر نهایی و سرور، دادههای ارسالی به سمت سرور را تغییر دهد. حمله Man In The Middle.
فرآیندی است که آرایهای از دادهها را به یک کلاس ارسال میکند. یعنی نیاز نیست دادهها را یکی یکی ارسال کنیم. مشکلی ک این روش دارد این است که ممکن است مهاجم مقداری را بدون هیچگونه بررسی به این کلاس ارسال و منجر به نقص امنیتی شود. مثال زیر نمونه ای از این آسیب پذیری است:
class User { private $userid; private $password; private $email; private $isAdmin; //Getters & Setters }
این دسته از آسیب پذیریها اکسپلویت کردنشان مشکل و معمولا نیاز به دسترسی فیزیکی به سرور دارند. همچنین تاثیر کمی بر روی کسب وکار یک سازمان دارند.
این حمله هنگامی رخ میدهد که مهاجم بتواند درخواستی را به سمت سرور ارسال و منابع داخلی سیستم که در حالت عادی امکان دسترسی به آنها از بیرون وجود ندارد، مانند فایلهایی پشت فایروال یا تنظیمات پیکربندی را بخواند. در قطعه کد زیر، مشاهده میکنید که مهاجم کنترل کاملی روی پارامتر url دارد:
<?php if (isset($_GET['url'])){ $url = $_GET['url']; $image = fopen($url, 'rb'); header("Content-Type: image/png"); fpassthru($image);}
از Memcached برای انجام عملیات کش بر روی سرور به صورت داینامیک استفاده میشود. ولی در صورت استفاده نادرست میتواند منجر به نقص امنیتی شود. قطعه کد زیر این آسیب پذیری رو نشان میدهد:
<?php $m = new Memcached(); $m->addServer('localhost', 11211); $m->set(str_repeat(“a”,251),"set injected 0 3600 10\r\n1234567890",30);?>
مشابه حمله Path Traversal، با این تفاوت که تمرکز اصلی آن دسترسی به منابع سیستم است. در مثال زیر نام فایل موقت از کاربر گرفته شده و سپس حذف میشود. ولی یک مهاجم با وارد کردن آدرس یا نام منبعی غیر از آنچه برنامه نویس انتظار دارد، موجب حذف شدن آن منبع میشود:
<?php $rName = $_GET['fileId']; $myfile = fopen('/log/'.$rName, "r"); unlink($myfile)?>
این آسیب پذیری از آنجا ناشی میشود که یک مهاجم بتواند به پیکربندی سیستم دسترسی و آنرا تغییر دهد.
این آسیب پذیری زمانی رخ میدهد که برنامه چندین پارامتر با نام یکسان را پذیرش کند. زمانی که این دادهها به سرور ارسال میشوند، با توجه به تنظیمات و پیکربندی سرور، این دادهها به طرق مختلف پردازش میشوند. مثلا در سرور آپاچی تنها آخرین پارامتر پردازش میشود. مهاجم با بهره برداری از این آسیب پذیری قادر به تغییر رفتار برنامه و حتی دور زدن WAF است.
این آسیب پذیری از آنجا ناشی میشود که برای انجام عملیاتی مثل رمزنگاری فایلها و دادهها، تولید هش، ایجاد اعداد تصادفی و غیر قابل حدس و ... از الگوریتمهای ضعیف یا منسوخ شده استفاده کنیم. برای مثال قطعه کد زیر تابعی برای تولید اعداد تصادفی جهت رمزنگاری است. ولی ایراد آن این است که مقادیر تکراری تولید میکند و مناسب رمزنگاری نیست:
function gen_uuid() { return sprintf( ' x x- x- x- x- x x x', mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0x0fff ) | 0x4000, mt_rand( 0, 0x3fff ) | 0x8000, mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ));}
در انتها جا داره دوباره بگم که این لیست کمبودهای زیادی داره و جمع آوری این لیست حاصل تجربه شخصی خودم در بررسی کدهاست. اگر شما هم پیشنهادی دارید توی نظرها بگید تا اعمال کنم. اگر بتوانم و وقت کنم بعدا همچین پستی برای زبانهای دیگه مثل پایتون، جاوا و سی هم می نویسم.