نکات امنیتی وردپرس
چون وردپرس یک سیستم متن باز است لذا کدهای آن و ساختار پایگاه داده آن برای تمامی افراد قابل دسترس است . به همین منظور بیشتر از سایر برنامه ها در معرض خطر است.رعایت نکات امنیتی حین توسعه پوسته یا افزونه وردپرس لازم است.
Your code works, but is it safe ?
نکات امنیتی که در این مقاله بررسی خواهیم کرد به شرح زیر است :
- Checking User Capabilities
- Data Validation
- Securing Input
- Securing output
- Nonce
بررسی سطح دسترسی کاربران (Checking User Capabilities)
اگر قرار باشد کاربر Action خاصی را روی وب سایت پیاده سازی کند ( چه در پنل ادمین یا فرانت اند ) قبل از هر چیز باید چک کنیم این کاربر مجوز این کار را دارد یا خیر. به این عمل authorization می گوییم.
هر کاربر بعد از ورود به وب سایت به طور اوتوماتیک بر حسب نقشی که در وب سایت داره یه سری سطوح دسترسی به اون داده میشه. در اون سطوح دسترسی یه سری ماژول ها براش مجازه.
User → AccessGroup → ModulesList
User → Roles → Capabilities
خوب قبل از پیاده سازی هر Action درخواست داده شده از سمت کاربر چک کنید که این عمل برای کاربر جاری مجاز هست یا خیر .
برای این کار باید ابتدا بفهمید که این عملی که قراره انجام بشه در کدوم دسته از Capabilities های وردپرس قرار داره. بعد با تابع current_user_can
چک می کنید که این عمل (capabilities) برای کاربر جاری مجازه یا خیر. به طور مثال برای اعمال تغییر روی جدول option یا همون تنظیمات سطح دسترسی manage_options رو چک می کنیم:
<?php if(!current_user_can("manage_options")){ return; }
نکته : لیست سطوح دسترسی وردپرس رو می تونید از این لینک پیدا کنید.
نکته : در زمان ساخت پست تایپ پارامتری به نام capabilities داشتیم که اگر اون پارامتر رو خالی بزاریم سطوح دسترسی پست به اون تعلق می گیره . حال اگر سطح دسترسی جدیدی برای اون action خاص مثلا ویرایش پست (edit_post) تعیین می کردیم در اینجا باید اون مقدار رو برای چک کردن به تابع current_user_can می دادیم.
نکته : این تابع رو به صورت شرطی قبل از اعمال add_action ها یا داخل تابعی که داره به یه hook خاص قلاب میشه بزارید.
معتبر بودن ورودی ها (Data Validation)
یعنی از روی یک pattern بگیم که ورودی معتبر یا غیر معتبر است. معمولا این داده ها از طریق ورودی کاربر یا پارامتر های خروجی وب سرویس ها اعمال میاد.
نمونه هایی از اعتبار سنجی داده ها :
- چک کردن این موضوع که ورودی کاربر خالی نباشد.
- چک کردن شماره تلفن ورودی که تنها شامل اعداد باشد.
- چک کردن کد پستی وارد شده از سمت کاربر (با توجه به قوانینی که کدهای پستی ایجاد می شوند).
- چک کردن تعداد که از عدد صفر باید بیشتر باشد (مثلا تعداد محصول سفارش داده شده).
نکته : اعتبار سنجی داده ها قبل از هر عملی.
نکته : اعتبار سنجی داده می تواند قبل توسط جاوا اسکریپت یا پی اچ پی در فرانت اند یا بک اند وب سایت اتفاق بیفتد.
اعتبار سنجی توسط توابع پیش فرض php:
is_set
وempty
برای این که بفهمیم ورودی هست یا خیر.strlen
برای فهمیدن تعداد کاراکترهای رشته – نوعmb_strlen
برای کاراکتر های فارسی به کار میره. در رابطه با خالی بودن یا نبودن یک آرایه می تونید از تابعcount
استفاده کنید و تعداد اعضای اون رو در بیارید.strpos
برای این که بفهمیم کاراکتری خاص در رشته ما هست یا خیر (این تابع position کاراکتری که بهش دادیم رو به ما میده و اگر وجود نداشت صفر بر میگردونه)in_array
مثلا میتونید لیستی از موارد معتبر در یک آرایه داشته باشید بعد نگاه کنید که آیا این مورد در اون آرایه هست یا خیر.- با استفاده از تابع
preg_match
قالب یا pattern رو تعریف کنید تا معتبر یا غیر معتبر بودن ورودی رو تعیین کنه.
اعتبار سنجی توسط توابع وردپرس
وردپرس هم با استفاده از توابع پیش فرض php و عبارات منظم (Regular Expression) توابعی رو در رابطه با اعتبار سنجی داده ها ساخته است:
-
is_email
که مشخص می کند آیا ایمیل معتبر است یا خیر. username_exists
نام کاربری وجود داره یا خیرvalidate_file
برای اعتبار سنجی این که فایل وجود داره یا خیر (file_exisists)
نکته : توابع زیادی در رابطه با اعتبار سنجی داده ها در وردپرس وجود داره برای پیدا کردن اون ها کافیه به reference توابع و کلاس های وردپرس برید و عباراتی نظیر *_exists
, *_validate
, is_*
را جستجو کنید.
نکته : از Attribute های html هم می تونید برای فیلتر ورودی کابران استفاده کنید (مثلا maxlength).
ایمن سازی ورودی ها (Securing Input)
روند پاکسازی و فیلتر ورودی برای جاهایی که نمیتونیم صد در صد از معتبر بودن ورودی اطمینان حاصل کنیم.
هر زمان داده ای رو گرفتید اون رو پاکسازی کنید. قطعا به هر شکل اگر ورودی داشته باشید از جایی میاد که شما اطلاع ندارید . مثلا اگر کلاس خاصی قرار در پنل ادمین به المنت خاصی داده بشه باید پاکسازی بشه. حتی داده هایی که از پایگاه داده میان باید فیلتر و پاکسازی بشن.
توابع پاکسازی داده ها به دو دسته تقسیم می شوند . پاکسازی ورودی ها (sanitizing is cleaning user input) و پاکسازی خروجی (escaping is securing output) . توابع پاکسازی ورودی با sanitize_*
شروع می شوند و توابع پاکسازی خروجی با esc_*
.
برخی مقادیر پاکسازی ورودی نیاز دارند . مثلا ایمیل اگر کاراکتر غیر مجاز داشت باید حذف بشه ولی اگر قرار باشه ایمیل چاپ بشه خروجیش از نظر رشته ای باید پاکسازی بشه.
تعدادی از توابع پاکسازی ورودی :
<?php sanitize_email("fsaffsa@fsd.as<br>d") //fsaffsa@fsd.asbrd ?>
مثلا در کد بالا علامت های تگ رو از خروجی حذف می کنه.
<?php sanitize_file_name("forbidden char.php"); //forbidden-char.php ?>
فاصله رو تبدیل به – کرد.
sanitize_html_class : کاراکتر های غیر مجاز رو برای کلاس از رشته حذف می کنه . (مثلا برای کلاس ها تنها کاراکتر های a-z , A-Z , 0-1 مجاز هستند اگر فارسی هم بنویسید حذف می کنه).
<?php echo sanitize_html_class("col-md 6 + *&^ dsf@#$%^&*()"); //col-md6dsf ?>
sanitize_text_field : رشته های متنی رو پاکسازی می کنه. (چون فیلدهای متنی استفاده زیادی دارند تابع پر کاربرید به شمار میره)
<?php echo sanitize_text_field("1f<form>fad</form>dfs?a"); //1ffaddfs?a ?>
تگ های html حذف شدند. به این ترتیب اگر ورودی کد جاوا اسکریپت وارد کنند خنثی می شود چون در هیچ تگی نیست توسط مرورگر کد رشته شناخته می شود.
$option
) که برای option_name انتخاب کردید جزو کلید های پیش فرض وردپرس است یا خیر . در صورت بله باید برای کلید مورد نظر مقدار درستی داشته باشد . به طور مثال برای option_name = 'new_admin_email'
حتما باید ایمیل معتبر وارد کنید. (برای مقادیر غیر مجاز در کلید های پیش فرض وردپرس مقدار 0 به عنوان false بر میگرداند).<?php echo sanitize_option( 'thumbnail_size_w', 'fsdfas' ); //0 echo sanitize_option('new_admin_email' , 'info@asabagh.ir!'); //ifno@asabagh.ir ?>
مورد اول باید عدد وارد می کرد نکرد صفر برگردوند و مورد دوم رو هم اصلاح کرد(علامت ! در ایمیل نباید باشد)
sanitize_key : کلیدها در جداول postmeta , commentmeta , option استفاده میشن که شامل یه سری کاراکتر های مجازه . اگر کاربر قراره کلید رو هم خودش تعیین کنه از این تابع استفاده کنید.
نکته : توابعی که در بالا برای شما توضیح دادیم یک مقدار ورودی میگیره و یک مقدار fallback که اگر پس از پاکسازی رشته خالی ماند از اون مقدار fallback استفاده بشه.
ایمن سازی خروجی ها (Securing output)
گاهی ما از ورودی های کاربران به عنوان خروجی استفاده می کنیم . (به طور مثال در دیدگاه ها) در این زمان برنامه ما در معرض خطر حملات XSS قرار خواهد گرفت. پس باید ورودی هایی که قرار چاپ بشه رو ایمن کنیم که هکر ها نتونن از طریق وب سایت ما به کاربران تزریق کد داشته باشند.
پس پاکسازی داده ها برای چاپ برای محافظت از کاربران وب سایت می باشد ⇔ پاکسازی خروجی برای جلوگیری از حملات XSS.
تعدادی از توابع وردپرس برای پاکسازی خروجی :
esc_html : در این تابع هر چی که بنویسید در خروجی چاپ میشه ، این کار به وسیله تبدیل کردن کدها به مقدار encode شده اون ها اتقاق می افته به طور مثال < تبدیل به >
چه می خواد تگ باشه یا کد های استایل.(پس هر وقت خواستید خروجی تون خام باشه و هیچ گونه تگی نداشته باشه از این تابع استفاده کنید.)
<?php echo esc_html( '<a href="http://www.example.com/">A link</a>' ); ?>
خروجی کد بالا به شکل زیر خواهد بود.
<a href=”http://www.example.com/”>A link</a>
esc_url : این تابع برای پاکسازی لینک ها از کدهای مخرب می باشد. طبیعیه که یه سری موارد رو برای لینک بودن ورودی این تابع رعایت می کنه.
<?php echo esc_url("https://website.&com<script>alert('this page hacked');</script>"); ?>
خروجی کد بالا به شکل زیر خواهد بود:
https://website.&comscriptalert(‘this%20page%20hacked’);/script
نکته : زمانی که دارید src و href رو به صورت داینامیک از ورودی های کاربر وارد می کنید از این تابع استفاده کنید.
esc_attr : این تابع برای پاکسازی خروجی ها در زمان وارد کردن ویژگی به تگ ها است (Attributes includes : class , name , value , data)
<?php $fname = ( isset( $_POST['fname'] ) ) ? $_POST['fname'] : ''; ?> <input type="text" name="fname" value="<?php echo esc_attr( $fname ); ?>">
این تابع مختص Attribute هاست و علاوه بر جلوگیری از حملات XSS کارکتر های غیر مجاز برای Attribute را هم پاکسازی می کند.
esc_sql : زمانی که قراره از کاربر داده ای بگیری و اون داده رو در یک SQL Query قرار بدی و نتیجه رو بگیری این تابع خیلی به کار میاد. کمک می کنه که کاربر لای داده خودش کد sql تزریق نکنه :
<?php $name = esc_sql( $name ); $status = esc_sql( $status ); $wpdb->get_var( "SELECT something FROM table WHERE foo = '$name' and status = '$status'" ); ?>
نکته : اگر از $wpdb->prepare()
استفاده می کنید نیازی به استفاده از تابع esc_sql
ندارید.
پاکسازی به همراه ترجمه :
هر گاه بحث خروجی باشه ترجمه هم هست. توابعی که هم قابلیت ترجمه و هم پاکسازی رو داره .
esc_html__()
esc_html_e()
esc_attr__()
esc_attr_e()
esc_attr_x()
نحوه کار با اونها دقیقا مثل کار با توابع معمولی ترجمه است (در پشت کار پاسکازی هم انجام میشه)
Nonce
nonce ها یک مقدار ناشناس رمز نگاری شده هستند که برای جاهایی که کاربر اجازه داره اطلاعاتی رو بفرسته استفاده میشه. نحوه کار اون ها به شکل زیر است.
انتخاب رشته به عنوان nonce ⇐ رمز شدن این رشته ⇐ ارسال به سرور ⇐ رمزشکنی رشته ارسالی در سرور و چک کردن با مقدار اولیه
بدین ترتیب فردی که درخواست رو میده باید در فرم حضور داشته باشه . (منظورم مکان درخواسته)
در حالت کلی ما در دوجا از nonce ها استفاده می کنیم:
- در زمانی که درخواستی قراره از طریق لینک و با پارامتر GET ارسال بشه(wp_create_nonce , wp_verify_nonce)
- زمانی که قراره از طریق فرم با پارامتر POST یا GET درخواستی ارسال بشه. (wp_nonce_field , wp_verify_nonce)
دو تابع داریم یکی برای تولید nonce (گرفتن رشته و رمز کردن اون ) و دیگری برای تایید nonce (رمز شکنی رشته و مقایسه با مقدار اولیه)
تولید nonce با تابع wp_create_nonce
<?php wp_create_nonce( $action ); ?>
یک پارامتر می گیره ($action
) و همون رشته ای هست که به عنوان کلید بهش میدید (هر چی تو حالتونه بدید ولی قابل تشخیص نباشه از مقادیر قابل حدس استفاده نکنید)
خروجی این تابع دقیقا همون رشته رمز نگاری شده است.
تولید nonce با تابع wp_nonce_field
<?php wp_nonce_field( $action, $name ) ?>
همون طور که از نام این تابع پیداست خروجی اون یک فیلد مخفی (input type hidden) است که حاوی نام ( name ) پارامتر $name
و مقدار( value ) رمز شده پارامتر $action
است.
در حقیقت تابع wp_nonce_field کار زیر را با استفاده از تابع wp_create_nonce می کند . به مثال زیر توجه کنید:
<input type="hidden" name="<?php echo $name ?>" value="<?php echo wp_create_nonce($action);?>">
اعتبار سنجی nonce به وسیله تابع wp_verify_noce
<?php wp_verify_nonce( $nonce, $action ); ?>
پارامتر اول ( $nonce
) همون مقداریه که از طریق GET یا POST اومده (nonce رمز نگاری شده) و پارامتر دوم ( $action
) همون رشته ایه که در تابع قبل در فرم جای دادید.
نکته : خروجی این تابع یک مقدار 0 یا 1 است که معتبر بودن یا نبودن nonce را تشخیص می دهد.
نکته : استفاده از nonce در در اعمال درخواست ها (چه از طریق لینک با تابع wp_create_nonce و یا چه از طریق با تابع wp_nonce_field باعث جلوگیری از حملات CSRF می شود)
دیدگاهتان را بنویسید