top of page

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

  • Writer's pictureAhmed Tarek

Protecting Public Methods From Illogical Calls In .NET C#

Updated: Apr 17

A full guide with code samples and explanation.


Protecting Public Methods From Illogical Calls In DotNet (.NET) CSharp (C#) Code Coding Programming Software Development Engineering Architecture Best Practices
Protecting Public Methods From Illogical Calls In .NET C#. Photo by FLY:D on Unsplash, adjusted by Ahmed Tarek

On this article I am going to show you a technique to protect your modules public methods from unauthorized calls, or let’s say, illogical calls.


You may say:

If they are public, then I should expect any module to be able to call them. If I want to stop some module from calling a public method, then it should not be public in the first place.

I partially agree with you for two reasons:

  1. Sometimes the public methods are on third-party libraries which you are consuming and they are not properly abstracted.

  2. Even if you split your methods into well defined interfaces, never ever underestimate a developer’s ability to make bad decisions.


Still don’t get it, right? Let me show you a quick example.


 

Protecting Public Methods From Illogical Calls In DotNet (.NET) CSharp (C#) Code Coding Programming Software Development Engineering Architecture Best Practices
Trip Down a Developer's Sick Mind. Photo by NeONBRAND on Unsplash, adjusted by Ahmed Tarek

Trip Down a Developer’s Sick Mind


Let’s check the code sample below.



As you can see in the code sample, on line 57 and 62, the developer is casting the IRead to IWrite and the IWrite to IRead and it is going to work because the passed in object is already implementing both interfaces. Yes, this is illogical but it could happen.


I know, I know. We should not design our code to be “this level of ignorance”-proof, but, if we can do something better, then why not?


In the next sections I will show you two ways to make your code more robust against this kind of behavior.


 


 

Protecting Public Methods From Illogical Calls In DotNet (.NET) CSharp (C#) Code Coding Programming Software Development Engineering Architecture Best Practices
The Problem. Photo by Elisa Ventur on Unsplash, adjusted by Ahmed Tarek

The Problem


Now let’s come up with a more clear example of the problem we are going to fix.

Let’s say that we are working on an Online Store Website like Amazon. On this project we have more than one module to work on.


We have the Store module where the end user would be able to see all products and choose the products to add to his shopping cart.


We have the Shopping Cart module where the end user can see the products he added for buying.


We have the Check Out module where the end user can see a summary of his products and the amount to pay.


For our example, we specifically care about the Shopping Cart module and how it could be used by the other two modules; Store and Check Out modules.


Let’s now jump into the code.



As you can notice in the code sample, nothing is too complicated.

However, on line 52 and 68, nothing would stop the developer from doing these castings and using the ShoppingCart in a wrong way by calling methods which should not be called in these contexts.


The dangerous thing is that the code actually would be executed because the object passed in already implements both IStoreShoppingItems and IListShoppingItems interfaces. If this is a banking system or something that critical, this would be a total disaster.


To stop this from happening we have two approaches:

  1. Allow the developer to do the casting but make sure it throws an exception.

  2. Stop the developer from doing the casting in the first place.


These two approaches would be different in terms of design and implementation.


 

Protecting Public Methods From Illogical Calls In DotNet (.NET) CSharp (C#) Code Coding Programming Software Development Engineering Architecture Best Practices
Method 1. Image by Ahmed Tarek

Method 1


On this approach we are going to allow the developer to do the casting but make sure it throws an exception.


IStoreShoppingItems, IListShoppingItems, ShoppingCart, Store, and CheckOutManager would all have the same old implementation.


Then, we would add the following:



What we actually did here is that we created two separate wrappers for the ShoppingCart class, one to take care of the Read operations, and the other to take care of the Write operations.


Then we would start using these wrappers instead of the original ShoppingCart class as follows:



This way, if the developer try to do the same illegal/illogical castings, the runtime would throw an exception as now the passed in object is whether ReadShoppingCart or WriteShoppingCart, not ShoppingCart as before.


 


 

Protecting Public Methods From Illogical Calls In DotNet (.NET) CSharp (C#) Code Coding Programming Software Development Engineering Architecture Best Practices
Method 2 - If the Code Is Self Owned. Image by Ahmed Tarek

Method 2 - If the Code Is Self Owned


On this approach we are going to stop the developer from doing the casting in the first place.

On this case, we are assuming that the whole code is owned by us which means that we can apply the changes on the whole code.


The main idea of this approach is to protect the calls to the public methods with multiple Secret Keys; a Secret Key for Read operations and another Secret Key for Write operations. These Secret Keys are created at the moment of creating an instance of the ShoppingCart class.


The owner of these Secret Keys is the main module and then the main module is responsible for sharing the right Secret Key to the right module.


Therefore, we are going to apply these changes:



IStoreShoppingItems and IListShoppingItems



What we can notice here:

  1. We added string writingSecretKey as a parameter to the AddShoppingItem method.

  2. This means that whatever the module calling the AddShoppingItem method, it would need to provide a Writing Secret Key that should match the one initially defined and saved into the ShoppingCart object.

  3. Otherwise, an exception should be thrown.

  4. We added string readingSecretKey as a parameter to the GetShoppingItems method.

  5. This means that whatever the module calling the GetShoppingItems method, it would need to provide a Reading Secret Key that should match the one initially defined and saved into the ShoppingCart object.

  6. Otherwise, an exception should be thrown.



ShoppingCart



What we can notice here:

  1. We added private readonly string m_WritingSecretKey; and private readonly string m_ReadingSecretKey;

  2. They would be initialized by the values passed in the constructor. This is so important because we want to delegate initializing them to the owner module.

  3. Also, worth to mention that they should be immutable and hidden from the outside world.

  4. At the beginning of the AddShoppingItem method, we are checking if the passed in writingSecretKey is the same as the m_WritingSecretKey. If not, an UnauthorizedAccessException exception would be thrown.

  5. At the beginning of the GetShoppingItems method, we are checking if the passed in readingSecretKey is the same as the m_ReadingSecretKey. If not, an UnauthorizedAccessException exception would be thrown.

  6. That’s it, as simple as that.



Store



What we can notice here:

  1. We added private readonly string m_WritingSecretKey;

  2. It would be initialized by the value passed in the constructor. This is so important because we want to delegate initializing them to the owner module.

  3. Also, worth to mention that it should be immutable and hidden from the outside world.

  4. When calling the AddShoppingItem of the ShoppingCart, it would pass in the key.

  5. Worth no mention that, on line 26, if the developer tries to do the illegal/illogical casting call the GetShoppingItems method, he would find that he needs to pass in a Reading Secret Key which he doesn’t have.

  6. This would make it sound and clear that he is trying to do something wrong.

  7. Additionally, even if the developer decides to pass in any value for the Reading Secret Key, it would throw an exception.



CheckOutManager



What we can notice here:

  1. We added private readonly string m_ReadingSecretKey;

  2. It would be initialized by the value passed in the constructor. This is so important because we want to delegate initializing them to the owner module.

  3. Also, worth to mention that it should be immutable and hidden from the outside world.

  4. When calling the GetShoppingItems of the ShoppingCart, it would pass in the key.

  5. Worth no mention that, on line 15, if the developer tries to do the illegal/illogical casting call the GetShoppingItems method, he would find that he needs to pass in a Writing Secret Key which he doesn’t have.

  6. This would make it sound and clear that he is trying to do something wrong.

  7. Additionally, even if the developer decides to pass in any value for the Writing Secret Key, it would throw an exception.



Program



What we can notice here:

  1. We are initializing both m_WritingSecretKey and m_ReadingSecretKey.

  2. We are creating an instance of ShoppingCart passing in the keys.

  3. We are sharing the Writing Secret Key with the Store.

  4. We are sharing the Reading Secret Key with the CheckOutManager.

  5. That’s it.


Having these code changes in place would make the whole solution work like charm.


 


 

Protecting Public Methods From Illogical Calls In DotNet (.NET) CSharp (C#) Code Coding Programming Software Development Engineering Architecture Best Practices
Method 2 - If the Code Is Third-Party Owned. Image by Ahmed Tarek

Method 2 - If the Code Is Third-Party Owned


On this approach we are going to stop the developer from doing the casting in the first place.

On this case, we are assuming that the whole code is owned by a third party library which means that we can not apply the changes on the main code.


The main idea of this approach is to protect the calls to the public methods with multiple Secret Keys; a Secret Key for Read operations and another Secret Key for Write operations. These Secret Keys are created at the moment of creating an instance of the ShoppingCart class.


The owner of these Secret Keys is the main module and then the main module is responsible for sharing the right Secret Key to the right module.


Therefore, let’s assume that this is the third-party code:



Now what we are going to do is to wrap these into our protected layer as follows.



IProtectedStoreShoppingItems and IProtectedListShoppingItems




ProtectedShoppingCart



The only new thing here is that we are internally using the un-protected ShoppingCart class but wrapping it with our Secret Keys checks.



ProtectedStore



The same concept is applied here, we are internally using the un-protected Store class but wrapping it with our Writing Secret Key check.



ProtectedCheckOutManager



The same concept is applied here, we are internally using the un-protected CheckOutManager class but wrapping it with our Reading Secret Key check.



Program



The only new thing here is that we are creating our protected wrappers passing in the un-protected objects and finally exposing these protected wrappers for the outside world.


Protecting Public Methods From Illogical Calls In DotNet (.NET) CSharp (C#) Code Coding Programming Software Development Engineering Architecture Best Practices
Final Thoughts. Photo by Erwan Hesry on Unsplash, adjusted by Ahmed Tarek

Final Thoughts


Now we have explored more than one way to make our code more robust, however, the question is:

Would I advise you to always use these ways?

The answer is simply:

No, use it only when you need it. If your system has some critical modules that would require an extra layer of security, then may be you can use these methods. Otherwise, you would be adding more complexity to your system without any gain in return.

Therefore, having that said, hope you found reading this article as interesting as I found writing it.



Recent Posts

See All

1 bình luận

Đã xếp hạng 0/5 sao.
Chưa có xếp hạng

Thêm điểm xếp hạng
Khách
19 thg 4
Đã xếp hạng 1/5 sao.

This is over engineered 🤣. You try to fix a bad design 😜. Go to the root cause 😎

Thích

Subscribe to get best practices, tutorials, and many other cool things directly to your email inbox

bottom of page