Frameworkler, içerisindeki istek ve nesnelerin http parametrelerine otomatik olarak bağlanmasına imkan tanır. Bu imkan, yazılımcıların parametreleri ya da objeleri tek tek bağlaması yerine otomatikleştirdiği için kullanımı daha kolay hale getirmektedir.
Fakat uygulama içinde ki değişkenlerin ya da objelerin otomatik bağlanması, saldırganların bağlanan bu obje ve değişkenler yoluyla istenmeyen aktiviteler oluşturmasına da yol açabilmektedir. Örneğin, bir kullanıcı oluşturma fonksiyonunda kullanıcıya ait ad ve soyad verisiyle birlikte role tanımı da yapılmışsa ve bu tanım otomatik şekilde bağlanmışsa, role değişkeni kötü niyetli kişiler tarafından tespit edilerek manipüle edilebilir. Buradaki role değişkeni aracılığıyla oluşturulan kullanıcı yönetici yetkilerine sahip bir kullanıcı olabilir.
İlgili zafiyet Fortify SAST ürünüyle kaynak kod seviyesinde tespit edilebilmektedir. Kodun yazımı ve atak yüzeyine göre bulgular farklılaşmaktadır. Bu yazıda Mass Assignment: Request Parameters Bound via Input Formatter bulgusunu inceleyeceğiz.
İlgili bulgu aşağıda kod örneği verilen fonksiyona benzer bir fonksiyonun kod analizinde ortaya çıkmıştır. Eğer FromBody kullanımının zorunluluğu yoksa aşağıdaki kod satırı FromBody yerine [Bind(“istenen_deger,istenen_deger2”)] şeklinde eklenerek kullanılabilir.
public async Task<ActionResult<PrtclProcessResponse>> PrtclPrcss([Bind("First,Last")] PrtclProcessRequest request)
FromBody kullanımı zorunluysa, önceden oluşturulup farklı yerlerde kullanılmış ve değiştirilmesi zor bir durum varsa aşağıda örnek olarak eklenen proje incelenerek istenen özelliklerin işlenerek http body parametrelerine aktarımı sağlanır.
Asp.Net Core’da C# ile geliştirilen API projesinin bu fonksiyonunda ilgili asenkron bir metot aracılığıyla, FromBody niteliğiyle isteğin gövdesinden parametrenin okunmasıyla çağırılan PrtclPrcss kontrollerini görebiliriz. İlgili kontroller PrtclProcessRequest sınıfından türetilen request objesiyle bir takım işlemler yapıldıktan sonra PrtclProcessResponse sınıfının özellikleri aracılığıyla sonucu döndürmektedir. Aslında burada Fortify’ın da bulgu olarak gösterdiği FromBody niteliği ile PrtclProcessRequestDto sınıfındaki tüm parametreler kullanılabildiği için Mass Assignment zafiyetine sebebiyet verecek şekilde bu sınıfın içindeki tüm parametrelerin işlenmesini sağlamaktadır.
ProcessController.cs
namespace WebApplication4.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProcessController : ControllerBase
{
[HttpPost]
[Route("PrtclPrcss")]
public async Task<ActionResult<PrtclProcessResponse>> PrtclPrcss([FromBody] PrtclProcessRequest request){
//İşlem yapılacak veriler (request objesi üzerinden)
if (request == null)
return BadRequest("Request body is null");
// İşlemi simüle edelim (gerçek bir iş mantığına yerleştirilebilir)
var response = new PrtclProcessResponse
{
IsSuccess = true,
Message = "Process completed successfully",
ProcessedData = $"Processed: {request.ProcessId} - {request.Data}"
};
// Simülasyon amaçlı async bekletme
await Task.Delay(100);
return Ok(response);
}
}
}
Burada örnek olması açısından yazılan PrtclProcessRequest sınıfı da aşağıda görüldüğü gibi PrtclProcessRequestDto sınıfından miras alınarak türetilmiştir.
PrtclProcessRequest.cs
public class PrtclProcessRequest : PrtclProcessRequestDto, IRequest<PrtclProcessResponse>
{
public bool IsUrgent { get; set; } // Ekstra özellik
}
PrtclProcessRequestDto.cs
public class PrtclProcessRequestDto
{
public Guid ProcessId { get; set; }
public string Data { get; set; }
public DateTime Timestamp { get; set; }
}
PrtclProcessResponse.cs, Message özelliğinde text bir ifadeyle birlikte ProcessedData alanında ise request.ProcessId ve request.Data içinde olan verinin birleştirilerek sonuçların döndürüldüğü bir sınıftır.
PrtclProcessResponse.cs
public class PrtclProcessResponse
{
public bool IsSuccess { get; set; }
public string Message { get; set; }
public string ProcessedData { get; set; }
}
Visual Studio’da çalıştırılarak sonuçların işlenip döndürülmesiyle ilgili aşağıdaki ekran görüntüsünde görülen ProcessController.cs’deki gibi 32. satıra breakpoint eklenir ve uygulama çalıştırılır.
Swagger UI üzerinden (istenirse postman gibi araçlarlada yapılabilir) aşağıdaki gibi ilgili parametreler gönderilerek breakpoint noktasında yakalanır.
Request objesi üzerinde aşağıdaki gibi diğer alanların da işlenerek döndürüldüğü izlenir. Yazının devamında filtrelendikten sonra nasıl sonuç alınacağı da işlenecektir.
Yukarıdaki kod örneği FromBody kullanılmaya devam edilecek şekilde değiştirilecek olursa öncelikle FromBody ile http Post isteğinde bulunan PrtclProcessRequest nesnesi bind edilecek şekilde kullanmaya devam ederek, zafiyet manuel çözülmeye çalışılırsa öncelikle json verisinin işlenmesi için bir custom input formatter yazılır ve eğer bir sınıf tipinde filtrelenmesi isteniyorsa burada filtrelenir. Fakat buradan ilerlenemezse istek işlendikten sonra sonuç döndürülürken istenen alanların döndürülmesi sağlanır. İlgili kontroller, sınıflar ve açıklamaları aşağıda bulabilirsiniz.
ProcessController.cs
[Route("api/[controller]")]
[ApiController]
public class ProcessController : ControllerBase
{
[HttpPost]
[Route("PrtclPrcss")]
public async Task<ActionResult<PrtclProcessResponse>> PrtclPrcss([FromBody] PrtclProcessRequest request)
{
//public async Task<ActionResult<PrtclProcessResponse>> PrtclPrcss([Bind("First,Last")] PrtclProcessRequest request)
if (request == null)
return BadRequest("Request body is null");
var response = new PrtclProcessResponse
{
IsSuccess = true,
Message = "Process completed successfully",
ProcessedData = $"Processed: {request.processId} - {request.data}"
};
await Task.Delay(100);
return Ok(response);
}
CustomJsonInputFormatter.cs
public class CustomJsonInputFormatter : TextInputFormatter
{
public CustomJsonInputFormatter()
{
SupportedMediaTypes.Add("application/json");
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanReadType(Type type)
{
//Tip filtrelenmesi istenirse burada filtreleme yapılabilir fakat PrtclProcessRequest gibi
//eğer PrtclProcessRequestDto dan kalıtım alınarak yapılıyorsa burada çözülmeyecektir.
if (type == typeof(PrtclProcessRequest))
return true;
return false;
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
{
var request = context.HttpContext.Request;
using var reader = new StreamReader(request.Body, encoding);
var bodyContent = await reader.ReadToEndAsync();
try{
var result = JsonSerializer.Deserialize<PrtclProcessRequest>(bodyContent);
//Burada bütün veriler result içine ekleniyor
if (result.processId == Guid.Empty || string.IsNullOrEmpty(result.data))
//Burada herhangi bir veri işlenmesi yok sadece processId ve data boş mu değil mi ona bakılıyor
throw new Exception("Invalid or missing fields.");
// Sadece ProcessId ve Data döndür
var filteredResult = new PrtclProcessRequest
{//Burada result objesine eklenmesi istenmeyen özelliklerin filtrelendiği alandır.
//Sadece processId ve data nın eklenmesi ve işlenmesi sağlanıyor.
//Diğer ön yüzden gönderilen timeStamp ve isUrgent parametrelerinin işlenmediği
//breakpoint eklenerekte son adımda döndürülen filteredResulttada görülebilir
//controllerda ise burada işlenen değer processed data ve IsSuccess true şeklinde basılıyor.
processId = result.processId,
data = result.data
};
return await InputFormatterResult.SuccessAsync(filteredResult);
}
catch (Exception ex)
{
var logger = context.HttpContext.RequestServices.GetService<ILogger<CustomJsonInputFormatter>>();
logger.LogError(ex, "Input validation failed.");
context.ModelState.AddModelError("InputValidation", "The provided input is invalid.");
return await InputFormatterResult.FailureAsync();
}
}
Program.cs dosyası içerisinde aşağıda belirtildiği gibi CustomJsonInputFormatter eklenmesi gerekmektedir.
builder.Services.AddControllers(options =>
{
options.InputFormatters.Insert(0, new CustomJsonInputFormatter());
});
PrtclProcessRequest.cs
public class PrtclProcessRequest : PrtclProcessRequestDto
{
public bool isUrgent { get; set; } // Ekstra özellik
}
PrtclProcessRequestDto.cs
public class PrtclProcessRequestDto
{
public Guid processId { get; set; }
public string data { get; set; }
public DateTime timeStamp { get; set; }
}
PrtclProcessResponse.cs
public class PrtclProcessResponse
{
public bool IsSuccess { get; set; }
public string Message { get; set; }
public string ProcessedData { get; set; }
}
İlgili filtreleme işlemi Visual Studio ortamında bu kez CustomInputFormatter alanında breakpoint eklenerek çalıştırılır.
Aşağıdaki ekran görüntüsünde görüldüğü gibi isUrgent ve timeStamp özellikleri CustomInputFormetter.cs dosyasındaki filteredResult objesine eklenmediği için işlenmemektedir.
Mass Assignment zafiyeti katmanlı mimarilerde veri alışverişi yapılırken de filtrelenerek çözülebilir.
Faydalı olması dileğiyle, hoşçakalın.