for hidden field tampering protection: Id, RowVersion, I use a version of Adam Tuliper AntiModelInjection.
I'm currently investigating a way to prevent tampering of valid options found in select lists/drop downs. Consider a multitenant shared database solution where fk isn't safe enough and options are dynamic filtered in cascading dropdowns.
In the old days of ASP.NET webforms, there was viewstate that added tampering prevention for free. How is select list tampering prevention accomplished in ajax era? Is there a general solution by comparing hashes rather than re-fetching option values from database and comparing manually?
Is ViewState relevant in ASP.NET MVC?
If you can, the single solution here is to filter by the current user ids permission to that data, and then those permissions are validated once again on the save.
If this isn't possible (and there are multiple ways server side to accomplish this via things like a CustomerId fk in your records, to adding to a temporary security cache on the server side, etc) , then a client side value can provide an additional option.
If a client side option is provided like was done with Web Forms, then consider encrypting based on their
a.) User id plus another key
b.) SessionId (session must be established ahead of time though or session ids can change per request until session is established by a value stored in the session object.
c.) Some other distinct value
HTTPS is extremely important here so these values aren't sniffed. In addition ideally you want to make them unique per page. That could be the second key in A above. Why? We don't want an attacker to figure out a way to create new records elsewhere in your web app and be able to figure out what the hashes or encrypted values are for 1,2,3,4,5,6,etc and create essentially a rainbow table of values to fake.
Leblanc, in my experience, client side validation has been used mostly for user convenience. Not having to POST, to only then find out that something is wrong.
Final validation needs to occurs in the server side, away from the ability to manipulate HTML. Common users will not go on to temper with select lists and drop downs. This is done by people trying to break your page or get illegal access to data. I guess my point is final security needs to exist in the server, instead of the client side.
I think a global solution could be created given a few assumptions. Before i build anything I'll like to propose an open solution to see if anyone can find flaws or potential problems.
Given all dropdowns retrieve their data remotely. - in an ajax era and with cascading boxes this is now more common. (We are using kendo dropdowns.)
public SelectList GetLocations(int dependantarg);
The SelectList will be returned back as json - but not before having newtonsoft serialization converter automatically inject: (done at global level)
EncryptedAndSigned property to the json. This property will contain a Serialized version of the full SelectList containing all valid values that is also encrypted.
EncryptedName property to the json. This property will have the controller actionname - For this example the EncryptedName value would be "GetLocations"
When the http post is made EncryptedName : EncryptedAndSigned must be sent in the post also. For this JSON POST example it would be:
{
Location_Id: 4,
GetLocations: 'EncryptedAndSigned value'
}
On the server side:
[ValidateOptionInjection("GetLocations","Location_Id")
public ActionResult Update(Case case)
{
//access case.Location_Id safety knowing that this was a valid option available to the user.
}
Related
I'm sending a mass email though Emma (3rd party vendor) that will contain a link to a landing page. The landing page will be personalized and display some of the user's identifying info (name, title, email). Additionally, there will be a form collecting a few of the user's preferences that will be saved back to that user's record in Emma's database.
The user ID column in the 3rd party's database is incremental so I obviously can't just append that value through the query string otherwise user 522, for example, would get a link such as www.example.com?landing/?uid=522 allowing him (or anyone with the link)cto take a wild guess at other values for uid (such as 523... or 444) and change other users' preferences as well as view their personal data quite easily.
Bottom line is that I'm trying to find a secure way to pass an ID (or other unique value) that I can look up via API and use to dynamically display and then resubmit personal info/data on this landing page on a user-to-user basis.
I had an idea to add a custom column to my list in Emma for a unique identifier. I would then write a script (accessing Emma's API) to BASE64 Encode the ID (or possibly email address, as that would be unique as well) and add that to the list for each user. In my email, I could then pass that to the landing page in for the form of ?xy=ZGF2ZUBidWRvbmsuY29t, but I know this is encoding and not encrypting so not all that secure... or secure at all for that matter.
To my knowledge, there's no remote risk of anyone receiving the mailing having the ability and/or inclination to know what those extra characters in the link are, BASE64 Decode, BASE64 ENCODE another email address or integer an make a request with the newly BASE64 encoded value in order to manipulate my system in an an unintended way.
BUT for the purpose of this question, I'd like to know the "right" way to do this or what levels of security are currently being taken in similar circumstances. I've read about JWT tokens and some OOth stuff, but I'm not quite sure that's possible given that I've got the Emma API to deal with as well... and/or if that is overkill.
What is appropriate/standard for passing values to a page that are in turn used for a form to be resubmitted along with other user-supplied values when giving the user the ability to submit a "compromised" (intentionally or not) form could, at worst, could cause one of their competitors to have bad preference and opt-in saved data in our Emma mailing list?
Security on the web is all about "acceptable risk". You can reduce risk in various ways, but ultimately there's always some risk exposure you must be willing to accept.
Your very best option would be to force users to be logged-in to view the page, and to avoid using any querystring parameters. That way the backend for the page can pull the ID (or whatever it might need) out of the server's session.
Your next best option still involves forcing the user to be logged in, but leave the uid in the URL -- just be sure to validate that the user has access to the uid (i.e. don't let a user access another user's info).
If you can't do that... then you could create random keys/ids that you store in a database, and use those values (rather than uid or email or real data) in the URL. BUT let's be clear: this isn't secure, as it's technically possible to guess/deduce the scheme.
Absolutely DO NOT try passing the info in the URL as base64 encoded data, that's likely to be the first thing a hacker will figure out.
Keep in mind that any unsecured API that returns PII of any kind will be abused by automated tools... not just a user farting around with your form.
To my knowledge, there's no remote risk of anyone receiving the
mailing having the ability and/or inclination to know
^ That's always always always a bad assumption. Even if the result is at worst something you think is trivial, it opens the door for escalation attacks and literally exposes the company to risks it likely doesn't want to accept.
If you're stuck between bad options, my professional advice is to have a meeting where you record the minutes (either video, or in a document) and have someone with "authority" approve the approach you take.
In case anyone needs a working example, I found this at https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/. It uses PHP's openssl_encrypt and openssl_decrypt, and it seems to work perfectly for my purposes
<?php
$key = base64_encode(openssl_random_pseudo_bytes(32));
function my_encrypt($data, $key) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// Generate an initialization vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
// Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, 0, $iv);
// The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
return base64_encode($encrypted . '::' . $iv);
}
function my_decrypt($data, $key) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// To decrypt, split the encrypted data from our IV - our unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}
I first ran my_encrypt in a loop to encrypt the uid of each member in the list.
$members[$uid] = array('unique-identifier' => my_encrypt($uid, $key));
Next, through the API, I modified each member's record with the new value.
$ret = update_members_batch($members);
That only had to be done once.
Now in my email, I can pass the uid through the query string like this www.example.com/landing/?UID=<% unique-identifier %>, which will look something like www.example.com/landing/?UID= XXXXX2ovR2xrVmorbjlMMklYd0RNSDNPMUp0dmVLNVBaZmd3TDYyTjBFMjRkejVHRjVkSEhEQmlYaXVIcGxVczo6Dm3HmE3IxGRO1HkLijQTNg==
And in my page, I'll decrypt the query string value and use it via the API to get the email address with something like:
$member_email = get_member(my_decrypt($_GET['UID']))['email'];
and display it in the appropriate location(s) on my page.
I think this covers all my bases, but I am going to have a stakeholder meeting to get sign-off. What potential vulnerabilities does this expose that I should warn them about?
I'm reading tutorials on ASP .NET Core and Razor Pages.
One of them, when dealing with the BindProperty attribute, has remarks I find hardly understandable:
Razor Pages, by default, bind properties only with non-GET verbs.
Binding to properties can reduce the amount of code you have to write.
Binding reduces code by using the same property to render form fields
(<input asp-for="Customer.Name" />) and accept the input.
For security reasons, you must opt in to binding GET request data to
page model properties. Verify user input before mapping it to
properties. Opting in to this behavior is useful when addressing
scenarios which rely on query string or route values.
To bind a property on GET requests, set the [BindProperty] attribute's
SupportsGet property to true: [BindProperty(SupportsGet = true)]
(emphasis mine) Source: Introduction to Razor Pages in ASP.NET Core § Writing a basic form
I do not understand. Why do extra security measures need to be taken when dealing specifically with GET requests?
As far as I can tell, GET requests are supposed to be safer, not less safe than POST requests, because GET only retrieves data, while POST sends data to the server. So, more often than not, POST requests need extra security measures GET reqs don't need.
And yet now I'm reading that it's fine to do X with POST but careful, don't do this with GET recklessly, you must opt-in and be warned, take precautions!
Why are these warnings necessary? What kind of security vulnerabilities can be introduced by binding GET request data to page model properties? And why are these vulnerabilites not applicable to POST requests?
Binding is two-way model, meaning it binds a given element for both rendering ("show this value here") and submitting back ("save this value there"). For example, you'd use binding to populate an input field with a value that a user could then update and POST back. Binding handles dealing with the value automatically.
If you simply want to display a value, you don't need to use binding at all. Simply make it a public property of your model and reference it directly ({Model.PropertyName}).
There are a bunch of reasons for avoiding [BindProperty(SupportsGet = true)] but I think HTTP's RFC 7231, Section 9.4 covers it well:
URIs are intended to be shared, not secured, even when they identify secure resources. URIs are often shown on displays, added to templates when a page is printed, and stored in a variety of unprotected bookmark lists. It is therefore unwise to include information within a URI that is sensitive, personally identifiable, or a risk to disclose.
Authors of services ought to avoid GET-based forms for the submission of sensitive data because that data will be placed in the request-target. Many existing servers, proxies, and user agents log or display the request-target in places where it might be visible to third parties. Such services ought to use POST-based form submission instead.
Since the Referer header field tells a target site about the context that resulted in a request, it has the potential to reveal information about the user's immediate browsing history and any personal information that might be found in the referring resource's URI.
I am testing a web application which is an attendance submitting page. The functionality is that if two users happen to update same student attendance we shall display a warning of the change with the latest change.
For this Developers have implemented some logic like they will save the value that we enter in text box which are not yet saved in view state.They will be comparing values of view state, current data and database data and display the latest change.
As part of testing, I would like to know is there anyway so that I can modify/hack the data that developer store in view state.
You cannot do that using javascript, You can access the Hidden field "__ViewState" through JavaScript but it is all encrypted. You can only see the encrypted values.
Even if you change it through javascript, server side it will check if the data is tampered.
You can decrypt and see viewstate using below code. For this to work the ViewState must be set as not encrypted More Info
string str = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(txtViewState.Text));
But tampering viewstate is not quite possible since EnableViewStateMac property is true by default in ASP.NET
More Info
I have searched for current solutions, but can't find a set of guidelines or examples as to how to achieve the following:
The original requirements involved models with required fields, so we included annotations to those fields. As usual, there is a last-minute change and we are being asked to allow the users to save drafts. These drafts must allow the user to save the forms without any of the required fields.
I would like to know what the best practices for this problem are.
Solutions I am considering, but I accept they might be a hack (and that's why I am asking the experts)
If the user clicks "Save as Draft" I can capture the fields that have information in another ActionResult and run basic validation on those fields. Since there is a chance that required fields are missing, I am thinking in storing the captured info in a temporal model (without any required annotations). If the user decides to edit such form, I can populate fields in the view with the temp. model until the user clicks on "Submit"
Another option is to remove all required annotations and run client-side validations... but am wondering on the amount of work required to do so.
Any thoughts are very much appreciated.
Just have 2 save methods. 1 which is called from the autosave and 1 that is used to submit the process. In the autosave method do not check if(ModelState.IsValid).
Whether you choose to save the incomplete objects to the same table or a different table is your choice. In a relational world I would likely use a separate table, in a non-relational world I would use a singular object collection.
This will allow you to keep the same set of original models. There is a very high cost to duplicating your models, there are certainly times that warrants pass by value/copy but make sure the cost of mapping is there. In this situtation I do not believe there is value in mapping, except perhaps at the persistence level if you need to map to a different object because of an ORM's constraints.
There is deep value in these partial forms. Recording this on the server will allow you to apply analytics to learn why your users abandon your processes. It also gives you the ability to follow up on users who leave incomplete forms such as sending a reminder (nag) email.
You don't want to save anything to your database until it is complete. Having a duplicate table where everything is nullable is cludgy as hell. Before HTML5, the typical path was to save the information to the session, which you could then pull from to refill the fields, but that's requires having a session with a relatively high expiry to be useful.
Thankfully, HTML5 has local storage, which is really the best way to handle this now. You just watch for onchange events on your fields and then insert that value into local storage. If the user submits the form successfully, you destroy the local storage values. Otherwise, you attempt to read those values from local storage when the page loads and refill the fields.
See: http://diveintohtml5.info/storage.html
There's pretty broad support, so unless you need to worry about IE6 or IE7, you won't have any issues.
Another option (depending on your data obviously) would be to comply with the database but not the model. By this I mean ignore Model.isValid and disable Javascript validation on the front end but then satisfy the database table. In a form, you mostly have:
textboxes - default to "" or " "
checkboxes - easy true/false default
radio buttons - one is probably already selected
dates - default to DateTime.MinValue (or DateTimeUTC)
enums - default to 0 (usually for 'unspecified')
Hopefully you are also saving a flag designating that it is in Draft state so that you know you need to interpret the 'null codes' you have set when it comes to displaying the semi-populated form again.
I was wondering if anyone knows if the following scenario can happen.
Suppose I have dynamically generated a form that has check boxes for products specific to that customer. If the customer checks the boxes, the products will be deleted when form posted. The checkboxes are named after the productID.
Now my handler will check the response.form and parse the productID out, then delete the product from the database based on the productID.
Potentially could someone amend the post to allow other productIDs to be deleted, potentially everything in the product table by adding fake checkbox names to the POST?
If so, it would be easy to check prior to delete the productID is related to the authenticated user, and they have sufficient roles to delete, or to generate a nonce and label the checkboxes with that rather than their product ID, however I am not doing this at the moment. Any pointers to best practices for this would be good.
I have never considered this before, and wonder just how many people actually do this by default, or are there a million web sites out there vunerable?
Thanks
It is absolutely possible that someone could build a custom POST request with any key/value pairs (including product ID values) and submit it to your application. The fact that the checkboxes are not on the form that the POST is supposed to come from is irrelevant from a security perspective.
When thinking about web application security, the client is a completely untrusted entity. You have to assume that your JavaScript validation will be bypassed, your SELECT elements can be altered to contain any value an attacker wants, and so forth.
So yes, you should validate that the current user is authorized to delete any product ID submitted to this handler.
I'm not necessarily convinced that you need to go the nonce-obfuscation route. It is an additional layer of security, which is good, but if you are performing proper authorization I don't think it's necessary.
My $0.02
Yes this is a problem. What you are describing is an example of the "Insecure Direct Object References" risk as defined by the Open Web Application Security Project (OWASP).
As to how common it is, it currently (2011) ranks 4th in the OWASP's list of top 10 most severe web application security risks. Details of how to prevent this can be found on the OWASP page.
How Do I Prevent Insecure Direct Object References?
Preventing insecure direct object
references requires selecting an
approach for protecting each user
accessible object (e.g., object
number, filename):
Use per user or session indirect
object references. This prevents
attackers from directly targeting
unauthorized resources. For example,
instead of using the resource’s
database key, a drop down list of
six resources authorized for the
current user could use the numbers 1
to 6 to indicate which value the
user selected. The application has
to map the per-user indirect
reference back to the actual
database key on the server. OWASP’s
ESAPI includes both sequential and
random access reference maps that
developers can use to eliminate
direct object references.
Check access. Each use of a direct object reference from an
untrusted source must include an
access control check to ensure the
user is authorized for the requested
object.
Why not simply validate the values you get against the values you provided? Example: You have provided check boxes for items 1, 2 and 3, 9. The user posts 1, 2, 3, 4, 5, 6. You can find the intersection of the lists and delete that (in this case 1, 2, and 3 are in both lists).