زیادہ تر سیکیورٹی مضامین خطرے کی رپورٹوں کی طرح پڑھتے ہیں۔ یہ باہر سے اندر دیکھ کر کمزوریوں کی وضاحت کرتا ہے: حملہ آور کیا کرتے ہیں، اس کا کیا اثر ہوتا ہے، اور عالمی سطح پر کتنے نظام متاثر ہوتے ہیں۔ یہ مفید سیاق و سباق ہے، لیکن یہ انجینئرز کو بہتر نظام بنانے میں مدد نہیں کرتا ہے۔
یہ مضمون مختلف ہے۔ ہر ایک OWASP API سیکیورٹی کی ٹاپ 10 کمزوریوں پر ایک اندرونی نظر۔ یعنی، ہم دیکھتے ہیں کہ انجینئرنگ کے کن فیصلوں سے کمزوری کے وجود میں آیا، کن تعمیراتی اصولوں کی خلاف ورزی ہوئی، اور اس خطرے کو پیداوار میں جاری ہونے سے روکنے کے لیے انجینئرنگ کے کون سے معیارات موجود تھے۔
میں نے ریگولیٹڈ مالیاتی ماحول میں بڑے پیمانے پر پروڈکشن ایپلی کیشنز بنانے میں کئی سال گزارے ہیں۔ اس فہرست میں موجود کمزوریاں نظریاتی نہیں ہیں۔ میں نے ان میں سے بیشتر کو حقیقی نظاموں میں دیکھا ہے۔ ان میں سے کچھ کو پیش ہونے سے پہلے ہی پکڑ لیا گیا۔ جب میں پہنچا تو ان میں سے کچھ پہلے ہی زندہ تھے۔ ان میں سے ہر ایک کو انجینئرنگ ڈسپلن کے ذریعے روکا جا سکتا تھا، سیکورٹی ٹولز سے نہیں۔
یہ اس مضمون میں استعمال شدہ عینک ہے۔
انڈیکس
شرطیں
اس مضمون کو پڑھنے سے پہلے، آپ کو درج ذیل سے واقف ہونا چاہیے:
-
کسی بھی زبان یا فریم ورک میں APIs بنائیں
-
تصدیق کی بنیادی تفہیم: ٹوکن اور سیشنز کیا ہیں؟
-
ڈیٹا بیس کا استفسار اعلی سطح پر کیسا لگتا ہے۔
-
عام سافٹ ویئر آرکیٹیکچر کے تصورات جیسے پرتیں، خدمات اور گیٹ ویز
سیکیورٹی کے پس منظر کی ضرورت نہیں ہے۔ یہ مضمون سیکیورٹی تجزیہ کار کے نقطہ نظر کے بجائے انجینئرنگ کے نقطہ نظر سے تمام خطرات کو بیان کرتا ہے۔
OWASP API سیکورٹی ٹاپ 10 کیا ہیں؟
OWASP کا مطلب ہے اوپن ویب ایپلیکیشن سیکیورٹی پروجیکٹ۔ ایک غیر منافع بخش فاؤنڈیشن جو انجینئرز اور تنظیموں کے لیے آزادانہ طور پر دستیاب حفاظتی رہنمائی تیار کرتی ہے۔ OWASP API سیکیورٹی ٹاپ 10 دنیا بھر کے پروڈکشن سسٹمز میں پائی جانے والی انتہائی اہم API کمزوریوں کی باقاعدگی سے اپ ڈیٹ کردہ فہرست ہے۔
یہ کوئی نظریاتی فہرست نہیں ہے۔ یہ حقیقی حفاظتی واقعات، دخول کی جانچ کے نتائج، اور صنعتوں کے پروڈکشن سسٹمز سے جمع کردہ خطرے کی رپورٹس پر مبنی ہے۔ اس فہرست میں موجود ہر آئٹم کے نتیجے میں حقیقی اعداد و شمار کی خلاف ورزیاں، حقیقی مالی نقصانات، اور حقیقی تنظیموں میں حقیقی ریگولیٹری جرمانے ہوئے ہیں۔
APIs بنانے والے انجینئرز کے لیے، اس فہرست کو سمجھنا اختیاری نہیں ہے۔ یہ بنیادی علم ہے۔
1. BOLA (بروکن آبجیکٹ لیول کی اجازت)
یہ کیا ہے
صارف کی توثیق کی گئی ہے اور اس کے پاس ایک درست ٹوکن ہے۔ تاہم، آپ اپنی درخواست میں شناخت کنندہ کو تبدیل کرکے اس ڈیٹا تک رسائی حاصل کرسکتے ہیں جو آپ کے پاس نہیں ہے۔
GET /api/accounts/12345/transactions
صارف A تصدیق شدہ ہے اور اکاؤنٹ 12345 کا مالک ہے۔ وہ اکاؤنٹ نمبر تبدیل کرتے ہیں۔
GET /api/accounts/99999/transactions
ایک BOLA موجود ہوتا ہے جب API صارف B سے کوئی لین دین واپس کرتا ہے۔ صارف کی توثیق ہوتی ہے، لیکن API اس بات کی تصدیق نہیں کرتا ہے کہ تصدیق شدہ صارف درخواست کردہ وسائل کا مالک ہے۔
یہ دنیا میں سب سے عام API کا خطرہ ہے۔ یہ مسلسل OWASP فہرستوں میں سرفہرست ہے کیونکہ اسے اپنانا آسان ہے اور کوڈ کے جائزوں میں یاد کرنا آسان ہے۔
انجینئرنگ کی ناکامی
BOLA اس وقت ہوتا ہے جب تصدیق کو بائنری سوال کے طور پر سمجھا جاتا ہے۔ کیا یہ صارف مستند ہے؟ ہاں یا نہیں؟ API چیک کرتا ہے کہ آیا صارف کے پاس درست ٹوکن ہے اور وہ وہیں رک جاتا ہے۔ دوسرا سوال کبھی نہیں پوچھا جاتا۔ کیا تصدیق شدہ صارف اس مخصوص وسائل کا مالک ہے جس کی وہ درخواست کر رہے ہیں؟
توثیق شناخت کا علم ہے۔ یہ ثابت کرتا ہے کہ آپ کون ہیں، لیکن یہ کبھی ثابت نہیں ہوتا کہ آپ جو مانگ رہے ہیں وہ آپ کا ہے۔
انجینئرنگ ٹھیک کریں
اجازت صرف API گیٹ وے پر نہیں بلکہ سروس لیئر پر ہونی چاہیے۔ API گیٹ وے تصدیق کر سکتا ہے کہ ٹوکن درست ہے۔ صرف سروس کی پرت ہی جانتی ہے کہ آیا تصدیق شدہ صارف درخواست کیے جانے والے وسائل کا مالک ہے۔
ڈارٹ (سروس لیئر کے ساتھ):
class TransactionService {
final TransactionRepository _repository;
final AuthContext _authContext;
TransactionService(this._repository, this._authContext);
Future, AppException>> getTransactions(
String accountId,
) async {
final currentUserId = _authContext.currentUserId;
// fetch the account first
final account = await _repository.findAccountById(accountId);
if (account == null) {
return Result.failure(AppException.notFound('Account not found'));
}
// verify ownership before returning any data
if (account.ownerId != currentUserId) {
return Result.failure(
AppException.forbidden('Access denied to this account'),
);
}
final transactions = await _repository.findByAccountId(accountId);
return Result.success(transactions);
}
}
aspirate#:
public async Task>> GetTransactions(
string accountId,
ClaimsPrincipal currentUser)
{
var userId = currentUser.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var account = await _repository.FindAccountByIdAsync(accountId);
if (account == null)
return Result.Failure>("Account not found");
// ownership check — never skip this
if (account.OwnerId != userId)
return Result.Failure>("Access denied");
var transactions = await _repository.FindByAccountIdAsync(accountId);
return Result.Success(transactions);
}
کسی مخصوص وسائل تک رسائی کی ہر درخواست پر ملکیت کی تصدیق ہونی چاہیے۔ یہ صرف پہلے لوڈ یا تحریری آپریشن پر نہیں ہوتا ہے۔ ہر ایک درخواست۔
2. تصدیق کی ناکامی۔
یہ کیا ہے
کمزور ٹوکن، کوئی ٹوکن میعاد ختم نہیں، تصدیق کے اختتامی مقامات پر کوئی شرح محدود نہیں، یا ایسے سیشن جو لاگ آؤٹ پر کبھی ختم نہیں ہوتے۔ درخواست کس نے کی ہے اس کی شناخت کے طریقہ کار میں یہ ایک کمزوری ہے۔
انجینئرنگ کی ناکامی
ایک بار جب توثیق کام کر رہی ہے، مسئلہ کو حل سمجھا جاتا ہے۔ انجینئر لاگ ان کو لاگو کرتا ہے، ٹوکن واپس وصول کرتا ہے، اور جاری رہتا ہے۔ یہ ناکامی کے طریقوں کے لیے ڈیزائن نہیں کیا گیا ہے (مثال کے طور پر اگر کوئی ٹوکن چوری ہو جاتا ہے، حملہ آور پاس ورڈ کی فہرست کے ساتھ لاگ ان اینڈ پوائنٹ کے ساتھ گڑبڑ کرتا ہے، یا کچھ ایسا ہوتا ہے جب صارف لاگ آؤٹ ہوتا ہے)۔
توثیق کے طریقہ کار کو مخالف حالات کے ساتھ ساتھ خوشگوار راستے کے استعمال کے لیے ڈیزائن کیا جانا چاہیے۔
انجینئرنگ ٹھیک کریں
ٹوکن کی میعاد ختم ہونے کی ضرورت ہے۔ رسائی کے ٹوکن قلیل المدت (15 منٹ سے 1 گھنٹہ) ہونے چاہئیں۔ ریفریش ٹوکن سیشن کے تسلسل کو سنبھالتے ہیں۔ قلیل المدت رسائی ٹوکن ٹوکن چوری کی صورت میں نقصان کی کھڑکی کو محدود کرتے ہیں۔
تصدیق کے اختتامی نقطہ کی شرح کو محدود کرنا ضروری ہے۔ ریٹ محدود کیے بغیر لاگ ان اینڈ پوائنٹ وحشیانہ طاقت کے حملوں کی کھلی دعوت ہے۔ ایک حملہ آور 10 ملین ای میل اور پاس ورڈ کے امتزاج کی فہرست کے ساتھ ہر امتزاج کو طریقہ سے آزمائے گا۔
سیشن دراصل لاگ آؤٹ پر ختم ہونا چاہیے۔ اگر آپ اسٹیٹفول سیشنز استعمال کرتے ہیں، تو آپ کو لاگ آؤٹ پر سرور سائیڈ پر سیشن کو باطل کرنا ہوگا۔ اگر آپ JWT استعمال کرتے ہیں تو یا تو ٹوکن بلیک لسٹ کو برقرار رکھیں یا ریفریش ٹوکن روٹیشن کے ساتھ ایک بہت ہی مختصر مدت ختم ہونے کا استعمال کریں۔
ڈارٹ:
class AuthService {
final TokenRepository _tokenRepository;
final RateLimiter _rateLimiter;
AuthService(this._tokenRepository, this._rateLimiter);
Future> login(
String email,
String password,
String ipAddress,
) async {
// rate limit by IP to prevent brute force
final isAllowed = await _rateLimiter.checkLimit(
key: 'login:$ipAddress',
maxAttempts: 5,
windowSeconds: 300,
);
if (!isAllowed) {
return Result.failure(
AppException.rateLimited('Too many login attempts. Try again later.'),
);
}
final user = await _validateCredentials(email, password);
if (user == null) {
return Result.failure(
AppException.unauthorized('Invalid credentials'),
);
}
// short-lived access token
final accessToken = _generateAccessToken(user, expiryMinutes: 15);
// longer-lived refresh token, stored server-side
final refreshToken = _generateRefreshToken(user);
await _tokenRepository.storeRefreshToken(user.id, refreshToken);
return Result.success(AuthTokens(
accessToken: accessToken,
refreshToken: refreshToken,
));
}
Future logout(String userId, String refreshToken) async {
// invalidate the refresh token server-side on logout
await _tokenRepository.revokeRefreshToken(userId, refreshToken);
}
}
aspirate#:
public async Task> Login(
LoginRequest request,
string ipAddress)
{
var isAllowed = await _rateLimiter.CheckLimit(
key: $"login:{ipAddress}",
maxAttempts: 5,
windowSeconds: 300);
if (!isAllowed)
return Result.Failure("Too many login attempts.");
var user = await _userService.ValidateCredentials(
request.Email,
request.Password);
if (user == null)
return Result.Failure("Invalid credentials");
var accessToken = _tokenService.GenerateAccessToken(user, expiryMinutes: 15);
var refreshToken = _tokenService.GenerateRefreshToken(user);
await _tokenRepository.StoreRefreshToken(user.Id, refreshToken);
return Result.Success(new AuthTokens(accessToken, refreshToken));
}
public async Task Logout(string userId, string refreshToken)
{
await _tokenRepository.RevokeRefreshToken(userId, refreshToken);
}
3. BOPLA (بروکن آبجیکٹ پراپرٹی لیول کی تصدیق)
یہ کیا ہے
API صارف کی ضرورت سے زیادہ ڈیٹا واپس کرتا ہے۔ اندرونی جھنڈے، منتظم کی اجازتیں، اور حساس فیلڈز جو سرور کو کبھی نہیں چھوڑنا چاہیے جوابی پے لوڈ میں شامل ہیں۔ یا اس سے بھی بدتر: API صارف کی سیٹ سے زیادہ ڈیٹا کو قبول کرتا ہے، جس سے صارف کو ان فیلڈز میں ترمیم کرنے کی اجازت ملتی ہے جنہیں انہیں کبھی ہاتھ نہیں لگانا چاہیے۔
جب کوئی صارف اپنا پروفائل درآمد کرتا ہے تو جواب میں شامل ہوتا ہے: isAdmin: false، internalAccountScore: 742یا fraudRiskLevel: "low". ان میں سے کوئی بھی صارف کے جواب میں موجود نہیں ہونا چاہیے۔
یا، جب صارف اپنے پروفائل کو اپ ڈیٹ کرتا ہے، تو درخواست کے باڈی میں شامل ہیں: "role": "admin". API آپ کے لیے اسے ہینڈل کرتا ہے۔ ایک صارف نے ابھی خود کو ایڈمنسٹریٹر کے طور پر ترقی دی ہے۔
انجینئرنگ کی ناکامی
یہ ایک ساختی مسئلہ ہے۔ ایسا اس وقت ہوتا ہے جب آپ جان بوجھ کر ڈیزائن کردہ رسپانس اسکیما کے بغیر سسٹم بناتے ہیں۔ کچھ ڈویلپرز خام ڈیٹا بیس اداروں کو براہ راست واپس کرتے ہیں کیونکہ یہ آسان ہے۔ کچھ کے پاس ڈیٹا لیئر کو صحیح طریقے سے ترتیب دینے کے لیے ڈومین کی معلومات کی کمی ہے۔ نتیجے کے طور پر، اندرونی ڈیٹا خارجی ردعمل میں لیک ہو جاتا ہے۔
یہ انٹرفیس علیحدگی کے اصول کی بھی خلاف ورزی ہے۔ رسپانس انٹرفیس صارفین کو ڈیٹا حاصل کرنے پر مجبور کرتا ہے جس تک انہیں کبھی بھی رسائی حاصل نہیں ہوگی۔
انجینئرنگ ٹھیک کریں
ہر API اینڈ پوائنٹ کا ایک واضح، جان بوجھ کر ڈیزائن کیا گیا جواب DTO ہونا چاہیے۔ جواب ڈی ٹی او میں صرف وہ فیلڈز ہوتے ہیں جو کال کرنے والے کو وصول کرنے کی اجازت ہوتی ہے۔ ڈومین ادارے براہ راست جواب میں نہیں جاتے ہیں۔
ڈارٹ:
//Wrong returning the domain entity directly - exposes internal fields
Future getProfile(Request request) async {
final user = await _userRepository.findById(userId);
return Response.ok(jsonEncode(user.toJson())); // exposes everything
}
// Correct : explicit response DTO — only what the caller should see
class UserProfileResponse {
final String id;
final String firstName;
final String lastName;
final String email;
const UserProfileResponse({
required this.id,
required this.firstName,
required this.lastName,
required this.email,
});
Map toJson() => {
'id': id,
'first_name': firstName,
'last_name': lastName,
'email': email,
// isAdmin, internalScore, fraudRiskLevel — never here
};
}
Future getProfile(Request request) async {
final user = await _userRepository.findById(userId);
final response = UserProfileResponse(
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
);
return Response.ok(jsonEncode(response.toJson()));
}
aspirate#:
// Wrong = returning the domain model directly
public async Task GetProfile(string userId)
{
var user = await _repository.FindByIdAsync(userId);
return Ok(user);
}
// Correct explicit response DTO
public record UserProfileResponse(
string Id,
string FirstName,
string LastName,
string Email);
public async Task GetProfile(string userId)
{
var user = await _repository.FindByIdAsync(userId);
var response = new UserProfileResponse(
user.Id,
user.FirstName,
user.LastName,
user.Email
// IsAdmin, InternalScore, FraudRiskLevel — never exposed
);
return Ok(response);
}
آنے والی درخواستوں کے لیے، ایک درخواست DTO استعمال کریں جو صرف ان فیلڈز کی اجازت دیتی ہے جن میں صارف کو ترمیم کرنے کی اجازت ہے۔ اپنے اپ ڈیٹ کی کارروائیوں میں براہ راست ڈومین اداروں سے منسلک نہ ہوں۔
4. لامحدود وسائل استعمال کریں۔
یہ کیا ہے
رفتار کی کوئی حد نہیں ہے۔ کال کرنے والے حساس APIs کو ہر منٹ میں ہزاروں بار سکریپنگ، بروٹ فورس، سروس سے انکار، وغیرہ کے ذریعے اسپام کر سکتے ہیں۔ API تمام درخواستوں کو تھروٹلنگ یا استعمال کی حد کے بغیر قبول کرتا ہے۔
انجینئرنگ کی ناکامی
رفتار کی حد یا تو اختیاری ہے یا مستقبل کا مسئلہ سمجھا جاتا ہے۔ یہ اس وقت تک ملتوی ہو جاتا ہے جب تک کہ "جب ہمیں توسیع کی ضرورت ہو” یا "جب ہمیں بدسلوکی کا پتہ چلتا ہے۔” جب تک زیادتی نظر آتی ہے، نقصان ہو چکا ہوتا ہے۔ شرح کی حد کے بغیر، مالیاتی API درست اکاؤنٹ نمبر تلاش کرنے، قیمتوں کے ڈیٹا کو کھرچنے، یا محض مغلوب اور ناقابل استعمال ہونے کے لیے زبردست حملہ کر سکتے ہیں۔
انجینئرنگ ٹھیک کریں
درخواست کے سروس لیئر تک پہنچنے سے پہلے API گیٹ وے پرت پر شرح کی حد بندی کو لاگو کیا جانا چاہیے۔ یہ سروس کی ذمہ داری نہیں ہے۔ گیٹ وے ایک درست نفاذ کا نقطہ ہے کیونکہ یہ تمام خدمات کے لیے بیک وقت اسے سنبھال سکتا ہے بغیر ہر سروس کو اسے دوبارہ نافذ کرنے کی ضرورت ہے۔
مختلف اختتامی مقامات کو مختلف حدود کی ضرورت ہوتی ہے۔ توثیق کے اختتامی مقامات کے لیے سخت حدود کی ضرورت ہوتی ہے (فی آئی پی میں 5 منٹ میں 5 کوششیں)۔ پبلک ریڈ پوائنٹس کے لیے معقول پابندیوں کی ضرورت ہوتی ہے۔ حساس وسائل پر کارروائیوں کو لکھنے کے لیے سخت حدود کی ضرورت ہوتی ہے۔
ڈارٹ (مڈل ویئر اپروچ):
class RateLimitMiddleware {
final RateLimiter _limiter;
RateLimitMiddleware(this._limiter);
Handler call(Handler innerHandler) {
return (Request request) async {
final clientIp = request.headers['x-forwarded-for'] ?? 'unknown';
final endpoint = request.url.path;
final limit = _getLimitForEndpoint(endpoint);
final isAllowed = await _limiter.checkLimit(
key: '$clientIp:$endpoint',
maxRequests: limit.maxRequests,
windowSeconds: limit.windowSeconds,
);
if (!isAllowed) {
return Response(
429,
body: jsonEncode({'error': 'Rate limit exceeded'}),
headers: {'Retry-After': '60'},
);
}
return innerHandler(request);
};
}
RateLimit _getLimitForEndpoint(String path) {
if (path.contains('/auth/login')) {
return RateLimit(maxRequests: 5, windowSeconds: 300);
}
if (path.contains('/transactions')) {
return RateLimit(maxRequests: 100, windowSeconds: 60);
}
return RateLimit(maxRequests: 1000, windowSeconds: 60);
}
}
aspirate#:
// using AspNetCoreRateLimit
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("auth", limiterOptions =>
{
limiterOptions.PermitLimit = 5;
limiterOptions.Window = TimeSpan.FromMinutes(5);
limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
limiterOptions.QueueLimit = 0;
});
options.AddFixedWindowLimiter("standard", limiterOptions =>
{
limiterOptions.PermitLimit = 100;
limiterOptions.Window = TimeSpan.FromMinutes(1);
});
options.RejectionStatusCode = 429;
});
// apply per controller
[EnableRateLimiting("auth")]
[HttpPost("login")]
public async Task Login(LoginRequest request) { }
[EnableRateLimiting("standard")]
[HttpGet("transactions")]
public async Task GetTransactions() { }
5. ٹوٹے ہوئے فنکشنل لیول کی توثیق (BFLA)
یہ کیا ہے
باقاعدہ صارفین ان خصوصیات یا اختتامی مقامات تک رسائی حاصل کر سکتے ہیں جن تک صرف منتظمین یا مراعات یافتہ کردار ہی رسائی حاصل کر سکتے ہیں۔ فرنٹ اینڈ بٹن کو چھپاتا ہے، لیکن اختتامی نقطہ اب بھی کھلا ہے۔
باقاعدہ صارف ٹوکن کا استعمال مینجمنٹ اینڈ پوائنٹ کو کال کرنے کے لیے کیا جاتا ہے۔ یہ کام کرتا ہے۔ حملہ آور کے پاس اب نان ایڈمنسٹریٹر اکاؤنٹ کے ساتھ ایڈمنسٹریٹر کی صلاحیتیں ہیں۔
مبہمیت کے ذریعے سلامتی سلامتی نہیں ہے۔ UI سطح پر منظم وسائل کو چھپانا سیکیورٹی جیسا نہیں ہے۔
انجینئرنگ کی ناکامی
رول پر مبنی رسائی کنٹرول (RBAC) کو غلط طریقے سے نافذ یا لاگو نہیں کیا گیا ہے۔ فنکشنل سطح پر اجازت کے چیک غائب ہیں۔ اگر صارف کو UI میں ایڈمن بٹن نظر نہیں آتا ہے، تو میں فرض کرتا ہوں کہ وہ Admin API کو کال نہیں کر سکتے۔ یہ قیاس ہمیشہ غلط ہوتا ہے۔ کوئی بھی ڈویلپر ٹول UI کو مکمل طور پر نظرانداز کرتے ہوئے براہ راست API اینڈ پوائنٹ کو کال کر سکتا ہے۔
انجینئرنگ ٹھیک کریں
ہر فنکشن کو عمل درآمد سے پہلے کال کرنے والے کے کردار کی جانچ کرنی چاہیے۔ یہ چیک سروس لیئر پر کیا جانا چاہئے نہ کہ صرف UI پرت یا API گیٹ وے پر۔ گیٹ وے تصدیق کر سکتا ہے کہ ٹوکن درست ہے۔ صرف سروس پرت ہی جانتی ہے کہ ہر مخصوص کام کے لیے کون سے کردار کی ضرورت ہے۔
ڈارٹ:
class UserManagementService {
final AuthContext _authContext;
final UserRepository _repository;
UserManagementService(this._authContext, this._repository);
Future> deleteUser(String targetUserId) async {
final currentUser = _authContext.currentUser;
// role check - this must happen in the service layer
if (!currentUser.hasRole(UserRole.admin)) {
return Result.failure(
AppException.forbidden('Admin role required for this operation'),
);
}
await _repository.deleteUser(targetUserId);
return Result.success(null);
}
Future, AppException>> getAllUsers() async {
final currentUser = _authContext.currentUser;
if (!currentUser.hasRole(UserRole.admin)) {
return Result.failure(
AppException.forbidden('Admin role required'),
);
}
final users = await _repository.findAll();
return Result.success(users);
}
}
aspirate#:
[ApiController]
[Route("api/admin/users")]
[Authorize]
public class UserManagementController : ControllerBase
{
private readonly IUserManagementService _service;
[HttpDelete("{userId}")]
[Authorize(Roles = "Admin")]
public async Task DeleteUser(string userId)
{
await _service.DeleteUser(userId);
return NoContent();
}
[HttpGet]
[Authorize(Roles = "Admin")]
public async Task GetAllUsers()
{
var users = await _service.GetAllUsers();
return Ok(users);
}
}
C# میں پراپرٹی لیول پر رول چیکنگ اور ڈارٹ میں سروس لیئر پر واضح رول کی توثیق دونوں ایک ہی نتیجہ حاصل کرتے ہیں۔ اس کا مطلب ہے کہ چیک کوڈ میں ہوتا ہے، UI میں نہیں، اور API صارف کے رویے میں نہیں۔
6. حساس کاروباری بہاؤ تک غیر محدود رسائی
یہ کیا ہے
بنیادی کاروباری منطق جس کے لیے سخت گارڈریلز کی ضرورت ہوتی ہے اس میں گارڈریلز نہیں ہوتے ہیں۔ صارفین کاروباری قوانین کو نظرانداز کر سکتے ہیں اور بہاؤ کو متحرک کر سکتے ہیں جن کی ان کی صورتحال میں کبھی اجازت نہیں ہونی چاہیے۔
سیلز ایجنٹ کوریج ایریا سے باہر کسی گاہک کے لیے سیل تیار کرتا ہے۔ سیل پیدا کرنے والے اختتامی نقطہ نے تصدیق نہیں کی ہے کہ کوریج موجود ہے۔ کاروباری اصول کی خلاف ورزی کی گئی ہے۔ مالی لحاظ سے نقصانات ہیں۔ عملی طور پر، اس سے مسائل پیدا ہوتے ہیں جنہیں حل ہونے میں مہینوں لگ سکتے ہیں۔
انجینئرنگ کی ناکامی
کاروباری منطق کی شناخت نہیں کی گئی ہے اور ڈومین پرت پر اسے نافذ نہیں کیا گیا ہے۔ ڈومین سے چلنے والا ڈیزائن اسی وجہ سے موجود ہے۔ کاروباری منطق ٹوٹ جاتی ہے یا ایپلیکیشن بناتی ہے۔ اگر ڈومین کی پرت خراب ڈیزائن کی گئی ہے اور کاروباری قواعد واضح طور پر انکوڈ نہیں کیے گئے ہیں، تو صارف انہیں نظرانداز کر سکتے ہیں۔
وہاں کوئی گارڈریل نہیں ہے کیونکہ انجینئرز کو معلوم نہیں تھا کہ وہ وہاں موجود ہیں۔ یہ ایک تکنیکی مسئلہ اور ڈومین کے علم کا مسئلہ دونوں ہے۔
انجینئرنگ ٹھیک کریں
کاروباری قوانین کا تعلق ڈومین پرت سے ہے۔ یہ وہی ہے جو قدر آبجیکٹ اور ڈومین اداروں کا اطلاق ہوتا ہے۔ اگر کوریج کی تصدیق نہیں کی جاتی ہے تو، فروخت نہیں کی جا سکتی. ان اصولوں کو ڈومینز کے ساتھ انکوڈ کیا گیا ہے، نہ کہ API اینڈ پوائنٹس یا UI۔
ڈارٹ:
class Sale {
final String agentId;
final String customerId;
final CoverageArea coverageArea;
final SaleStatus status;
Sale._({
required this.agentId,
required this.customerId,
required this.coverageArea,
required this.status,
});
// business rule enforced at domain creation - no coverage, no sale
static Result create({
required String agentId,
required String customerId,
required CoverageArea coverageArea,
}) {
if (!coverageArea.hasActiveCoverage) {
return Result.failure(
DomainException('Cannot create sale: customer area has no active coverage'),
);
}
return Result.success(Sale._(
agentId: agentId,
customerId: customerId,
coverageArea: coverageArea,
status: SaleStatus.pending,
));
}
}
aspirate#:
public class Sale
{
private Sale(string agentId, string customerId, CoverageArea area)
{
AgentId = agentId;
CustomerId = customerId;
CoverageArea = area;
Status = SaleStatus.Pending;
}
public string AgentId { get; }
public string CustomerId { get; }
public CoverageArea CoverageArea { get; }
public SaleStatus Status { get; private set; }
// factory enforces the business rule no coverage, no sale
public static Result Create(
string agentId,
string customerId,
CoverageArea coverageArea)
{
if (!coverageArea.HasActiveCoverage)
return Result.Failure(
"Cannot create sale: no active coverage in this area");
return Result.Success(new Sale(agentId, customerId, coverageArea));
}
}
اختتامی نقطہ جو فروخت پیدا کرتا ہے ڈومین فیکٹری کو کال کرتا ہے۔ ڈومین فیکٹریاں کاروباری قوانین نافذ کرتی ہیں۔ قواعد کو API کے ذریعے نظرانداز نہیں کیا جا سکتا کیونکہ APIs فیکٹری کو نظرانداز کرنے والی سیلز پیدا نہیں کر سکتے۔
7. غلط سیکورٹی کنفیگریشن
یہ کیا ہے
ڈویلپر کے ذاتی ڈیبگنگ کے عمل کو پروڈکشن میں لے جایا جاتا ہے۔ اسٹیک ٹریس کو ظاہر کرنے والا ایک تفصیلی خرابی کا پیغام۔ ڈیبگ اینڈ پوائنٹ فعال رہتا ہے۔ CORS کو کسی بھی اصل کی اجازت دینے کی اجازت دیں۔ نوشتہ جات میں حساس ڈیٹا۔ یہ ڈویلپر کی مقامی ترتیب ہے جو پیداوار میں چل رہی ہے۔
انجینئرنگ کی ناکامی
اس قسم کے مسائل اس وقت پیش آتے ہیں جب ترقی، اسٹیجنگ اور پروڈکشن کنفیگریشنز کے درمیان کوئی فرق نہیں ہوتا ہے۔ پیداوار تک پہنچنے سے پہلے غلط کنفیگریشنز کو پکڑنے کے لیے کوئی پائپ لائن گیٹ نہیں ہیں۔ ہر ڈویلپر اپنی ترتیب کا خود انتظام کرتا ہے۔ اس کا مطلب یہ ہے کہ ہر ڈویلپر کی عادات اور ڈیبگنگ کی ترجیحات کو بالآخر پیداوار پر لاگو کیا جا سکتا ہے۔
انجینئرنگ ٹھیک کریں
ہر ماحول کے لیے مختلف ورک فلو ترتیب دیں۔ پروڈکشن پائپ لائن میں جامد تجزیہ، کنفیگریشن کی توثیق، ڈیبگ اینڈ پوائنٹس کو کیپچر کرنے کے لیے لنٹنگ، CORS کی اجازت، تفصیلی غلطی لاگنگ، اور انضمام کی اجازت سے پہلے بے نقاب راز شامل ہونا چاہیے۔
ڈارٹ (ماحول سے آگاہی کی خرابی سے نمٹنے):
class ErrorHandler {
final Environment _environment;
ErrorHandler(this._environment);
Response handleException(Object error, StackTrace stackTrace) {
logger.error('Unhandled exception', error: error, stackTrace: stackTrace);
if (_environment.isProduction) {
return Response.internalServerError(
body: jsonEncode({'error': 'An internal error occurred'}),
);
}
return Response.internalServerError(
body: jsonEncode({
'error': error.toString(),
'stackTrace': stackTrace.toString(),
}),
);
}
}
aspirate#:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
var error = context.Features.Get();
if (error != null)
{
// log internally with full details
logger.LogError(error.Error, "Unhandled exception");
}
// return generic message to caller
await context.Response.WriteAsync(
JsonSerializer.Serialize(new { error = "An internal error occurred" })
);
});
});
// CORS - explicit allowed origins, never wildcard in production
builder.Services.AddCors(options =>
{
options.AddPolicy("ProductionPolicy", policy =>
{
policy.WithOrigins(
"https://app.yourproduct.com",
"https://admin.yourproduct.com"
)
.AllowedMethods("GET", "POST", "PUT", "DELETE")
.AllowedHeaders("Authorization", "Content-Type");
});
});
پیداوار میں CORS کو واضح طور پر اجازت شدہ ذرائع کا استعمال کرنا چاہیے۔ پروڈکشن میں وائلڈ کارڈ CORS ایک براہ راست حفاظتی خامی ہے جو انٹرنیٹ پر کسی بھی ویب پیج کو کسی API سے تصدیق شدہ درخواست کرنے کے لیے وزیٹر کی اسناد استعمال کرنے کی اجازت دیتی ہے۔
8. انوینٹری کا غلط انتظام
یہ کیا ہے
فرسودہ APIs، فرسودہ اختتامی پوائنٹس، اور پرانے API ورژن اب بھی آپ کے سرورز پر چل رہے ہیں۔ کوئی نہیں جانتا کہ یہ موجود ہے اور نہ کوئی اس کا مالک ہے۔ تاہم، میراثی اینڈ پوائنٹس اکثر موجودہ اینڈ پوائنٹس کے مقابلے میں کم محفوظ ہوتے ہیں، جنہیں حملہ آور تلاش کرتے اور استحصال کرتے ہیں۔
انجینئرنگ کی ناکامی
API لائف سائیکل مینجمنٹ کسی کی رسمی ذمہ داری نہیں ہے۔ ایک اختتامی نقطہ بنایا جاتا ہے اور اس کی فعالیت کو تبدیل کیا جاتا ہے۔ پرانے اختتامی ورژن ریٹائر ہونے کے بجائے بھول جاتے ہیں۔ وقت گزرنے کے ساتھ، API کی سطح کا رقبہ ڈیڈ اینڈ پوائنٹس کے ساتھ بڑھتا ہے جو اب بھی درخواستوں کا جواب دیتے ہیں، اور اکثر جدید اینڈ پوائنٹس پر کوئی سیکیورٹی کنٹرول لاگو نہیں ہوتا ہے۔
انجینئرنگ ٹھیک کریں
کسی کو API انوینٹری کے مالک ہونے کی ضرورت ہے۔ یہ انجینئرنگ کی ایک رسمی ذمہ داری ہے اور اختیاری نہیں۔ تمام API اینڈ پوائنٹس کو دستاویزی، ورژن، اور ایک متعین لائف سائیکل ہونا چاہیے: فعال، فرسودہ، یا ریٹائرڈ۔
فرسودہ اختتامی نقطہ کو ایک جوابی ہیڈر واپس کرنا چاہیے جو اس تاریخ کی نشاندہی کرے جب اسے فرسودہ کیا گیا تھا۔ ختم شدہ اینڈ پوائنٹ کو درخواستوں پر کارروائی جاری نہیں رکھنی چاہیے اور 410 Gone کو واپس کرنا چاہیے۔
ڈارٹ:
// deprecated endpoint wrapper
Handler deprecatedEndpoint({
required Handler handler,
required DateTime removalDate,
required String replacementEndpoint,
}) {
return (Request request) async {
final response = await handler(request);
// add deprecation headers so clients know to migrate
return response.change(headers: {
'Deprecation': 'true',
'Sunset': HttpDate.format(removalDate),
'Link': '<$replacementEndpoint>; rel="successor-version"',
'Warning': '299 - "This endpoint is deprecated and will be removed on ${removalDate.toIso8601String()}"',
});
};
}
// decommissioned endpoint
Future decommissionedEndpoint(Request request) async {
return Response(
410,
body: jsonEncode({
'error': 'This endpoint has been permanently removed',
'replacement': '/api/v2/accounts',
}),
);
}
aspirate#:
// mark endpoints as deprecated using ApiVersion attributes
[ApiController]
[ApiVersion("1.0", Deprecated = true)]
[Route("api/v{version:apiVersion}/accounts")]
public class AccountsV1Controller : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetAccount(string id)
{
Response.Headers.Add("Deprecation", "true");
Response.Headers.Add("Sunset", "Sat, 01 Jan 2027 00:00:00 GMT");
Response.Headers.Add("Link", "; rel=\"successor-version\"");
// still process the request during deprecation period
return Ok(_service.GetAccount(id));
}
}
// gone - permanent removal
[ApiController]
[Route("api/v1/legacy/accounts")]
public class LegacyAccountsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll()
{
return StatusCode(410, new { error = "This endpoint has been permanently removed", replacement = "/api/v2/accounts" });
}
}
9. APIs کا غیر محفوظ استعمال
یہ کیا ہے
آپ کی ایپلیکیشن فریق ثالث کی خدمات کے ساتھ ضم ہوجاتی ہے اور بغیر تصدیق یا چوکیوں کے ان کے ڈیٹا پر بھروسہ کرتی ہے۔ جو بھی بیرونی API واپس آتا ہے، آپ کی ایپلیکیشن اسے براہ راست ہینڈل کرتی ہے۔
فریق ثالث کی ادائیگی کا کال بیک لین دین کی حیثیت لوٹاتا ہے۔ آپ کی درخواست دستخط کی تصدیق کیے بغیر یا ڈیٹا کے ڈھانچے کی تصدیق کیے بغیر اس پر بھروسہ کرتی ہے۔ حملہ آور کال بیک یو آر ایل پر جعلی کال بیک بھیجتا ہے۔ آپ کی درخواست اسے جائز سمجھے گی۔
انجینئرنگ کی ناکامی
بیرونی خدمات کو بطور ڈیفالٹ قابل اعتماد خدمات سمجھا جاتا ہے۔ بیرونی خدمات کا درست ڈیٹا کیسا لگتا ہے اس کے لیے کوئی متعین معاہدہ نہیں ہے۔ بیرونی سروس کے جواب اور آپ کی درخواست کی پروسیسنگ منطق کے درمیان کوئی توثیق کی پرت نہیں ہے۔
اعتماد ثابت کرنے کے بجائے فرض کیا جاتا ہے۔
انجینئرنگ ٹھیک کریں
بیرونی خدمات کے ساتھ تمام انضمام کا ایک متعین معاہدہ ہونا ضروری ہے۔ معاہدہ بتاتا ہے کہ درست ڈیٹا کیسا ہونا چاہیے، کون سے فیلڈز درکار ہیں، کون سی رینجز درست ہیں، اور کون سا فارمیٹ۔ پروسیسنگ ہونے سے پہلے بیرونی خدمات کے تمام جوابات اس معاہدے کے خلاف تصدیق شدہ ہیں۔
ڈارٹ:
class PaymentCallbackService {
final String _webhookSecret;
PaymentCallbackService(this._webhookSecret);
Future> processCallback(
Map payload,
String signature,
String rawBody,
) async {
// step 1: verify the signature before processing anything
final isValid = _verifySignature(rawBody, signature, _webhookSecret);
if (!isValid) {
logger.warning('Invalid webhook signature received');
return Result.failure(
AppException.unauthorized('Invalid webhook signature'),
);
}
// step 2: validate the payload structure against our contract
final validationResult = _validateCallbackPayload(payload);
if (validationResult.isFailure) {
logger.warning('Invalid callback payload: ${validationResult.error}');
return Result.failure(validationResult.error!);
}
// step 3: only now do we trust and process the data
final status = PaymentStatus.fromString(payload['status'] as String);
return Result.success(status);
}
bool _verifySignature(String body, String signature, String secret) {
final hmac = Hmac(sha256, utf8.encode(secret));
final digest = hmac.convert(utf8.encode(body));
final expectedSignature="sha256=${base64.encode(digest.bytes)}";
return expectedSignature == signature;
}
Result _validateCallbackPayload(
Map payload,
) {
if (!payload.containsKey('transaction_id')) {
return Result.failure(
AppException.validation('Missing required field: transaction_id'),
);
}
if (!payload.containsKey('status')) {
return Result.failure(
AppException.validation('Missing required field: status'),
);
}
final validStatuses = {'success', 'failed', 'pending'};
if (!validStatuses.contains(payload['status'])) {
return Result.failure(
AppException.validation('Invalid status value: ${payload['status']}'),
);
}
return Result.success(null);
}
}
aspirate#:
public class PaymentCallbackService
{
private readonly string _webhookSecret;
private readonly ILogger _logger;
public async Task> ProcessCallback(
string rawBody,
string signature,
PaymentCallbackDto payload)
{
// verify signature first
if (!VerifySignature(rawBody, signature))
{
_logger.LogWarning("Invalid webhook signature received");
return Result.Failure("Invalid webhook signature");
}
// validate payload
if (string.IsNullOrEmpty(payload.TransactionId))
return Result.Failure("Missing transaction_id");
var validStatuses = new[] { "success", "failed", "pending" };
if (!validStatuses.Contains(payload.Status))
return Result.Failure($"Invalid status: {payload.Status}");
return Result.Success(Enum.Parse(payload.Status, true));
}
private bool VerifySignature(string body, string signature)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_webhookSecret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(body));
var expectedSignature = $"sha256={Convert.ToBase64String(hash)}";
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expectedSignature),
Encoding.UTF8.GetBytes(signature)
);
}
}
انضمام شروع ہونے سے پہلے بیرونی خدمات کے ساتھ توثیق شدہ معاہدے بنانا انجینئرنگ کا معیار بن جانا چاہیے۔ کبھی بھروسہ نہ کریں، ہمیشہ چیک کریں۔
10. سرور سائیڈ درخواست جعلسازی (SSRF)
یہ کیا ہے
API ایک URL کو بطور ان پٹ قبول کرتا ہے اور اس URL کے لیے سرور کی طرف سے درخواست بھیجتا ہے۔ حملہ آور ایک URL فراہم کرتا ہے جو اندرونی انفراسٹرکچر کی طرف اشارہ کرتا ہے۔ http://169.254.169.254/latest/meta-data/ (AWS میٹا ڈیٹا سروس) http://internal-database:5432یا http://admin-panel.internal. سرور بیرونی فائر وال کو نظرانداز کرتے ہوئے نیٹ ورک کے اندر سے درخواستیں کرتا ہے۔
زیادہ تر ادائیگی کے نظام اپنی درخواستوں میں کال بیک URL یا ری ڈائریکٹ URL استعمال کرتے ہیں۔ اگر آپ کا API بغیر تصدیق کے اس URL کو ایک درخواست بھیجتا ہے، تو یہ حملہ آور کی جانب سے خوشی سے آپ کے اندرونی انفراسٹرکچر کو درخواست بھیج دے گا۔
انجینئرنگ کی ناکامی
یہ ایک ڈیزائن کی خامی ہے جسے آرکیٹیکچر پرت پر پکڑا جانا چاہئے، کوڈ پرت پر نہیں۔ کوئی بھی API جو کسی URL کو بطور ان پٹ قبول کرتا ہے اور اس URL کے لیے سرور کی طرف سے درخواست کرتا ہے ایک ممکنہ SSRF ہدف ہے۔ صوابدیدی URLs کی اجازت دینے کے ڈیزائن کے فیصلے کو ان URLs کی سختی سے توثیق کرنے کے ڈیزائن کے فیصلے کے ساتھ ملنا چاہیے۔
انجینئرنگ ٹھیک کریں
کوئی بھی API جو URL قبول کرتا ہے اسے درخواست کرنے سے پہلے وائٹ لسٹ شدہ ڈومینز کی فہرست کے خلاف URL کی توثیق کرنی چاہیے۔ یہ ایک ڈیزائن کا فیصلہ ہے۔ وائٹ لسٹنگ API تفصیلات کا حصہ ہے، سوچنے کے بعد نہیں۔
ڈارٹ:
class WebhookService {
// only these domains are allowed to receive callbacks
static const _allowedDomains = {
'api.yourpartner.com',
'hooks.yourintegration.com',
'callbacks.trustedservice.io',
};
Future> registerCallbackUrl(String url) async {
// validate before storing or using
final validationResult = _validateCallbackUrl(url);
if (validationResult.isFailure) {
return Result.failure(validationResult.error!);
}
await _webhookRepository.save(url);
return Result.success(null);
}
Result _validateCallbackUrl(String url) {
final uri = Uri.tryParse(url);
if (uri == null) {
return Result.failure(AppException.validation('Invalid URL format'));
}
// must be HTTPS in production
if (uri.scheme != 'https') {
return Result.failure(
AppException.validation('Callback URL must use HTTPS'),
);
}
// must be in the allowlist
if (!_allowedDomains.contains(uri.host)) {
return Result.failure(
AppException.validation(
'Callback URL domain is not in the approved list',
),
);
}
// block internal IP ranges explicitly
if (_isInternalAddress(uri.host)) {
return Result.failure(
AppException.validation('Callback URL cannot point to internal addresses'),
);
}
return Result.success(null);
}
bool _isInternalAddress(String host) {
final privateRanges = [
'127.', '10.', '172.16.', '172.17.', '172.18.',
'192.168.', '169.254.', 'localhost', '0.0.0.0',
];
return privateRanges.any((range) => host.startsWith(range));
}
}
aspirate#:
public class WebhookService
{
private static readonly HashSet AllowedDomains = new()
{
"api.yourpartner.com",
"hooks.yourintegration.com",
"callbacks.trustedservice.io"
};
public async Task> RegisterCallbackUrl(string url)
{
var validationResult = ValidateCallbackUrl(url);
if (!validationResult.IsSuccess)
return Result.Failure(validationResult.Error);
await _repository.SaveCallbackUrl(url);
return Result.Success(true);
}
private Result ValidateCallbackUrl(string url)
{
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
return Result.Failure("Invalid URL format");
if (uri.Scheme != "https")
return Result.Failure("Callback URL must use HTTPS");
if (!AllowedDomains.Contains(uri.Host))
return Result.Failure("Domain not in approved list");
if (IsInternalAddress(uri.Host))
return Result.Failure("Internal addresses are not permitted");
return Result.Success(true);
}
private bool IsInternalAddress(string host)
{
var internalPrefixes = new[]
{
"127.", "10.", "172.16.", "192.168.",
"169.254.", "localhost", "0.0.0.0"
};
return internalPrefixes.Any(p => host.StartsWith(p));
}
}
OWASP فہرست سے باہر انجینئرنگ کی کمزوریاں
OWASP فہرست میں سب سے اہم اور سب سے عام API کمزوریاں شامل ہیں۔ تاہم، انجینئرنگ کے تجربے نے اضافی نمونوں کا انکشاف کیا ہے جو سنگین حفاظتی سوراخ بنا سکتے ہیں۔
ڈومین وائٹ لسٹ کے بغیر متعدد کلائنٹس کے ذریعہ بلایا گیا API
کچھ APIs جن کو ایک سے زیادہ کلائنٹس کے ذریعہ کال کیا جاتا ہے وہ ڈومینز کو نافذ نہیں کرتے ہیں جہاں سے انہیں کال کرنے کی اجازت ہے۔ یہ لوگوں کو API کیز کا اشتراک کرنے اور غیر مجاز ذرائع سے وسائل تک رسائی کی اجازت دیتا ہے۔
بڑی تنظیموں میں، یہ ایک بڑا سیکورٹی ہول ہے جس پر اکثر کسی کا دھیان نہیں جاتا ہے کیونکہ API تکنیکی طور پر تمام کلائنٹس پر صحیح طریقے سے کام کرتا ہے۔
حل: کوئی بھی API جو API کیز کو قبول کرتا ہے اسے کالنگ ڈومین کی بھی تصدیق کرنی ہوگی۔ API کیز اور وائٹ لسٹڈ ڈومینز کو واضح طور پر جوڑا بنایا جانا چاہیے۔
کوڈ اور لاگز کے رازوں کو بے نقاب کرنا
ڈویلپرز اپنی خفیہ چابیاں، عوامی چابیاں، اور اسناد اپنے کوڈ، ریپوزٹری یا کمپیوٹر میں چھوڑ دیتے ہیں۔ فائل شامل کریں۔ .gitignore یہ کافی نہیں ہے۔ یہ ڈیٹا سیٹ رن ٹائم کے وقت کسی قابل اعتماد خفیہ مینیجر سے حاصل کیے جانے چاہئیں اور انہیں سورس کوڈ میں محفوظ نہیں کرنا چاہیے۔
آپ کی تنظیم کو سٹوریج، Azure ایپ کنفیگریشن، AWS Secrets Manager، یا مساوی نظام کا استعمال کرنا چاہیے۔ یہ انجینئرنگ کا معیار ہونا چاہیے، ڈویلپر کی ترجیح نہیں۔ والٹ تک رسائی کے نمونوں کو معیاری بنانے کی ضرورت ہے تاکہ ڈویلپرز کو پروجیکٹ بہ پروجیکٹ کی بنیاد پر ان کا پتہ لگانے کی ضرورت نہ ہو۔
مائیکرو سروسز گیٹ وے کے بغیر ایک دوسرے کے ساتھ بات چیت کرتی ہیں۔
مائیکرو سروسز بناتے وقت، سروسز کے درمیان تمام مواصلات کو حساس طریقے سے برتا جانا چاہیے۔ آپ کے پاس ایک API گیٹ وے ہونا چاہیے جو تمام سروسز کے لیے تصدیق کو ہینڈل کرے۔ خدمات کو واضح طور پر ایک دوسرے پر بھروسہ نہیں کرنا چاہیے۔ تمام سروس ٹو سروس کالز کی تصدیق ہونی چاہیے۔
مائیکرو سروسز کے درمیان مواصلت کے لیے ایونٹ بروکر کے استعمال کی ضرورت ہوتی ہے، جیسے کافکا غیر مطابقت پذیر بہاؤ کے لیے یا ایم ٹی ایل ایس ہم آہنگ بہاؤ کے لیے۔ مائیکرو سروسز کو گیٹ وے سے گزرے بغیر انٹرنیٹ سے براہ راست قابل رسائی نہیں ہونا چاہیے۔
تنظیمی معیارات کے بغیر خفیہ کاری
تنظیموں کے پاس معیاری خفیہ کاری الگورتھم ہونا ضروری ہے۔ یہ وہ الگورتھم نہیں ہے جسے ڈویلپر نے منتخب کیا ہے، یہ ہفتے کا الگورتھم نہیں ہے، اور یہ وہ نہیں ہے جو ڈویلپر نے Stack Overflow کے جواب میں پایا۔ اسٹاف کی سطح کے انجینئرنگ کے فیصلوں کو یہ بتانا چاہیے کہ کون سے الگورتھم، موڈز، اور کلیدی لمبائی تنظیمی معیارات ہیں۔
ایڈہاک خفیہ کاری کے انتخاب سے پیدا ہونے والے خطرات سنگین ہیں۔ ان میں پیڈنگ اوریکل اٹیک، کمزور کلید کی لمبائی، IV کا دوبارہ استعمال، اور انکرپشن ناکام ہونے پر اسٹیک ٹریس شامل ہیں جو کال کرنے والے کو اندرونی نفاذ کی تفصیلات ظاہر کرتے ہیں۔
انجینئرنگ کنٹرولز: انکرپشن کے لیے اندرونی طور پر بنائے گئے پیکجز یا کلاؤڈ فنکشنز کو بے نقاب کریں جنہیں پروجیکٹ لاگو کرنے کے بجائے درآمد کرتا ہے۔ ڈویلپرز پیکجز استعمال کرتے ہیں۔ ہم ہر پروجیکٹ کے لیے شروع سے خفیہ کاری نہیں لکھتے ہیں۔
ایس کیو ایل انجیکشن
ایس کیو ایل انجیکشن لگ بھگ 25 سال سے ہے۔ یہ 1998 سے موجود ہے۔ تاہم، یہ اب بھی ہوتا ہے کیونکہ انجینئرنگ کے معیارات نافذ نہیں ہوتے ہیں۔
ایسا اس لیے ہوتا ہے کیونکہ ڈویلپرز استفسارات لکھنے کے لیے سٹرنگ کنکٹنیشن کا استعمال کرتے ہیں، صارف کا ان پٹ لیتے ہیں اور اسے SQL سٹرنگ میں جوڑتے ہیں۔ ترمیم ہمیشہ پیرامیٹرائزڈ سوالات یا تیار بیانات ہوتے ہیں۔ خام SQL سٹرنگز کی اجازت نہیں ہے جس میں صارف کے ان پٹ کو مربوط کیا گیا ہے۔
ڈارٹ:
//Wrong string concatenation — SQL injection vulnerability
Future findUser(String email) async {
final query = "SELECT * FROM users WHERE email="$email"";
// attacker passes: admin@example.com' OR '1'='1
// query becomes: SELECT * FROM users WHERE email="admin@example.com" OR '1'='1'
// returns all users
return await database.rawQuery(query);
}
// parameterized query — injection proof
Future findUser(String email) async {
final results = await database.query(
'users',
where: 'email = ?',
whereArgs: [email],
);
return results.isNotEmpty ? User.fromMap(results.first) : null;
}
aspirate#:
// Wrong string concatenation
public async Task FindUser(string email)
{
var query = $"SELECT * FROM Users WHERE Email="{email}"";
return await _context.Users.FromSqlRaw(query).FirstOrDefaultAsync();
}
// Correct parameterized query using EF Core
public async Task FindUser(string email)
{
return await _context.Users
.Where(u => u.Email == email)
.FirstOrDefaultAsync();
}
// Correct parameterized raw SQL when needed
public async Task FindUserRaw(string email)
{
return await _context.Users
.FromSqlRaw("SELECT * FROM Users WHERE Email = {0}", email)
.FirstOrDefaultAsync();
}
یہ ایک بڑا خطرہ ہے جو انجینئرنگ کے بنیادی فیصلوں سے پیدا ہوتا ہے جو سخت حکمرانی اور تعمیل کے اصولوں کے تابع ہونا چاہیے۔ یہ اصول منصوبوں اور تنظیموں کے لیے ترقیاتی معیار قائم کرنے میں مدد کرتے ہیں۔
اسے CI CD کی سطح پر بھی لاگو کیا جا سکتا ہے۔ اگر آپ کی CI/CD پائپ لائن کا جامد تجزیہ خام SQL سٹرنگ کنکٹنیشن کو حاصل نہیں کرتا ہے، تو آپ کی ٹیم کو اسے شامل کرنے کی ضرورت ہوگی۔ یہ کمزوریوں کا ایک طبقہ ہے جو کبھی بھی پیداوار تک نہیں پہنچنا چاہیے۔
نتیجہ
اس فہرست میں موجود تمام خطرات میں ایک چیز مشترک ہے: روک تھام ممکن ہے۔ یہ حقیقت کے بعد لاگو سیکیورٹی ٹولز کے ذریعہ نہیں بلکہ ڈیزائن اور ترقی کے دوران لاگو انجینئرنگ کے مضامین سے حاصل ہوتا ہے۔
-
BOLA کو سروس لیئر پر شناخت سے آگاہ توثیق کے ذریعے روکا جاتا ہے۔
-
مناسب ٹوکن ڈیزائن اور شرح کو محدود کرنے سے سمجھوتہ شدہ تصدیق کو روکنے میں مدد مل سکتی ہے۔
-
BOPLA کو ایک واضح جواب DTO کے ساتھ روکا جاتا ہے۔
-
گیٹ وے پر ریٹ محدود کرنا وسائل کے غیر محدود استعمال کو روکتا ہے۔
-
BFLA کو کوڈ میں رول چیکنگ کے ذریعے روکا جاتا ہے، UI میں نہیں۔
-
حساس کاروباری بہاؤ کی نمائش کو ڈومین مرکوز ڈیزائن اور مناسب کاروباری اصول کے نفاذ کے ذریعے روکا جاتا ہے۔
-
حفاظتی غلط کنفیگریشنز کو خودکار گیٹس کے ساتھ ماحول کے لیے مخصوص پائپ لائنوں کے ذریعے روکا جاتا ہے۔
-
رسمی API لائف سائیکل ملکیت کے ذریعے انوینٹری کے غلط انتظام کو روکیں۔
-
معاہدے کے پہلے بیرونی انضمام کے ذریعے غیر محفوظ APIs استعمال کرنے سے گریز کریں۔
-
SSRF کو ڈیزائن کے فیصلے کے طور پر URL وائٹ لسٹنگ کے ذریعے روکا جاتا ہے۔
پیٹرن مسلسل ہے. سیکیورٹی ایسی چیز نہیں ہے جسے آپ اپنی درخواست میں شامل کرتے ہیں۔ یہ وہ چیز ہے جسے ہم زمین سے اپنے فن تعمیر میں انجینئر کرتے ہیں۔ اس دستاویز میں تمام فیصلے انجینئرنگ کے فیصلے ہیں۔ یہ چیک فن تعمیر میں کہاں سے تعلق رکھتا ہے؟ اس تصدیق کے لیے کون سی پرت ذمہ دار ہے؟ ڈومین کیا نافذ کرتا ہے؟ گیٹ وے کیا نافذ کرتا ہے؟ پائپ لائن کیا پکڑتی ہے؟
سیکیورٹی ڈیزائن سیکیورٹی ٹیم کی ذمہ داری نہیں ہے۔ یہ انجینئرنگ ٹیم کی ذمہ داری ہے۔ اور یہ اس بات کو سمجھنے کے ساتھ شروع ہوتا ہے کہ یہ کمزوریاں کیا ہیں، یہ کہاں سے آتی ہیں، اور ان کو روکنے کے لیے انجینئرنگ کے کون سے معیارات موجود ہیں۔
سافٹ ویئر انجینئرز کے طور پر، اس قسم کی اگلی سطح کی سوچ اس بات کو یقینی بناتی ہے کہ ہمارا کوڈ ان حفاظتی ٹیسٹوں میں کبھی ناکام نہیں ہوتا ہے۔
محفوظ کوڈنگ کا لطف اٹھائیں!