How to click a button on a site? - javascript

To be specific, (although I am also interested in a general solution, if anyone would care to explain) I am trying to "click" Steam's 'Not Interested' button.
I have the urls of the pages I want to do this on in a Python script, alternatively I can write them in a file somewhere.
As much as I (think I) know about that button, it has no ID, and the relevant bit of code that is executed is:
$J.post('http://store.steampowered.com/recommended/ignorerecommendation/', {
sessionid: g_sessionID,
appid: store_appid
})
I am mostly unfamiliar with Javascript, but copying the headers and parameters, I managed to successfully send that post request (in Python instead), which worked. The problem with that is that it seems it requires the following cookies: steamRememberLogin, steamLogin, sessionid. I am rather sure those won't forever stay the same, and so my script would soon break.
I am unsure what to do from here on. I could imagine somehow automatically getting those cookies from somewhere, but where and how? Alternatively if something could actually send a click event from the button, I think that might be possible in Javascript?
And if this bit's important, then it doesn't need to operate in the background. As long as it can be scheduled, it's fine if it needs to actually open up that webpage in my browser.

Related

Security issue, need to change the address bar without reloading. Can't quite seem to get there

Lets say we have address bar: https://blablabla.com/lessons/lesson1/?data=2&name=blahblahblah
That's all well and fine, but if the user changes 'lesson1' to 'lesson2' they suddenly get to take a lesson they never paid for.
We are using jquery and aspx technology.
I have tried using:
window.history.pushState({}, 'lesson1', 'blahblah');
This only replaces the data that comes after the '?' and not the folder path, turning it into:
https://blablabla.com/lessons/lesson1/blahblah
But they could still replace lesson1 with lesson2 and it gives them the lesson...
Likewise if they hit the 'back' arrowkey on their browser they can still see the query string before I tried to modify it.
Any suggestions would be super helpful, but they need to be cross browser and not limited to only HTML5.
We are open to the idea of downloading plugins if necessarily and also open to the idea of somehow sending them to an obfuscated link which still knows what to do, but that would be rather difficult with what we are trying to do.
If user has a login then you should check if they have purchased lession 2, if not you should return a error, for tyhis you will need to check your database to verify if this user has actually purchased the lession 2 or lession 3 etc, if you post your code we can give better answers.

How do I verify the user with AJAX after user logined?

