Okay, so I've been trying to fix the xss on my website, all times failed.
There is string called 'name' in whitch people change their name to something like
<script>alert("HahS");</script> I already tried
name = name.replace(/(<|>)/g, '');
if(name.indexOf('<') !== -1){
return false;
}
All I managed to do, is fix xss on the the user who sent the xss, but for all other people logged it still pops up.
Don't write your own regex for this. Use encodeURIComponent(), which is the proper way to escape user input like this.
const sanitized = encodeURIComponent(name);
The trick is that you need to do this in the right place. If your name parameter is being broadcast to other clients, you need to decide where you want to escape it. Generally, it is best to do this right before the data is rendered or used in such a way that would be dangerous if not escaped.
This implies:
Doing it on the client, rather than on your server.
Doing it when the client initializes / renders, rather than when the potentially malicious user submits their name.
If you are having a hard time figuring out where the right place is, just add encodeURIComponent() everywhere you use name and work backwards (figure out where you can remove it). You will probably end up with name being double-escaped in the mean time. But that's okay for most innocent values of name and it is a better default than being open to XSS attacks.
More details:
http://lukeplant.me.uk/blog/posts/why-escape-on-input-is-a-bad-idea/
https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
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?
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.
}
Actually, i have this url http://mydomain.fr/user/1 in my web application. I think it is not very safe
I would hide the id which is auto_increment.
To not be able to do that:
http://mydomain.fr/user/1
http://mydomain.fr/user/2
http://mydomain.fr/user/3
http://mydomain.fr/user/4
http://mydomain.fr/user/[...]
I do not know which technique to use...
Hash MD5 stored beside primary key
UUID / GUID
I use MySQL.
You should restrict access to URLs based on authentication. Just making it 'hard to guess' an ID will not prevent someone from accessing another user's page or, e.g., deleting an unexpected user. Basically, anyone will be able to access any URL unless you provide some access control.
I think generate a random unique string for a user is the best way.
simply use sha1 hash should be ok.
There is no way properly to hide it, you can generate unique ID with a long random hashed string, it's harder to guest. Basically that won't prevent someone to access other's ID.
OP may be concerned with divulging the primary keys because it could leak information into how many of a certain resource exists.
For example, if he is building a web app and someone creates an account and sees a url of domain.fr/user/23 they will know they have created an account on an application with low adoption.
My suggestion would be to either use a GUID value as suggested above or a username that is constrained to be unique.
If you use a GUID, it will look ugly, but make sure to not just use the beginning part as you could greatly increase the chance of collision since the first 60 bits are based on the timestamp.
If you use a unique username, your url would instead look like domain.fr/user/username
I know this is easily done on RoR.
Basically, I am writing e-mails that are sent when a person registers on my website, and it will have an activation link that needs to have a string assigned to a GET variable.
When the person clicks the link, they will be taken to a page on the site where the string will then be decrypted and matched to something in the database that is unique to them. It will then activate their account.
I'm doing this in CakePHP, so if there's any function built in, that would be preferable.
I've tried lots of options, and most of them either are really short, really strange, or have characters in them that would mess up the GET variable.
I need the output to be preferably around 20 characters, with only letters and numbers.
Simply use a random, unique string. No need for encrypting or decrypting it, it just needs to be unique, long and random. That's simply known as an opaque token. It does not have any meaning, it's just something unique that only one user is supposed to have. UUIDs are a great fit.
do {
$token = str_replace('-', '', String::uuid());
} while (!$this->User->isUnique(array('token' => $token)));
If I receive user input, does it make any difference if I validate first and then sanitize before saving it to the database?
Or is there any risk in validating unsanitized input?
(when I say 'sanitize' I mainly mean stripping out any HTML tags)
UPDATE & CLARIFICATION:
I wouldn't put HTML tags into the database. I would sanitize the input before saving it - but after validating against my model. The reason the validation and sanitization are separate is because they are separate libraries - the only question is whether I should call 'sanitize' upon 'before_validate' or upon 'before_save'.
The only risk I can think of is if you are performing extensive client-side validation. If you were validating and then spitting the values back onto the page in your error messages then you could have an issue.
For example, if you have a textbox that has to only contain numbers and a user enters a letter in that textbox. If your client side script generates an error message like " 'somevalue' is an invalid value for this textbox ", then you are injecting whatever the user has entered into the box back onto the page. Either way, it will only affect that user's session.
Why are you separating HTML stripping (or escaping) from other validation? Isn't it all the same thing?
And why would you put HTML tags into the database only to strip them later? Doesn't that mean that your database is temporarily incorrect?
I don't see why you're separating "validation" from "sanitize". They're two sides of the same coin. Do everything you can to make sure the data is perfect before committing it to the database.
"the only question is whether I should call 'sanitize' upon 'before_validate' or upon 'before_save'."
The distinction is too subtle for words. You must do both. Generally, you do not want to try and validate HTML.
Therefore, it's only sensible if you (1) "sanitize" to strip HTML tags and then (2) validate what's left.
I'm not sure how else you could do it.
ALWAYS validate and sanitize before submitting to the database. Without doing so, you could expose your database to SQL injection attacks.
What order you do so depends on what you want your user experience to be. If you validate first, then you could provide validation back to the user easier (You could use javascript and regular expressions to do this)
Required xkcd cartoon: