به نام خداوندی که به آدمی آنچه را که نمیدانست تعلیم داد.
مشکلات امنیتی رایج استفاده جاوااسکریپت در بک اند
امروز جاوااسکریپت فقط در فرانت استفاده نمی شود و استفاده از جاوا اسکریپت در بک اند توسط runtime هایی همچون nodejs متداول شده است.
در این مطلب میخواهیم مشکلاتی که اغلب خود برنامه نویسان در بک اند به وجود می آورند را بررسی کنیم.
انواع Injection
در این نوع attack ها سعی ما بر این است از جاهایی که توسعه دهنده مقادیر ورودی را کنترل نکرده است مقادیر خودمان را وارد کنیم.
۱- تزریق SQL/noSQL injection
مثال noSQL
در مثال ما پایگاه داده مورد استفاده ما mongodb است.
در این پایگاه داده متغیر هایی مانند ne$(به معنی نابرابر) و gt$( به معنی بزرگتر) که می توان از آن ها سوء استفاده کرد.
همین موضوع کافی است تا در query که از ما گرفته میشود به جای مقدار درخواست شده متغیر ne$
یا gt$ بگذاریم.
برای مثال در json زیر ایمیل و کلمه عبور از ما گرفته می شود و با مدل User چک می شود، کافی است به جای مقدار واقعی payload خود را بگذاریم.
{
"email": {"$gt": ""},
"password": {"$gt": ""}
}
این مقدار ها باعت می شود دیتابیس هر مقداری را که بزرگتر از "" را برگرداند و این موضوع یعنی اینکه همه کاربرانی که ایمیل و کلمه عبورشان بزرگتر از "" میتواند منظور query ما باشد.
حالا نگاهی به کد آسیب پذیر بی اندازیم:
app.post('/login',function(req,res){
User.findOne({'email':req.body.email,'password':req.body.password},function(err,data){
if(err){
res.send(err);
}else if(data){
res.send('User Login Successful');
}else {
res.send('Wrong Username Password Combination');
}
})
});
توسعه دهنده بدون هیچ input validation مقادیر ایمیل و پسورد را از کاربر می گیرد کد بهبود یافته مانند زیر می شود:
app.post('/login',function(req,res){
var email = req.body.email;
var password = req.body.password;
User.findOne({'email': { $in: [email] },'password': { $in: [password] }},function(err,data){
if(err){
res.send(err);
}else if(data){
res.send('User Login Successful');
}else {
res.send('Wrong Username Password Combination');
}
})
});
بهبود فقط با اضافه شدن in$ به این معنی که مقدار فرستاده شده کاربر اگر در آرایه مقادیر بود مقدار True برگردان.
توضیحات بیشتر در scotch.io
مثال SQL
فرض کنید توسعه دهنده نام کاربری را به این شکل در query خود قرار می دهد.
SELECT * FROM users WHERE username = '" + username + "';
خب ما میتوانیم داخل username این payload را قرار دهیم
' OR '1'='1';
یعنی query را ببندیم و مقدار ۱=۱ را قرار دهیم تا query توسط ما True شود.
اگر هم بعد از username مقادیر دیگری هم مانند مثال زیر گرفته می شود کافی است بقیه query را کامنت کنیم.
SELECT * FROM users WHERE username = '" + username + "' and password = '" + password + "';
پس
' OR '1'='1';--
برای بهبود کد می توان به جای اینکه مستقیم مقدار کاربر را در query قرار داد آن را توسط کتابخانه pg از کاربر گفت.
توضیحات بیشتر به همراه آموزش تست توسط sqlmap در nearform.com
مثال جالب دیگر برای دور زدن Authentication در medium
۲- تزریق Command Injection
توابعی مثل ()eval، setTimeout()، setInterval()، Function() که مقادیری را از کاربر می توانند بگیرند اگر دستوراتی مانند:
- while(1)
- process.exit()
- process.kill(process.pid)
بگیرند باعث حملات تکذیب سرور یا Denial of Service شود.
البته می توان فایل های سیستمی یا دایرکتوری ها را نیز هم با این Attack احتمالا خواند
دستوراتی مانند:
- res.end(require('fs').readdirSync('../..').toString())
- res.end(require('fs').readFileSync('../../../../../../../../../../etc/passwd'))
کد آسیب پذیر:
var cmd = eval(req.body.cmd);
کد بهبود یافته:
var cmd = parseInt(req.body.cmd);
توضیحات بیشتر در کتابچه nodegoat
۳- حملات XXE
فرض کنید قرار است اطلاعاتی را از کاربر در قالب xml بگیرید مانند کد زیر
var products = libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:true,noblanks:true})
به صورت پیش فرض کتابخانه libxmljs می تواند فایل های اکسترنال را هم فراخوانی کنید و ما هم از این فرصت استفاده می کنیم و یک entity تعریف می کنیم و ارسال می کنیم:
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY bar SYSTEM "file:///etc/passwd" >]>
<products>
<product>
<name>Playstation 4</name>
<code>274</code>
<tags>gaming console</tags>
<description>&bar;</description>
</product>
</products>
عملا باید بتوانیم محتوا فایل passwd را بخوانیم.
کد بهبود یافته:
var products = libxmljs.parseXmlString(req.files.products.data.toString('utf8'), {noent:false,noblanks:true})
توضیحات بیشتر در کتابچه appsecco
۵-حملات Insecure Deserialization
اول از همه نگاهی به payload اتک بی اندازیم:
{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('id;cat /etc/passwd', function(error, stdout, stderr) { console.log(stdout) });}()"}
با این payload می توانیم محتوا passwd را بخوانیم! چطور؟
اگر موقع deserialization کردن مانند کد زیر به صورت نا امن deserialization بکنیم این آسیب پذیری به وجود می آید:
var products = serialize.unserialize(req.files.products.data.toString('utf8'))
کد بهبود یافته می تواند مانند زیر باشد:
var products = JSON.parse(req.files.products.data.toString('utf8'))
توضیح بیشتر در کتابچه appsecco
منابع
معذرت بابت کم و کاستی های فراوان این مطلب
خیلی خوشحال میشوم نظرات خود را اینجا بگذارید
با تشکر از شما
مطلبی دیگر از این انتشارات
اجرای دستورات ES6 در NodeJs همراه با NodeMon
مطلبی دیگر از این انتشارات
5 فریمورک nodejs که بهره وری کدتان را افزایش میدهند
مطلبی دیگر از این انتشارات
آموزش استفاده از module.exports در node.js