I'm totally new to make a website with javascript AJAX. I want to provide every experience on my website with one domain(like Facebook), thus I made every page-changing method with javascript AJAX. At first, when you visit my website, you have to log in, after that it turns to the main page and you can go to several menus with clicking button which triggers page-changing method.
The problem what I faced is.. I've recently seen someone typed javascript code into the console to delete all of his(or her) photos on Tumblr instead of clicking all of that. The idea hit my head.
Every page-changing method in my website also can be called without login. Someone can input page-changing javascript code in the console without login and see the contents of pages.
The first idea came to my head to prevent this situation was, to send id/pw every time when I make a post request to the server and everytime server gets the request, server checks the id/pw to assign the browser to change page. For instance, when a user wants to go to menu A, he(or she) has to send his(or her) id/pw to see the content of menu A.
I thought this is such a bad idea. As I guess it will result overload in server CPU when the server always has to check id and pw(sorry, I don't know well about server CPU and process, this is just my supposition). So I guess there is another way to verify the user and their requests without sending id/pw every time.
Does anyone know about that? Or should I check id/pw with every post requests?
To answer you, you are talking about Cross Site Scripting. Let me first point you to some documents in order to make you aware of what you are dealing with:-
Its called Cross Site Scripting using which a user on the client side inject script in your website and change the different stuff on it.
https://en.wikipedia.org/wiki/Cross-site_scripting
Now to deal with such things there are remedies as following:-
CSRF Token
JWT
Both of them work in somewhat identical way but there are data and payload carrying capacity and encryption involved in JWT and I recommend that.
This is a very known problem in the community and is also pretty old.
On the other hand I will also recommend you to do a data sanitization before storing it into your database. Someone can easily input some JS in your site and you can be defaced in no time.
Have a look at this Sanitizing user input before adding it to the DOM in Javascript
Last but not the least. Stop exposing the functions in the Global level while writing JavaScript. Stop creating global variables and functions and rather use closures.
(function(){
var a =10;
var b = 20;
function add(msg){
console.log(msg)
return a+b;
}
add("I am calling from Inside the self executing function");
})();
add("I am calling from outside the self executing function");
Have a look at the code above and how it protects that add() method to be called from outside.
Hope this answers your questions.
Yes, on stateless scenarios you should send some client identification like a token and verify on the server. Don't worry about the performance :)
You could take a look to JWT: https://jwt.io/

Google Apps Script - how to login and fetch data?

Intro:
I am pretty inexperienced, but recently I have been trying to access some data from a website using Google Apps Scripts. However, to access the data, I must be logged into that website. There have actually been many posts about similar issues before, but none of them were very helpful until I came to this one: how to fetch a wordpress admin page using google apps script. The accepted answer gave a method for saving the cookies and sending them out again in the second request. I basically copied and pasted the code into my own GAS file. Since the problem in that post was logging into Wordpress, I tried that first, and it worked. I had to remove the if statement checking for the response code because 200 was being returned even when I entered the correct combo. I don't know if that was just an error in the post's code or what. In any case, I verified that the second request I made returned information as if I was logged in.
Details about specific site:
The actual website that I am trying to log onto has a some kind of weird hashing method that I haven't seen on any other login pages. When you click submit, the password changes to something really long before going to another page. The opening form tag looks like this:
<form action="/guardian/home.html" method="post" name="LoginForm" target="_top" id="LoginForm" onsubmit="doPCASLogin(this);">
As you can see, it has an "onsubmit" attribute, which I believe will just run "doPCASLogin(this);" when the form is submitted. I decided to play around with the page by just entering javascript into the address bar. What I found was that doing a command like this (after entering in my username and password):
javascript: document.forms[0].submit();
didn't work. So I dug around and found the function "doPCASLogin()" in a javascript file called "md5.js". I believe md5 is some kind of hash algorithm, but that doesn't really matter. The important part of "doPCASLogin()" is this:
function doPCASLogin(form) {
var originalpw = form.pw.value;
var b64pw = b64_md5(originalpw);
var hmac_md5pw = hex_hmac_md5(pskey, b64pw)
form.pw.value = hmac_md5pw;
form.dbpw.value = hex_hmac_md5(pskey, originalpw.toLowerCase())
if (form.ldappassword!=null) {
form.ldappassword.value = originalpw;
}
}
There is some other stuff as well, but I found that it didn't matter for my login. It is pretty obvious that this just runs the password through another function a few times using "pskey" (stored in a hidden input, different on each reload) as a key, and puts these in inputs on the original form ("dbpw" and "ldappassword" are hidden inputs, while "pw" is the visible password entry input). After it does this, it submits. I located this other "hex_hmac_md5()" function, which actually connects to a whole bunch of other functions to hash the password. Anyway, that doesn't matter, because I can just call the "hex_hmac_md5()" from the javascript I type in the address bar. This is the working code that I came up with, I just broke the line up for readability:
javascript:
document.forms['LoginForm']['account'].value="username";
document.forms['LoginForm']['pw'].value="hex_hmac_md5(pskey, b64_md5('password');)";
document.forms['LoginForm']['ldappassword'].value="password";
document.forms['LoginForm']['dbpw'].value="hex_hmac_md5(pskey, 'password')";
document.forms['LoginForm'].submit();
Wherever you see "username" or "password", this just means that I entered my username and password in those spots, but obviously I have removed them. When I discovered that this worked, I wrote a small Chrome extension that will automatically log me in when I go to the website (the login process is weird so Chrome doesn't remember my username and password). That was nice, but it wasn't my end goal.
Dilemma:
After discovering all this about the hashing, I tried just putting in all these values into the HTTP payload in my GAS file, though I was skeptical that it would work. It didn't, and I suspect that is because the values are just being read as strings and the javascript is not actually being run. This would make sense, because running the actual javascript would probably be a security issue. However, why would it work in the address bar then? Just as a side note, I am getting a 200 response code back, and it also seems that a cookie is being sent back too, though it may not be valid. When I read the actual response, it is just the login page again.
I also considered trying to replicate the entire function in my own code after seeing this: How to programmatically log into a website?, but since "pskey" is different on each reload, I think the hashing would have to be done with the new key on the second UrlFetch. So even if I did copy all of the functions into my GAS file, I don't think I could successfully log on because I would need to know the "pskey" that will be generated for a particular request BEFORE actually sending the request, which would be impossible. The only way this would work is if I could somehow maintain one page somehow and read it before sending data, but I don't know how I would do this with GAS.
EDIT: I have found another input, named "contextData", which is the same as "pskey" when the page is loaded. However, if I login once and look at the POST request made using Chrome Developers tools, I can copy all the input values, including "contextData", and I can send another request a second time. Using javascript in the address bar, it looks like this:
javascript:
document.forms['LoginForm']['account'].value="username";
document.forms['LoginForm']['pw'].value="value in field that browser sent once";
document.forms['LoginForm']['ldappassword'].value="password";
document.forms['LoginForm'['dbpw'].value="value in field that browser sent once";
document.forms['LoginForm'['contextData'].value="value in field that browser sent once";
document.forms['LoginForm'].submit();
I can sign into the website as many times as I want in this manner, no matter what "pskey" is, because I am submitting everything directly and no hashing is being done. However, this still doesn't work for me, so I'm kind of stuck. I should note that I have checked the other hidden input fields and I can still log in successfully with the javascript above even after clearing every input in the form.
QUESTIONS:
-was I correct in assuming that the code I was sending was being interpreted as a string?
-why is the new code below that I just recently wrote not working?
-for future reference, how would I use GAS to sign into a site like Google where a randomly generated string is sent in the login form, and must be sent back?
function getData() {
var loginURL = 'login page';
var dataURL = 'page with data';
var loginPayload = {
'account':'same as in previous code block',
'pw':"same as in previous code block",
'ldappassword':'same as in previous code block',
'dbpw':"same as in previous code block",
"contextData":"same as in previous code block",
};
var loginOptions = {'method':'post','payload':loginPayload,'followredirects':false};
var loginResponse = UrlFetchApp.fetch(loginURL,loginOptions);
var loginHeaders = loginResponse.getAllHeaders();
var cookie = [loginResponse.getAllHeaders()["Set-Cookie"]];
cookie[0] = cookie[0].split(";")[0];
cookie = cookie.join(";");
var dataHeaders = {'Cookie':cookie};
var dataOptions = {'method':'get','headers':dataHeaders};
var dataResponse = UrlFetchApp.fetch(dataURL,dataOptions);
Logger.log(dataResponse);
}
some kind of weird hashing method that I haven't seen on any other login pages
This login uses the well-known MD5 hashing algorithm from a base-64 encoded password (of note is that it uses the same password, but lowercased, for what seems like database access dbpw and has an option of sending the plaintext (!) version of the password for LDAP login).
know the "pskey" that will be generated for a particular request BEFORE actually sending the request, which would be impossible
pskey simply stores the key used in computing HMAC signature. There is nothing stopping you from hardcoding it, reading from disk, generating it or fetching from remote whenever and wherever you want (obviously, before the computation).
running the actual javascript would probably be a security issue
Although running untrusted JavaScript code is indeed a security issue, this is not what happened at all in your case. See next point for detailed explanation why. What you should've done, is to actually run the hashing functions (in 2020, Utilities service provides everything you need in that regard) before assigning them to loginPayload properties.
was I correct in assuming that the code I was sending was being interpreted as a string?
Everything you put in quotes (single or double) is treated as a sequence of characters. That's not how Google Apps Script works, this is how ECMAScript (on which it is based) is designed to work. In order to execute the functions "inside" the string, you need to use eval, but please never do that.
Now, in 2020 it took me some time to remember what javascript: protocol meant. This is the only reason why your code executed in the first place - you explicitly told the browser that what follows is JavaScript code to be executed. If someone sees this: please, don't use that ever again.
Google Apps Script is a server-side code and is not executed in the browser environment, therefore, even if you did use the protocol, it would have no effect because no evaluation took place.
why is the new code below that I just recently wrote not working?
Because of all the reasons explained above.
for future reference, how would I use GAS to sign into a site like Google where a randomly generated string is sent in the login form, and must be sent back?
If you are talking about the OAuth / OAuth2.0 authentication protocol, here is an officially endorsed library dedicated for exactly this purpose.

"Undefined" appearing sometimes in the URL of my ASP.NET application (somehow google related?)

Some users repeatedly run into a very mysterious problem when using my web application.
In the middle of using it, they'll click a button or link that takes them to another page, but there will be a "page not found" error, because the URL is something like:
http://www.correctwebsitename.com/undefined
I thought it might be a javascript bug in my app: a redirect done by choosing a page name (maybe with some parameters) where one of the values is bad, resulting in the page name = "undefined". But there is no such code in my app anywhere, and this happens on many different pages, seemingly at random.
The one thing that seems to make it happen more often is if the user logged in originally by clicking a link in an email message in gmail. But a user who cut and pasted the link URL into a browser window said it still happened. Googling around reveals some hints that some kind of Google redirecting or caching is happening behind the scenes.
Any ideas?
Edit:
I'm not getting responses from anyone familiar with how gmail links etc work, does anyone know what SO tags google experts "hang around in"?
Edit 2:
Awarding bounty to top answer for useful info and temporary workaround idea, but still interested in real solution to the problem, so not accepting workaround as solution.
I believe you are right about gmail doing something with the links. See the gmail image below:
Non-standard header fields are conventionally marked by prefixing the field name with X-
Its probably behaving like... oh well, Google, and inspecting everything.
To stop google search from tracking my clicks i had to create a userscript to rewrite one of their functions:
rwt = function(){};
Maybe you can try something similar for gmail.
What is rwt?
rwt() is a javascript function from google search that rewrites the links to track which site you have visited.
for example, searching for "greasemonkey" showed the mozilla addons page as the first result. clicking on it opened
https://www.google.com.br/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CCUQFjAA&url=https%3A%2F%2Faddons.mozilla.org%2Fpt-BR%2Ffirefox%2Faddon%2Fgreasemonkey%2F&ei=iWNtUIXjIoyQ8wTxv4DQAQ&usg=AFQjCNEO9EJcHp9rAmKyD_XZF2Bt6hs_YQ&sig2=P19xVUsD-Q1G_9AiUBP3PQ
and then redirected to
https://addons.mozilla.org/pt-BR/firefox/addon/greasemonkey/
The image above and the rwt() case is just to show you that there is a great chance that gmail is changing the links, so this could be related to your problem.
Since there is nothing you can do at gmail's side, maybe you could fix it on your server, by redirecting http://www.correctwebsitename.com/undefined to http://www.correctwebsitename.com or any other page that you'd like your users to see.
So, be it from gmail or any other referer, every time a client lands on http://www.correctwebsitename.com/undefined redirect him to another page.
so maybe I can figure out how to just send them back to the page they
came from
ASP
if not request.UrlReferrer is nothing then
response.redirect (request.UrlReferrer.tostring)
end if
JS (for this to work, you would have to actually create a page called undefined)
if (window.location.href.toLowerCase().indexOf('undefined') > -1) {
// this works
window.location.href = document.referrer;
// this works too (uncomment to enable)
// history.back();
}
remember that if the user directly typed the url or used the link from favorites there wont be no referrer
I would suggest you to check the below things in your application.
Is there any code in your application, apart from what you own ?
there can be injected code by third party applications, like for ex "AddThis" adds an extra #parameter to your url sometimes, in your case its clear that a javascript is trying to playaround with the location.href as "undefined" is something which many js developers will come across.
by adding an # will help do cross site communication, some bug might also be causing an issue here.
Do a complete search in your code for "location.href" and see if you have used it anywhere.
Sometimes third party addons on the browser too might cause this issue
hope these would help you narrow down to your problem.
if you are not able to trace out the issue anywhere, i would suggest you to override 404 functionality on your webserver and implement the solution using Referrer.

HTML inside JS? Or in separate html file(load via $.ajax or $.load)

What would be the better/best solution? previously all my markup were all initialized in an html file,
index.php:
//login block
<div id="login">
form ...
</div>
so whenever I logged in, I have to remove/hide these login block by using $.ajax to check if there's an existing session then hide the whole login markup ( .hide() ) and show a different markup for logged in users.
The problem with this is that, it waits for the whole document to load before it executes the script, so the unintended/hidden markup will show and then vanished quickly upon page load.
I also tried putting the markup inline inside javascript, but I think it violates the "Unobtrusive" idea in js.
e.g.
var markup_arr = [
'<h4>Login</h4>',
'<form></form>'
];
var markup = markup_arr.join('');
So I end up with this
Current solution: separate html file and loading it using jQuery's $.load()
What are you using, which are the best practices and which one loads fast? Or are there any better solution out there that you can suggest? Thanks.
EDIT:
These are all javascript/ajax processes, so I'm not looking for a server side solution(include,require_once)
There's no correct answer to this. My view is you want to deliver the minimum amount of data to your users, in the minimum number of requests. It's all about finding the right balance. Depending on your users the balance will change to.
For me, I'd prefer sending two files that are 5kB each, rather than four that are 2kB. You're sending more data, but as there are less requests it should be just as quick. I'd think that delivering it as part of the Javascript might be best. Note it doesn't necessarily need to be the same file, although I'd deliver it as one - have a simple (PHP etc) script which joins the code file and the data file into one, then passes it out
The other thing I'd make sure is that you're caching everything as best you can. Having a slightly bigger file isn't generally an issue if you only have to download it once and it caches for a year. If your users are downloading a larger file every day, or worse, every page view it becomes an issue.
What I would do is check server side if the session exists, and include your separate "html" (php/rb/py/asp/whatever really) file if the session exist, and the login form if not. When the user logs, ajax would answer the same "html" file. (if I understand your problem correctly, and the login form is a single line in the page header).

Categories