Data Protection Components in NET Core

  • 2021-10-27 06:59:17
  • OfStack

Background introduction

In the report released by OWASP (Open Web Application Security Project) in 2013, insecure direct object references (Insecure Direct Object Reference) were marked as one of the top 10 Web application risks, which is manifested in the fact that object references (such as database primary keys) are exploited by various malicious attacks, so we need to encrypt various primary key foreign keys ID returned by Api.

Data Protection Components for NET Core

One IDataProtectionProvider interface and one IDataProtector interface are built into NET Core. IDataProtectionProvider is the interface for creating protection components, and IDataProtector is the interface for data protection. Developers can implement these two interfaces and create data protection components.
Built-in data protection components

A data protection component is provided by default in NET Core. Let's try to use this default component to protect our data.

Example: We currently have an Movie class with the following code, and we expect the Id field to be encrypted when we get the Movie object.


public class Movie
 {
  public Movie(int id, string title)
  {
   Id = id;
   Title = title;
  }

  public int Id { get; set; }
  public string Title { get; set; }
 }

First we need to configure the default data protection component in the ConfigureService method in Startup. cs.


public void ConfigureServices(IServiceCollection services)
 {
  services.AddMvc();
  services.AddDataProtection();
 }

This code enables the. NET Core default data protector.

We then create an MoviesController, inject an IDataProtectionProvider object into the constructor, and then use this Provider object to create a data protector object that implements the IDataProtector interface


[Route("movies")]
 public class MoviesController : Controller
 {
  private readonly IDataProtector protector;
 
  public MoviesController(IDataProtectionProvider provider)
  {
   this.protector = provider.CreateProtector("protect_my_query_string");
  }
 }

TIPS: When creating Protector with Provider, we passed in a parameter "protect_my_query_string", which indicates the purpose of this protector, or you can think of it as the name of this protector.

Note: Protectors for different purposes cannot decrypt each other's encrypted strings. If the protector A is used to decrypt the string generated by the protector B, the following exception CryptographicException is generated: The payload was invalid.

Then we add two Api to MovieController, one for all Movies objects and one for the specified Movie object


[HttpGet]
 public IActionResult Get()
 {
  var model = GetMovies();
  
  var outputModel = model.Select(item => new
  {
   Id = this.protector.Protect(item.Id.ToString()),
   item.Title,
   item.ReleaseYear,
   item.Summary
  });

  return Ok(outputModel);
 }

 [HttpGet("{id}")]
 public IActionResult Get(string id)
 {
  var orignalId = int.Parse(this.protector.Unprotect(id));

  var model = GetMovies(); 
  
  var outputModel = model.Where(item => item.Id == orignalId);

  return Ok(outputModel);
 }

Code interpretation

In the api that gets the Movie list, we encrypt the Id field using the Protect method of the IDataProtector interface Correspondingly, in the api to obtain a single Movie object, we need to decrypt the Id field using the Unprotect method of the IDataProtector interface.

Final effect

First we call the/api/movies, and the result is as follows. The id field has been encrypted correctly


[{
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6ygyO6avkgI2teCQGZQShNwsxC9ApDdsnyYd1K5IyNHjhZcRoGd6W31se3W6TWM8H9UdLEPn4fJpS5uKkqUa0PMV6a0ZZHBQSnlGoisSnj29g",
 "title": " Titanic "
}, {
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6wkMUYyzflIzy3CwoMhcaO-np2WOy4czIL3WZd2FWi7Tsy119tDeFq7yAeye4o2W-KmbffpGXnTDZzNv2QbCrAm7-AyEN35g3pkfAYHa3X7aQ",
 "title": " Who am I "
}, {
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6x2AXM6ulCwts2-uQSfzIU8UquTz-OAZIl-49D5-CYYl5H4mfZH8VihhCBJ60MMrZOlZla9qvb8EIP6GYRkEap4nhktbzGxW0Qu5r3edm6_Kg",
 "title": " Spider-Man "
}, {
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6zDZeLtPIVlkRLCd_V6Mr2kTzWsCkfYgmS0-cqhFAOu4dUWGtx6d402_eKnObAOFUClEDdF4mrUeDQawE71DDa805umhbAvX2712i7UgYO5MA",
 "title": " Iron Man "
}]

Then we continue to call api and query the movie information of Iron Man


/api/movies/CfDJ8D9KlbQBeipPoQwll5uLR6zDZeLtPIVlkRLCd_V6Mr2kTzWsCkfYgmS0-cqhFAOu4dUWGtx6d402_eKnObAOFUClEDdF4mrUeDQawE71DDa805umhbAvX2712i7UgYO5MA

The result is returned correctly.


[{"id":4,"title":" Iron Man "}]

Data Protector with Expiration Time (Limited Lifetime)

