بازبینی چندمنظوره APIها از دیدگاه امنیت، یکپارچگی کلاینت و عملیات با GitHub Copilot
نویسنده: وحید نصیری
تاریخ: ۱۴۰۵/۰۵/۲۹ ۱۲:۵۲
آدرس: www.dntips.ir
این Endpoint در ASP.NET Core را به عنوان یک مهندس ارشد امنیت نرمافزار بازبینی کن.
چکلیست امنیتی:
۱. احراز هویت و سطوح دسترسی (AuthN & AuthZ): پیشفرضهای ضمنی و نقصهای Role/Policy-based.
۲. ریسکهای ارجاع مستقیم و ناامن به اشیاء (IDOR / BOLA): آیا کاربر لاگینشده میتواند با تغییر شناسه در URL به دادههای سازمان یا کاربران دیگر دسترسی پیدا کند؟
۳. ارسال بیشازحد داده (Over-Posting / Mass Assignment): آیا مدل ورودی شامل فیلدهای حساسی است که نباید توسط کلاینت ویرایش شوند (مثلIsAdminیاBalance)؟
۴. افشای دادههای حساس (Information Disclosure): آیا جزئیات ساختار پایگاه داده، خطاهای داخلی (Stack Trace) یا اطلاعات PII در پاسخ بازگردانده میشوند؟
۵. ثبت غیرایمن اطلاعات در لاگها (Log Injection & Secrets): آیا توکنها، رمزهای عبور یا دادههای هویتی در پیامهای لاگ ثبت میشوند؟
۶. محدودسازی نرخ درخواست (Rate Limiting) و جلوگیری از حملات DoS.
فعلاً کدی را تغییر نده؛ ابتدا ریسکها و شدت آنها را توضیح بده.
اکنون همین Endpoint را به عنوان یک توسعهدهنده فرانتاند یا تیم مصرفکننده API تحلیل کن:
ProblemDetails (RFC 7807) پیروی میکند تا کلاینت بتواند خطاها را به درستی پارس کند؟در نهایت، این متد را از دیدگاه یک مهندس عملیات و نگهداری سیستم (SRE / DevOps) ارزیابی کن:
[HttpPost("api/orders/{id}/update-status")]
public async Task<IActionResult> UpdateStatus(Guid id, [FromBody] Order order)
{
var existingOrder = await _db.Orders.FindAsync(id);
if (existingOrder == null)
return BadRequest("Order not found");
existingOrder.Status = order.Status;
existingOrder.DiscountPercent = order.DiscountPercent; // باگ Mass Assignment
await _db.SaveChangesAsync();
_logger.LogInformation($"Updated order {id} for user {existingOrder.UserId} with token {Request.Headers["Authorization"]}");
return Ok(existingOrder);
}existingOrder.UserId (آسیبپذیری بحرانی IDOR)، امکان تغییر فیلد حساس DiscountPercent توسط کلاینت (Mass Assignment)، و ثبت مستقیم توکن احراز هویت در متن لاگ (افشای اطلاعات حساس).400 Bad Request به جای 404 Not Found هنگامی که سفارش وجود ندارد، و بازگرداندن کل موجودیت داخلی به جای DTO.public sealed record UpdateOrderStatusRequest(OrderStatus Status);
public sealed record OrderStatusResponse(Guid Id, OrderStatus Status, DateTime UpdatedAtUtc);
[HttpPut("api/orders/{id:guid}/status")]
[Authorize]
[ProducesResponseType(typeof(OrderStatusResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status403Forbidden)]
public async Task<ActionResult<OrderStatusResponse>> UpdateStatus(
Guid id,
[FromBody] UpdateOrderStatusRequest request,
[FromServices] ICurrentUserService currentUser,
CancellationToken cancellationToken)
{
var order = await _db.Orders.FirstOrDefaultAsync(x => x.Id == id, cancellationToken);
if (order is null)
{
return NotFound(new ProblemDetails
{
Status = StatusCodes.Status404NotFound,
Title = "Order Not Found",
Detail = $"No order found with ID '{id}'."
});
}
// پیشگیری از IDOR: اعتبارسنجی مالکیت سفارش
if (order.UserId != currentUser.UserId && !currentUser.IsInRole("Admin"))
{
_logger.LogWarning("Unauthorized access attempt to Order {OrderId} by User {UserId}", id, currentUser.UserId);
return Forbid();
}
// بهروزرسانی امن تنها فیلد مجاز از طریق DTO
order.Status = request.Status;
order.UpdatedAtUtc = DateTime.UtcNow;
await _db.SaveChangesAsync(cancellationToken);
// ثبت لاگ ساختاریافته بدون افشای دادههای محرمانه
_logger.LogInformation("Order {OrderId} status updated to {OrderStatus} by User {UserId}",
order.Id, order.Status, currentUser.UserId);
return Ok(new OrderStatusResponse(order.Id, order.Status, order.UpdatedAtUtc));
}