. NET Core also provides a data protector with expiration time by default. This data protector is used in many scenarios. The most commonly used scenario is to set the expiration time for an Token for password reset operation, so that Token cannot be decrypted successfully if one denier times out, so we can determine that the password reset operation times out.

In NET Core, we can use the ToTimeLimitedDataProtector method of the IDataProtector interface to create a data protector with an expiration time.

Here, let's use the default or continue to take the above example as an example, and the code is modified as follows


private readonly ITimeLimitedDataProtector protector;

 public MoviesController(IDataProtectionProvider provider)
 {
  this.protector = provider.CreateProtector("protect_my_query_string")
     .ToTimeLimitedDataProtector();
 }

 [HttpGet]
 public IActionResult Get()
 {
  var model = GetMovies(); // simulate call to repository
  
  var outputModel = model.Select(item => new
  {
   Id = this.protector.Protect(item.Id.ToString(), 
          TimeSpan.FromSeconds(10)),
   item.Title,
   item.ReleaseYear,
   item.Summary
  });

  return Ok(outputModel);
 }

Code interpretation

Here we define an ITimeLimitedDataProtector interface object, protector, and use ToTimeLimitedDataProtector method in the constructor to convert a common data protector into a data protector with expiration time In the api that gets the Movie list, we still use the Protect method to encrypt the Id field. Unlike before, here we add the second TimeSpan parameter, which indicates that the current encryption is only valid for 10 seconds.

Final effect

Now let's run the project again, or call the/api/movies method as before to get the Movies list, and the result is as follows


[{
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6yzbDbZ931toH32VC6Jqg8DWsrmiLrOxOFFViH4QWZne43jwSVzBjzJIfctYKZniZKNVbr50RRIZpW2fe9UtPajEzBhI-H32Effm-F0ColUaA",
 "title": " Titanic "
}, {
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6zDDVymvftZK9lKBIjEyuoNTzOEu0SC2-qfTy6quXir2S8f3A1r44f9Yz3Sd_cyLZUp-_4gfJAasMfE8_ngYLrJmdsjN9LZ0g4vox0WJLjiGA",
 "title": " Who am I "
}, {
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6zL-M2jzv2HCeTiHjevkXvI2216NERplp43TOjCXtj4S52ll68sLyQNtG2FhhWlsOmFGvYY5G4gm5SKfASMMgE1jBr20xc2b_djWdLhWLIxnA",
 "title": " Spider-Man "
}, {
 "id": "CfDJ8D9KlbQBeipPoQwll5uLR6wAoZKCHTG0lvgYS3If_0_eAD30a2YV8RjNagwLXUdCSKsO3kyS58hqDqAPHw_KHwNpd-hjDFl3hFPa8LOWHyk901oc6ZuSxwzxFlljaVreFA",
 "title": " Iron Man "
}]

After waiting for 10 seconds, we continue to call api and query the movie information of Iron Man


/api/movies/CfDJ8D9KlbQBeipPoQwll5uLR6wAoZKCHTG0lvgYS3If_0_eAD30a2YV8RjNagwLXUdCSKsO3kyS58hqDqAPHw_KHwNpd-hjDFl3hFPa8LOWHyk901oc6ZuSxwzxFlljaVreFA

Error message CryptographicException: The payload expired at 9/29/2018 11:25:05 AM +00: 00 is returned. This indicates that the current encryption has expired and cannot be decrypted correctly.

Tips: Decryption parameters using Action Filter

In the previous code, we manually called the Unprotected method to decrypt the id property in the method to get a single Movie


public void ConfigureServices(IServiceCollection services)
 {
  services.AddMvc();
  services.AddDataProtection();
 }
0

Let's use Action Filter instead to improve this part of the code.

First, we create an DecryptReferenceFilter with the following code:


public void ConfigureServices(IServiceCollection services)
 {
  services.AddMvc();
  services.AddDataProtection();
 }
1

Code interpretation

Here DecryptReferenceFilter implements IActionFilter interface, and implements OnActionExecuting and OnActionExecuted methods In the DecryptReferenceFilter class, we injected the default data protector provider and initialized a data protector in the constructor In OnActionExecuting, we get the undecrypted id field from RouteData, then decrypt it and replace the previously undecrypted id field, so that ModelBinder uses the decrypted string to bind the model.

Final modification

Finally, we modify api under 1 to get a single Movie, and the code is as follows:


public void ConfigureServices(IServiceCollection services)
 {
  services.AddMvc();
  services.AddDataProtection();
 }
2

We added the DecryptReference feature to the method of obtaining a single Movie.

After running the code, the code has the same effect as before.

Source address: http://xiazai.ofstack.com/201809/yuanma/id_protector_jb51. rar


Related articles: